Skip to content

Commit 7fc8aab

Browse files
mandel-macaqueGitHub Actions Autoformatter
andauthored
[RGen] Add support for nested classes to all emitter. (#23401)
While we added the support of nested class for strong dicts there is no reason not to support it on other binding types. --------- Co-authored-by: GitHub Actions Autoformatter <[email protected]>
1 parent f31acf6 commit 7fc8aab

File tree

16 files changed

+1398
-136
lines changed

16 files changed

+1398
-136
lines changed

src/rgen/Microsoft.Macios.Generator/Emitters/CategoryEmitter.cs

Lines changed: 18 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -56,25 +56,28 @@ public bool TryEmit (in BindingContext bindingContext, [NotNullWhen (false)] out
5656
var registrationName = bindingData.CategoryType.Value.Name;
5757

5858
// namespace declaration
59-
bindingContext.Builder.WriteLine ();
60-
bindingContext.Builder.WriteLine ($"namespace {string.Join (".", bindingContext.Changes.Namespace)};");
61-
bindingContext.Builder.WriteLine ();
59+
this.EmitNamespace (bindingContext);
6260

63-
// append the class availability, this will add the necessary attributes to the class
64-
bindingContext.Builder.AppendMemberAvailability (bindingContext.Changes.SymbolAvailability);
61+
using (var _ = this.EmitOuterClasses (bindingContext, out var builder)) {
62+
// append the class availability, this will add the necessary attributes to the class
63+
builder.AppendMemberAvailability (bindingContext.Changes.SymbolAvailability);
6564

66-
var modifiers = $"{string.Join (' ', bindingContext.Changes.Modifiers)} ";
67-
// class declaration, the analyzer should ensure that the class is static, otherwise it will fail to compile with an error.
68-
using (var classBlock = bindingContext.Builder.CreateBlock ($"{(string.IsNullOrWhiteSpace (modifiers) ? string.Empty : modifiers)}class {bindingContext.Changes.Name}", true)) {
69-
// emit the fields for the selectors before we register the class or anything
70-
this.EmitSelectorFields (bindingContext, classBlock);
65+
var modifiers = $"{string.Join (' ', bindingContext.Changes.Modifiers)} ";
66+
// class declaration, the analyzer should ensure that the class is static, otherwise it will fail to compile with an error.
67+
using (var classBlock = builder.CreateBlock (
68+
$"{(string.IsNullOrWhiteSpace (modifiers) ? string.Empty : modifiers)}class {bindingContext.Changes.Name}",
69+
true)) {
70+
// emit the fields for the selectors before we register the class or anything
71+
this.EmitSelectorFields (bindingContext, classBlock);
7172

72-
classBlock.WriteLine ($"static readonly {NativeHandle} {ClassPtr} = {BindingSyntaxFactory.Class}.GetHandle (\"{registrationName}\");");
73+
classBlock.WriteLine (
74+
$"static readonly {NativeHandle} {ClassPtr} = {BindingSyntaxFactory.Class}.GetHandle (\"{registrationName}\");");
7375

74-
// categories only have methods since we cannot have extensions properties in C#.
75-
this.EmitMethods (bindingContext, classBlock);
76-
}
76+
// categories only have methods since we cannot have extensions properties in C#.
77+
this.EmitMethods (bindingContext, classBlock);
78+
}
7779

78-
return true;
80+
return true;
81+
}
7982
}
8083
}

src/rgen/Microsoft.Macios.Generator/Emitters/ClassEmitter.cs

Lines changed: 43 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -400,72 +400,57 @@ public bool TryEmit (in BindingContext bindingContext, [NotNullWhen (false)] out
400400
}
401401

402402
// namespace declaration
403-
bindingContext.Builder.WriteLine ();
404-
bindingContext.Builder.WriteLine ($"namespace {string.Join (".", bindingContext.Changes.Namespace)};");
405-
bindingContext.Builder.WriteLine ();
406-
407-
// if the type of the change contains outer classes, we need to emit the outer classes first
408-
TabbedWriter<StringWriter> builder = bindingContext.Builder;
409-
// create a list for the outer classes builders so that we can dispose them later, we are using a linked list
410-
// because AddFirst is O(1) and we need to dispose them in the reverse order of creation
411-
var outerClassesBuilders = new LinkedList<TabbedWriter<StringWriter>> ();
412-
if (bindingContext.Changes.OuterClasses.Length > 0) {
413-
// emit the outer classes and set the builder to the innermost class
414-
foreach (var outerClass in bindingContext.Changes.OuterClasses) {
415-
var outerModifiers = $"{string.Join (' ', outerClass.Modifiers)} ";
416-
builder = builder.CreateBlock ($"{(string.IsNullOrWhiteSpace (outerModifiers) ? string.Empty : outerModifiers)}class {outerClass.Name}", true);
417-
// add the current builder to the list so that we can dispose it later, we add it to the head of the
418-
// list so that we can dispose it in the reverse order of creation
419-
outerClassesBuilders.AddFirst (builder);
420-
}
421-
}
403+
this.EmitNamespace (bindingContext);
422404

423-
// register the class only if we are not dealing with a static class
424-
var bindingData = (BindingTypeData<Class>) bindingContext.Changes.BindingInfo;
425-
// Registration depends on the class name. If the binding data contains a name, we use that one, else
426-
// we use the name of the class
427-
var registrationName = bindingData.Name ?? bindingContext.Changes.Name;
405+
using (var _ = this.EmitOuterClasses (bindingContext, out var builder)) {
428406

429-
// append the class availability, this will add the necessary attributes to the class
430-
bindingContext.Builder.AppendMemberAvailability (bindingContext.Changes.SymbolAvailability);
431-
if (!bindingContext.Changes.IsStatic) {
432-
builder.WriteLine ($"[Register (\"{registrationName}\", true)]");
433-
}
434-
var modifiers = $"{string.Join (' ', bindingContext.Changes.Modifiers)} ";
435-
using (var classBlock = builder.CreateBlock ($"{(string.IsNullOrWhiteSpace (modifiers) ? string.Empty : modifiers)}class {bindingContext.Changes.Name}", true)) {
436-
// emit the fields for the selectors before we register the class or anything
437-
this.EmitSelectorFields (bindingContext, classBlock);
407+
// register the class only if we are not dealing with a static class
408+
var bindingData = (BindingTypeData<Class>) bindingContext.Changes.BindingInfo;
409+
// Registration depends on the class name. If the binding data contains a name, we use that one, else
410+
// we use the name of the class
411+
var registrationName = bindingData.Name ?? bindingContext.Changes.Name;
438412

413+
// append the class availability, this will add the necessary attributes to the class
414+
bindingContext.Builder.AppendMemberAvailability (bindingContext.Changes.SymbolAvailability);
439415
if (!bindingContext.Changes.IsStatic) {
440-
classBlock.AppendGeneratedCodeAttribute (optimizable: true);
441-
classBlock.WriteLine ($"static readonly {NativeHandle} {ClassPtr} = {BindingSyntaxFactory.Class}.GetHandle (\"{registrationName}\");");
442-
classBlock.WriteLine ();
443-
classBlock.WriteDocumentation (Documentation.Class.ClassHandle (bindingContext.Changes.Name));
444-
classBlock.WriteLine ($"public override {NativeHandle} ClassHandle => {ClassPtr};");
445-
classBlock.WriteLine ();
446-
447-
EmitDefaultConstructors (bindingContext: bindingContext,
448-
classBlock: classBlock,
449-
disableDefaultCtor: bindingData.Flags.HasFlag (ObjCBindings.Class.DisableDefaultCtor));
450-
451-
// emit any other constructor that is not the default one
452-
EmitConstructors (bindingContext, classBlock);
416+
builder.WriteLine ($"[Register (\"{registrationName}\", true)]");
453417
}
454418

455-
EmitFields (bindingContext.Changes.Name, bindingContext.Changes.Properties, classBlock,
456-
out var notificationProperties);
457-
EmitProperties (bindingContext, classBlock);
458-
this.EmitMethods (bindingContext, classBlock);
419+
var modifiers = $"{string.Join (' ', bindingContext.Changes.Modifiers)} ";
420+
using (var classBlock =
421+
builder.CreateBlock (
422+
$"{(string.IsNullOrWhiteSpace (modifiers) ? string.Empty : modifiers)}class {bindingContext.Changes.Name}",
423+
true)) {
424+
// emit the fields for the selectors before we register the class or anything
425+
this.EmitSelectorFields (bindingContext, classBlock);
426+
427+
if (!bindingContext.Changes.IsStatic) {
428+
classBlock.AppendGeneratedCodeAttribute (optimizable: true);
429+
classBlock.WriteLine (
430+
$"static readonly {NativeHandle} {ClassPtr} = {BindingSyntaxFactory.Class}.GetHandle (\"{registrationName}\");");
431+
classBlock.WriteLine ();
432+
classBlock.WriteDocumentation (Documentation.Class.ClassHandle (bindingContext.Changes.Name));
433+
classBlock.WriteLine ($"public override {NativeHandle} ClassHandle => {ClassPtr};");
434+
classBlock.WriteLine ();
435+
436+
EmitDefaultConstructors (bindingContext: bindingContext,
437+
classBlock: classBlock,
438+
disableDefaultCtor: bindingData.Flags.HasFlag (ObjCBindings.Class.DisableDefaultCtor));
439+
440+
// emit any other constructor that is not the default one
441+
EmitConstructors (bindingContext, classBlock);
442+
}
443+
444+
EmitFields (bindingContext.Changes.Name, bindingContext.Changes.Properties, classBlock,
445+
out var notificationProperties);
446+
EmitProperties (bindingContext, classBlock);
447+
this.EmitMethods (bindingContext, classBlock);
459448

460-
// emit the notification helper classes, leave this for the very bottom of the class
461-
EmitNotifications (notificationProperties, classBlock);
462-
}
449+
// emit the notification helper classes, leave this for the very bottom of the class
450+
EmitNotifications (notificationProperties, classBlock);
451+
}
463452

464-
// close the outer classes since we used a LinkedList to store them and insert them at the head, we can
465-
// simply dispose them in current order, which is the reverse of the creation order
466-
foreach (var outerClassesBuilder in outerClassesBuilders) {
467-
outerClassesBuilder.Dispose ();
453+
return true;
468454
}
469-
return true;
470455
}
471456
}

src/rgen/Microsoft.Macios.Generator/Emitters/EnumEmitter.cs

Lines changed: 52 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ void EmitExtensionMethods (TabbedWriter<StringWriter> classBlock, string symbolN
171171
/// <param name="bindingContext">The current binding context.</param>
172172
/// <param name="diagnostics">Out parameter with the error diagnostics.</param>
173173
/// <returns>True if the binding was generated, false otherwise.</returns>
174-
bool TryEmitSmartEnum (in BindingTypeData<SmartEnum> _, in BindingContext bindingContext, [NotNullWhen (false)] out ImmutableArray<Diagnostic>? diagnostics)
174+
bool TryEmitSmartEnum (in BindingContext bindingContext, [NotNullWhen (false)] out ImmutableArray<Diagnostic>? diagnostics)
175175
{
176176
diagnostics = null;
177177
if (bindingContext.Changes.BindingType != BindingType.SmartEnum) {
@@ -184,34 +184,36 @@ bool TryEmitSmartEnum (in BindingTypeData<SmartEnum> _, in BindingContext bindin
184184
}
185185
// in the old generator we had to copy over the enum, in this new approach, the only code
186186
// we need to create is the extension class for the enum that is backed by fields
187-
bindingContext.Builder.WriteLine ();
188-
bindingContext.Builder.WriteLine ($"namespace {string.Join (".", bindingContext.Changes.Namespace)};");
189-
bindingContext.Builder.WriteLine ();
190-
191-
var symbolName = GetSymbolName (bindingContext.Changes);
192-
var extensionClassDeclaration =
193-
bindingContext.Changes.ToSmartEnumExtensionDeclaration (symbolName);
194-
195-
bindingContext.Builder.WriteDocumentation (Documentation.SmartEnum.ClassDocumentation (symbolName));
196-
bindingContext.Builder.AppendMemberAvailability (bindingContext.Changes.SymbolAvailability);
197-
bindingContext.Builder.AppendGeneratedCodeAttribute ();
198-
using (var classBlock = bindingContext.Builder.CreateBlock (extensionClassDeclaration.ToString (), true)) {
199-
classBlock.WriteLine ();
200-
classBlock.WriteLine ($"static IntPtr[] values = new IntPtr [{bindingContext.Changes.EnumMembers.Length}];");
201-
// foreach member in the enum we need to create a field that holds the value, the property emitter
202-
// will take care of generating the property. Do not order it by name to keep the order of the enum
203-
if (!TryEmit (classBlock, bindingContext.Changes)) {
204-
diagnostics = []; // empty diagnostics since it was a user error
205-
return false;
187+
this.EmitNamespace (bindingContext);
188+
189+
using (var _ = this.EmitOuterClasses (bindingContext, out var builder)) {
190+
var symbolName = GetSymbolName (bindingContext.Changes);
191+
var extensionClassDeclaration =
192+
bindingContext.Changes.ToSmartEnumExtensionDeclaration (symbolName);
193+
194+
builder.WriteDocumentation (Documentation.SmartEnum.ClassDocumentation (symbolName));
195+
builder.AppendMemberAvailability (bindingContext.Changes.SymbolAvailability);
196+
builder.AppendGeneratedCodeAttribute ();
197+
using (var classBlock = builder.CreateBlock (extensionClassDeclaration.ToString (), true)) {
198+
classBlock.WriteLine ();
199+
classBlock.WriteLine (
200+
$"static IntPtr[] values = new IntPtr [{bindingContext.Changes.EnumMembers.Length}];");
201+
// foreach member in the enum we need to create a field that holds the value, the property emitter
202+
// will take care of generating the property. Do not order it by name to keep the order of the enum
203+
if (!TryEmit (classBlock, bindingContext.Changes)) {
204+
diagnostics = []; // empty diagnostics since it was a user error
205+
return false;
206+
}
207+
208+
classBlock.WriteLine ();
209+
210+
// emit the extension methods that will be used to get the values from the enum
211+
EmitExtensionMethods (classBlock, symbolName, bindingContext.Changes);
212+
classBlock.WriteLine ();
206213
}
207-
classBlock.WriteLine ();
208214

209-
// emit the extension methods that will be used to get the values from the enum
210-
EmitExtensionMethods (classBlock, symbolName, bindingContext.Changes);
211-
classBlock.WriteLine ();
215+
return true;
212216
}
213-
214-
return true;
215217
}
216218

217219
bool TryEmitErrorCode (in BindingTypeData<SmartEnum> bindingTypeData, in BindingContext bindingContext, [NotNullWhen (false)] out ImmutableArray<Diagnostic>? diagnostics)
@@ -252,35 +254,36 @@ bool TryEmitErrorCode (in BindingTypeData<SmartEnum> bindingTypeData, in Binding
252254

253255
var library = libraryPath ?? libraryName;
254256

255-
bindingContext.Builder.WriteLine ();
256-
bindingContext.Builder.WriteLine ($"namespace {string.Join (".", bindingContext.Changes.Namespace)};");
257-
bindingContext.Builder.WriteLine ();
258-
259-
bindingContext.Builder.AppendMemberAvailability (bindingContext.Changes.SymbolAvailability);
260-
bindingContext.Builder.AppendGeneratedCodeAttribute ();
261-
var extensionClassDeclaration =
262-
bindingContext.Changes.ToSmartEnumExtensionDeclaration (GetSymbolName (bindingContext.Changes));
263-
264-
using (var classBlock = bindingContext.Builder.CreateBlock (extensionClassDeclaration.ToString (), true)) {
265-
classBlock.WriteLine ();
266-
// emit the field that holds the error domain
267-
classBlock.WriteLine ($"[Field (\"{bindingTypeData.ErrorDomain}\", \"{library}\")]");
268-
classBlock.WriteLine (StaticVariable (backingFieldName, NSString, true).ToString ());
269-
classBlock.WriteLine ();
270-
271-
// emit the extension method to return the error domain
272-
classBlock.WriteDocumentation (Documentation.SmartEnum.GetDomain (bindingContext.Changes.FullyQualifiedSymbol));
273-
classBlock.WriteRaw (
274-
$@"public static {NSString}? GetDomain (this {bindingContext.Changes.Name.GetIdentifierName (bindingContext.Changes.Namespace)} self)
257+
this.EmitNamespace (bindingContext);
258+
259+
using (var _ = this.EmitOuterClasses (bindingContext, out var builder)) {
260+
builder.AppendMemberAvailability (bindingContext.Changes.SymbolAvailability);
261+
builder.AppendGeneratedCodeAttribute ();
262+
var extensionClassDeclaration =
263+
bindingContext.Changes.ToSmartEnumExtensionDeclaration (GetSymbolName (bindingContext.Changes));
264+
265+
using (var classBlock = builder.CreateBlock (extensionClassDeclaration.ToString (), true)) {
266+
classBlock.WriteLine ();
267+
// emit the field that holds the error domain
268+
classBlock.WriteLine ($"[Field (\"{bindingTypeData.ErrorDomain}\", \"{library}\")]");
269+
classBlock.WriteLine (StaticVariable (backingFieldName, NSString, true).ToString ());
270+
classBlock.WriteLine ();
271+
272+
// emit the extension method to return the error domain
273+
classBlock.WriteDocumentation (
274+
Documentation.SmartEnum.GetDomain (bindingContext.Changes.FullyQualifiedSymbol));
275+
classBlock.WriteRaw (
276+
$@"public static {NSString}? GetDomain (this {bindingContext.Changes.Name.GetIdentifierName (bindingContext.Changes.Namespace)} self)
275277
{{
276278
if ({backingFieldName} is null)
277279
{backingFieldName} = {Dlfcn}.GetStringConstant ({Libraries}.{libraryName}.Handle, ""{bindingTypeData.ErrorDomain}"");
278280
return {backingFieldName};
279281
}}
280282
");
281-
}
283+
}
282284

283-
return true;
285+
return true;
286+
}
284287
}
285288

286289
public bool TryEmit (in BindingContext bindingContext, [NotNullWhen (false)] out ImmutableArray<Diagnostic>? diagnostics)
@@ -292,6 +295,6 @@ public bool TryEmit (in BindingContext bindingContext, [NotNullWhen (false)] out
292295

293296
return bindingData.Flags.HasFlag (SmartEnum.ErrorCode)
294297
? TryEmitErrorCode (bindingData, bindingContext, out diagnostics)
295-
: TryEmitSmartEnum (bindingData, bindingContext, out diagnostics);
298+
: TryEmitSmartEnum (bindingContext, out diagnostics);
296299
}
297300
}

src/rgen/Microsoft.Macios.Generator/Emitters/ICodeEmitter.cs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,16 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
3+
4+
using System;
5+
using System.Collections;
36
using System.Collections.Generic;
47
using System.Collections.Immutable;
58
using System.Diagnostics.CodeAnalysis;
9+
using System.IO;
610
using Microsoft.CodeAnalysis;
711
using Microsoft.Macios.Generator.Context;
812
using Microsoft.Macios.Generator.DataModel;
13+
using Microsoft.Macios.Generator.IO;
914

1015
namespace Microsoft.Macios.Generator.Emitters;
1116

@@ -33,3 +38,60 @@ interface ICodeEmitter {
3338
/// </summary>
3439
IEnumerable<string> UsingStatements { get; }
3540
}
41+
42+
static class ICodeEmitterExtensions {
43+
44+
/// <summary>
45+
/// Helper class to manage the disposal of the writers for the outer classes.
46+
/// </summary>
47+
class Blocks (LinkedList<TabbedWriter<StringWriter>> blocks) : IDisposable {
48+
public void Dispose ()
49+
{
50+
// close the outer classes since we used a LinkedList to store them and insert them at the head, we can
51+
// simply dispose them in current order, which is the reverse of the creation order
52+
foreach (var outerClassesBuilder in blocks) {
53+
outerClassesBuilder.Dispose ();
54+
}
55+
}
56+
}
57+
58+
/// <summary>
59+
/// Emits the namespace declaration for the binding.
60+
/// </summary>
61+
/// <param name="self">The code emitter.</param>
62+
/// <param name="bindingContext">The context for the binding.</param>
63+
public static void EmitNamespace (this ICodeEmitter self, in BindingContext bindingContext)
64+
{
65+
bindingContext.Builder.WriteLine ();
66+
bindingContext.Builder.WriteLine ($"namespace {string.Join (".", bindingContext.Changes.Namespace)};");
67+
bindingContext.Builder.WriteLine ();
68+
}
69+
70+
/// <summary>
71+
/// Emits the outer classes for the binding.
72+
/// </summary>
73+
/// <param name="self">The code emitter.</param>
74+
/// <param name="bindingContext">The context for the binding.</param>
75+
/// <param name="builder">The tabbed writer for the innermost class.</param>
76+
/// <returns>An <see cref="IDisposable"/> that will close the outer classes blocks when disposed.</returns>
77+
public static IDisposable EmitOuterClasses (this ICodeEmitter self, in BindingContext bindingContext,
78+
out TabbedWriter<StringWriter> builder)
79+
{
80+
// if the type of the change contains outer classes, we need to emit the outer classes first
81+
builder = bindingContext.Builder;
82+
// create a list for the outer classes builders so that we can dispose them later, we are using a linked list
83+
// because AddFirst is O(1) and we need to dispose them in the reverse order of creation
84+
var outerClassesBuilders = new LinkedList<TabbedWriter<StringWriter>> ();
85+
if (bindingContext.Changes.OuterClasses.Length > 0) {
86+
// emit the outer classes and set the builder to the innermost class
87+
foreach (var outerClass in bindingContext.Changes.OuterClasses) {
88+
var outerModifiers = $"{string.Join (' ', outerClass.Modifiers)} ";
89+
builder = builder.CreateBlock ($"{(string.IsNullOrWhiteSpace (outerModifiers) ? string.Empty : outerModifiers)}class {outerClass.Name}", true);
90+
// add the current builder to the list so that we can dispose it later, we add it to the head of the
91+
// list so that we can dispose it in the reverse order of creation
92+
outerClassesBuilders.AddFirst (builder);
93+
}
94+
}
95+
return new Blocks (outerClassesBuilders);
96+
}
97+
}

0 commit comments

Comments
 (0)