Skip to content

Commit 99eaf5f

Browse files
[RGen] Add support to ignore the registration of certain selector (#23663)
Add the ability to ignore the registration of the selector in some methods. This allows to use the same selector in varios methods and just register a single one. The PR also updates the emitter to use the old bgen attributes to allow the registrar to work with the new binding API.
2 parents 7435114 + cb0fbb4 commit 99eaf5f

34 files changed

+789
-5
lines changed

src/ObjCBindings/ExportTag.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,11 @@ public enum Constructor : Int64 {
3131
/// marshalling the property.
3232
/// </summary>
3333
PlainString = 1 << 4,
34+
35+
/// <summary>
36+
/// This value is used to skip the constructor registration in the ObjC runtime.
37+
/// </summary>
38+
SkipRegistration = 1 << 5,
3439
}
3540

3641
/// <summary>
@@ -121,6 +126,11 @@ public enum Method : Int64 {
121126
/// </summary>
122127
Event = 1 << 15,
123128

129+
/// <summary>
130+
/// This value is used to skip the method registration in the ObjC runtime.
131+
/// </summary>
132+
SkipRegistration = 1 << 15,
133+
124134
}
125135

126136
/// <summary>
@@ -234,6 +244,11 @@ public enum Property : Int64 {
234244
/// to the delegate.
235245
/// </summary>
236246
CreateEvents = 1 << 17,
247+
248+
/// <summary>
249+
/// This value is used to skip the property registration in the ObjC runtime.
250+
/// </summary>
251+
SkipRegistration = 1 << 18,
237252
}
238253

239254
/// <summary>

src/rgen/Microsoft.Macios.Generator/DataModel/Constructor.Generator.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,11 @@ readonly partial struct Constructor {
3030
/// </summary>
3131
public Location? Location { get; init; }
3232

33+
/// <summary>
34+
/// True if the cosntructor was marked to skip its registration.
35+
/// </summary>
36+
public bool SkipRegistration => ExportMethodData.Flags.HasFlag (ObjCBindings.Constructor.SkipRegistration);
37+
3338
public Constructor (string type,
3439
SymbolAvailability symbolAvailability,
3540
ExportData<ObjCBindings.Constructor> exportData,

src/rgen/Microsoft.Macios.Generator/DataModel/Method.Generator.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@ public bool UsePlainString
101101
/// </summary>
102102
public bool IsEvent => ExportMethodData.Flags.HasFlag (ObjCBindings.Method.Event);
103103

104+
/// <summary>
105+
/// True if the method was marked to skip its registration.
106+
/// </summary>
107+
public bool SkipRegistration => ExportMethodData.Flags.HasFlag (ObjCBindings.Method.SkipRegistration);
108+
104109
/// <summary>
105110
/// The location of the attribute in source code.
106111
/// </summary>

src/rgen/Microsoft.Macios.Generator/DataModel/Property.Generator.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,11 @@ public bool ReleaseReturnValue
147147
/// </summary>
148148
public bool IsOptional => IsProperty && ExportPropertyData.Flags.HasFlag (ObjCBindings.Property.Optional);
149149

150+
/// <summary>
151+
/// True if the property was marked to skip its registration.
152+
/// </summary>
153+
public bool SkipRegistration => IsProperty && ExportPropertyData.Flags.HasFlag (ObjCBindings.Property.SkipRegistration);
154+
150155
readonly bool? needsBackingField = null;
151156
/// <summary>
152157
/// States if the property, when generated, needs a backing field.

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ void EmitConstructors (in BindingContext context, TabbedWriter<StringWriter> cla
6363
classBlock.AppendDesignatedInitializer ();
6464
}
6565

66+
if (GeneratorConfiguration.BGenCompatible && !constructor.SkipRegistration) {
67+
classBlock.AppendBgenExportAttribute (constructor.Selector);
68+
}
69+
6670
using (var constructorBlock = classBlock.CreateBlock (constructor.ToDeclaration (withBaseNSFlag: true).ToString (), block: true)) {
6771
// retrieve the method invocation via the factory, this will generate the necessary arguments
6872
// transformations and the invocation

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

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ static void EmitVoidMethodBody (in BindingContext context, in Method method, in
101101
}
102102

103103
// if we are dealing with a protocol or an extension method, we need to call send directly
104-
if (context.Changes.BindingType == BindingType.Protocol || method.IsExtension) {
104+
if (context.Changes.BindingType == BindingType.Protocol || method.IsExtension || method.SkipRegistration) {
105105
methodBlock.WriteRaw (
106106
$@"{ExpressionStatement (invocations.Send)}
107107
{ExpressionStatement (KeepAlive (method.This))}
@@ -148,7 +148,7 @@ static void EmitReturnMethodBody (in BindingContext context, in Method method, i
148148
}
149149

150150
// if we are dealing with a protocol or an extension method, we need to call send directly
151-
if (context.Changes.BindingType == BindingType.Protocol || method.IsExtension) {
151+
if (context.Changes.BindingType == BindingType.Protocol || method.IsExtension || method.SkipRegistration) {
152152
methodBlock.WriteRaw (
153153
$@"{tempDeclaration}
154154
{ExpressionStatement (invocations.Send)}
@@ -200,6 +200,11 @@ public static void EmitMethod (this IClassEmitter self, in BindingContext contex
200200
classBlock.AppendExportAttribute (method.ExportMethodData);
201201
}
202202

203+
// this is needed to support the registrar until it understands the rgen attributes.
204+
if (GeneratorConfiguration.BGenCompatible && !method.SkipRegistration) {
205+
classBlock.AppendBgenExportAttribute (method.ExportMethodData);
206+
}
207+
203208
using (var methodBlock = classBlock.CreateBlock (method.ToDeclaration ().ToString (), block: true)) {
204209
// write any possible thread check at the beginning of the method
205210
if (uiThreadCheck is not null) {
@@ -377,6 +382,7 @@ public static void EmitProperty (this IClassEmitter self, in BindingContext cont
377382
// add the export method for the property, this is needed for the protocol wrapper
378383
classBlock.AppendExportAttribute (property.ExportPropertyData);
379384
}
385+
380386
using (var propertyBlock = classBlock.CreateBlock (property.ToDeclaration ().ToString (), block: true)) {
381387
// be very verbose with the availability, makes the life easier to the dotnet analyzer
382388
propertyBlock.AppendMemberAvailability (getter.SymbolAvailability);
@@ -386,6 +392,13 @@ public static void EmitProperty (this IClassEmitter self, in BindingContext cont
386392
propertyBlock.AppendDelegateProxyReturn (property.ReturnType);
387393
if (context.Changes.BindingType == BindingType.Protocol && !getter.ExportPropertyData.IsNullOrDefault)
388394
propertyBlock.AppendExportAttribute (getter.ExportPropertyData);
395+
396+
if (GeneratorConfiguration.BGenCompatible && !property.SkipRegistration) {
397+
var selector = getter.GetSelector (property);
398+
// same, needed to support the registrar until it understands the rgen attributes.
399+
propertyBlock.AppendBgenExportAttribute (selector);
400+
}
401+
389402
using (var getterBlock = propertyBlock.CreateBlock ("get", block: true)) {
390403
if (uiThreadCheck is not null) {
391404
getterBlock.WriteLine (uiThreadCheck.ToString ());
@@ -395,7 +408,7 @@ public static void EmitProperty (this IClassEmitter self, in BindingContext cont
395408
// the return value
396409
var (tempVar, tempDeclaration) = GetReturnValueAuxVariable (property.ReturnType);
397410
// if the binding is a protocol, we need to call send directly
398-
if (context.Changes.BindingType == BindingType.Protocol) {
411+
if (context.Changes.BindingType == BindingType.Protocol || property.SkipRegistration) {
399412
getterBlock.WriteLine ($"{tempDeclaration}");
400413
getterBlock.WriteLine ($"{ExpressionStatement (invocations.Getter.Send)}");
401414
getterBlock.WriteLine ($"{ExpressionStatement (KeepAlive ("this"))}");
@@ -435,6 +448,12 @@ public static void EmitProperty (this IClassEmitter self, in BindingContext cont
435448
propertyBlock.AppendDelegateParameter (property.ReturnType);
436449
if (context.Changes.BindingType == BindingType.Protocol && !setter.ExportPropertyData.IsNullOrDefault)
437450
propertyBlock.AppendExportAttribute (setter.ExportPropertyData);
451+
452+
if (GeneratorConfiguration.BGenCompatible && !property.SkipRegistration) {
453+
// same, needed to support the registrar until it understands the rgen attributes.
454+
var selector = setter.GetSelector (property);
455+
propertyBlock.AppendBgenExportAttribute (selector);
456+
}
438457
using (var setterBlock = propertyBlock.CreateBlock ("set", block: true)) {
439458
if (uiThreadCheck is not null) {
440459
setterBlock.WriteLine (uiThreadCheck.ToString ());
@@ -447,7 +466,7 @@ public static void EmitProperty (this IClassEmitter self, in BindingContext cont
447466

448467
// perform the invocation
449468
// if the binding is a protocol, we need to call send directly
450-
if (context.Changes.BindingType == BindingType.Protocol) {
469+
if (context.Changes.BindingType == BindingType.Protocol || property.SkipRegistration) {
451470
setterBlock.WriteLine ($"{ExpressionStatement (invocations.Setter.Value.Send)}");
452471
setterBlock.WriteLine ($"{ExpressionStatement (KeepAlive ("this"))}");
453472
} else {

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,9 @@ void EmitMethods (in BindingContext context, TabbedWriter<StringWriter> classBlo
9292
classBlock.AppendMemberAvailability (method.SymbolAvailability);
9393
classBlock.AppendGeneratedCodeAttribute ();
9494
classBlock.AppendExportAttribute (method.ExportMethodData);
95+
if (GeneratorConfiguration.BGenCompatible && !method.SkipRegistration) {
96+
classBlock.AppendBgenExportAttribute (method.ExportMethodData);
97+
}
9598
using (var methodBlock = classBlock.CreateBlock (method.ToDeclaration ().ToString (), block: true)) {
9699
methodBlock.WriteLine ($"throw new {ModelNotImplementedException} ();");
97100
}

src/rgen/Microsoft.Macios.Generator/IO/TabbedStringBuilderExtensions.cs

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,57 @@ public static TabbedWriter<StringWriter> AppendExportAttribute (this TabbedWrite
288288
return self;
289289
}
290290

291+
/// <summary>
292+
/// Appends an `[Export]` attribute to the current writer for bgen compatibility.
293+
/// This is a simplified version that only includes the selector.
294+
/// </summary>
295+
/// <param name="self">A tabbed string writer.</param>
296+
/// <param name="exportData">The export data for the method, from which the selector is extracted.</param>
297+
/// <returns>The current writer.</returns>
298+
public static TabbedWriter<StringWriter> AppendBgenExportAttribute (this TabbedWriter<StringWriter> self,
299+
ExportData<ObjCBindings.Method> exportData)
300+
{
301+
// append the old export attribute for bgen compatibility so that we do not need to update the registrar right
302+
// away
303+
self.WriteLine ($"[Export (\"{exportData.Selector}\")]");
304+
return self;
305+
}
306+
307+
/// <summary>
308+
/// Appends an `[Export]` attribute to the current writer for bgen compatibility.
309+
/// This is a simplified version that only includes the selector.
310+
/// </summary>
311+
/// <param name="self">A tabbed string writer.</param>
312+
/// <param name="selector">The selector for the export attribute.</param>
313+
/// <returns>The current writer.</returns>
314+
public static TabbedWriter<StringWriter> AppendBgenExportAttribute (this TabbedWriter<StringWriter> self,
315+
string? selector)
316+
{
317+
if (string.IsNullOrEmpty (selector))
318+
return self;
319+
320+
// append the old export attribute for bgen compatibility so that we do not need to update the registrar right
321+
// away
322+
self.WriteLine ($"[Export (\"{selector}\")]");
323+
return self;
324+
}
325+
326+
/// <summary>
327+
/// Appends an `[Export]` attribute to the current writer for bgen compatibility.
328+
/// This is a simplified version that only includes the selector.
329+
/// </summary>
330+
/// <param name="self">A tabbed string writer.</param>
331+
/// <param name="exportData">The export data for the property, from which the selector is extracted.</param>
332+
/// <returns>The current writer.</returns>
333+
public static TabbedWriter<StringWriter> AppendBgenExportAttribute (this TabbedWriter<StringWriter> self,
334+
ExportData<ObjCBindings.Property> exportData)
335+
{
336+
// append the old export attribute for bgen compatibility so that we do not need to update the registrar right
337+
// away
338+
self.WriteLine ($"[Export (\"{exportData.Selector}\")]");
339+
return self;
340+
}
341+
291342
/// <summary>
292343
/// Appends a `[Register]` attribute to the current writer.
293344
/// This attribute is used to register a type with the Objective-C runtime.

tests/rgen/Microsoft.Macios.Generator.Tests/Categories/Data/ExpectedMethodTests.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,7 @@ public static partial class MethodTests
7474
[SupportedOSPlatform ("tvos")]
7575
[SupportedOSPlatform ("maccatalyst13.1")]
7676
[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
77+
[Export ("completeRequestReturningItems:completionHandler:")]
7778
public static unsafe partial void CompleteRequest (this global::Foundation.NSObject self, global::Foundation.NSExtensionItem[] returningItems, global::System.Action<bool>? completionHandler)
7879
{
7980
if (returningItems is null)
@@ -105,6 +106,7 @@ public static unsafe partial void CompleteRequest (this global::Foundation.NSObj
105106
[SupportedOSPlatform ("tvos")]
106107
[SupportedOSPlatform ("maccatalyst13.1")]
107108
[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
109+
[Export ("completeRequestReturningItems:completionHandler:")]
108110
public static unsafe partial void CompleteRequest (this global::Foundation.NSObject self, global::Foundation.NSExtensionItem[] returningItems, global::System.Action<bool, string?>? completionHandler)
109111
{
110112
if (returningItems is null)
@@ -136,6 +138,7 @@ public static unsafe partial void CompleteRequest (this global::Foundation.NSObj
136138
[SupportedOSPlatform ("tvos")]
137139
[SupportedOSPlatform ("maccatalyst13.1")]
138140
[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
141+
[Export ("completeRequestReturningItems:completionHandler:")]
139142
public static unsafe partial void CompleteRequest (this global::Foundation.NSObject self, global::Foundation.NSExtensionItem[] returningItems, global::System.Action<bool, string, string?>? completionHandler)
140143
{
141144
if (returningItems is null)
@@ -167,6 +170,7 @@ public static unsafe partial void CompleteRequest (this global::Foundation.NSObj
167170
[SupportedOSPlatform ("tvos")]
168171
[SupportedOSPlatform ("maccatalyst13.1")]
169172
[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
173+
[Export ("filteredArrayUsingPredicate:")]
170174
public static unsafe partial global::Foundation.NSArray Filter (this global::Foundation.NSObject self, global::Foundation.NSPredicate predicate)
171175
{
172176
var predicate__handle__ = predicate!.GetNonNullHandle (nameof (predicate));
@@ -182,6 +186,7 @@ public static unsafe partial void CompleteRequest (this global::Foundation.NSObj
182186
[SupportedOSPlatform ("tvos")]
183187
[SupportedOSPlatform ("maccatalyst13.1")]
184188
[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
189+
[Export ("filteredArrayUsingStrings:")]
185190
public static unsafe partial global::Foundation.NSArray FilterStrings (this global::Foundation.NSObject self, string[] predicate)
186191
{
187192
if (predicate is null)
@@ -199,6 +204,7 @@ public static unsafe partial void CompleteRequest (this global::Foundation.NSObj
199204
[SupportedOSPlatform ("tvos")]
200205
[SupportedOSPlatform ("maccatalyst13.1")]
201206
[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
207+
[Export ("arrayWithContentsOfFile:")]
202208
public static unsafe partial static global::Foundation.NSArray FromFile (this global::Foundation.NSObject self, string path)
203209
{
204210
if (path is null)
@@ -216,6 +222,7 @@ public static unsafe partial void CompleteRequest (this global::Foundation.NSObj
216222
[SupportedOSPlatform ("tvos")]
217223
[SupportedOSPlatform ("maccatalyst13.1")]
218224
[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
225+
[Export ("bookmarkDataWithContentsOfURL:error:")]
219226
public static unsafe partial global::Foundation.NSData GetBookmarkData (this global::Foundation.NSObject self, global::Foundation.NSUrl bookmarkFileUrl, out global::Foundation.NSError? error)
220227
{
221228
var bookmarkFileUrl__handle__ = bookmarkFileUrl!.GetNonNullHandle (nameof (bookmarkFileUrl));
@@ -233,6 +240,7 @@ public static unsafe partial void CompleteRequest (this global::Foundation.NSObj
233240
[SupportedOSPlatform ("tvos")]
234241
[SupportedOSPlatform ("maccatalyst13.1")]
235242
[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
243+
[Export ("bookmarkDataWithContentsOfURL:subdomain:error:")]
236244
public static unsafe partial global::Foundation.NSData GetBookmarkData (this global::Foundation.NSObject self, global::Foundation.NSUrl bookmarkFileUrl, string subdomain, out global::Foundation.NSError? error)
237245
{
238246
if (subdomain is null)
@@ -254,6 +262,7 @@ public static unsafe partial void CompleteRequest (this global::Foundation.NSObj
254262
[UnsupportedOSPlatform ("tvos")]
255263
[SupportedOSPlatform ("maccatalyst13.1")]
256264
[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
265+
[Export ("loadFromHTMLWithRequest:options:completionHandler:")]
257266
public partial static void LoadFromHtml (this global::Foundation.NSObject self, global::Foundation.NSUrlRequest request, global::Foundation.NSDictionary options, global::Foundation.NSAttributedStringCompletionHandler completionHandler)
258267
{
259268
if (completionHandler is null)
@@ -290,6 +299,7 @@ public partial static void LoadFromHtml (this global::Foundation.NSObject self,
290299
[UnsupportedOSPlatform ("tvos")]
291300
[SupportedOSPlatform ("maccatalyst13.1")]
292301
[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
302+
[Export ("loadFromHTMLWithRequest:options:completionHandler:")]
293303
public partial static void LoadFromHtmlNoName (this global::Foundation.NSObject self, global::Foundation.NSUrlRequest request, global::Foundation.NSDictionary options, global::Foundation.NSAttributedStringCompletionHandler completionHandler)
294304
{
295305
if (completionHandler is null)
@@ -326,6 +336,7 @@ public partial static void LoadFromHtmlNoName (this global::Foundation.NSObject
326336
[SupportedOSPlatform ("tvos")]
327337
[SupportedOSPlatform ("maccatalyst13.1")]
328338
[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
339+
[Export ("setValue:forKey:")]
329340
public static unsafe partial void SetValueForKey (this global::Foundation.NSObject self, global::Foundation.NSObject value, global::Foundation.NSString key)
330341
{
331342
var value__handle__ = value!.GetNonNullHandle (nameof (value));
@@ -341,6 +352,7 @@ public static unsafe partial void SetValueForKey (this global::Foundation.NSObje
341352
[SupportedOSPlatform ("tvos")]
342353
[SupportedOSPlatform ("maccatalyst13.1")]
343354
[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
355+
[Export ("sortedArrayUsingComparator:")]
344356
public static unsafe partial global::Foundation.NSArray Sort (this global::Foundation.NSObject self, global::Foundation.NSComparator cmptr)
345357
{
346358
if (cmptr is null)
@@ -358,6 +370,7 @@ public static unsafe partial void SetValueForKey (this global::Foundation.NSObje
358370
[SupportedOSPlatform ("tvos")]
359371
[SupportedOSPlatform ("maccatalyst13.1")]
360372
[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
373+
[Export ("valueForKey:")]
361374
public static unsafe partial global::Foundation.NSObject ValueForKey (this global::Foundation.NSObject self, global::Foundation.NSString key)
362375
{
363376
var key__handle__ = key!.GetNonNullHandle (nameof (key));
@@ -373,6 +386,7 @@ public static unsafe partial void SetValueForKey (this global::Foundation.NSObje
373386
[SupportedOSPlatform ("tvos")]
374387
[SupportedOSPlatform ("maccatalyst13.1")]
375388
[BindingImpl (BindingImplOptions.GeneratedCode | BindingImplOptions.Optimizable)]
389+
[Export ("writeToFile:atomically:")]
376390
public static unsafe partial bool WriteToFile (this global::Foundation.NSObject self, string path, bool useAuxiliaryFile)
377391
{
378392
if (path is null)

0 commit comments

Comments
 (0)