Skip to content

Commit e2c70fd

Browse files
[TrimmableTypeMap] Fix #11037 rebase compatibility
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent b5b29b1 commit e2c70fd

7 files changed

Lines changed: 199 additions & 15 deletions

File tree

src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/JcwJavaSourceGenerator.cs

Lines changed: 33 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -122,15 +122,28 @@ static void WriteClassDeclaration (JavaPeerInfo type, TextWriter writer)
122122

123123
static void WriteStaticInitializer (JavaPeerInfo type, TextWriter writer)
124124
{
125+
string className = JniSignatureHelper.GetJavaSimpleName (type.JavaName);
126+
125127
// Application and Instrumentation types cannot call registerNatives in their
126-
// static initializer — the native library isn't loaded yet at that point.
127-
// Their registerNatives call is emitted in the generated
128-
// ApplicationRegistration.registerApplications() method instead.
128+
// static initializer — the runtime isn't ready yet at that point. Emit a
129+
// lazy one-time helper instead so the first managed callback can register
130+
// the class just before invoking its native method.
129131
if (type.CannotRegisterInStaticConstructor) {
132+
writer.Write ($$"""
133+
private static boolean __md_natives_registered;
134+
private static synchronized void __md_registerNatives ()
135+
{
136+
if (!__md_natives_registered) {
137+
mono.android.Runtime.registerNatives ({{className}}.class);
138+
__md_natives_registered = true;
139+
}
140+
}
141+
142+
143+
""");
130144
return;
131145
}
132146

133-
string className = JniSignatureHelper.GetJavaSimpleName (type.JavaName);
134147
writer.Write ($$"""
135148
static {
136149
mono.android.Runtime.registerNatives ({{className}}.class);
@@ -154,7 +167,17 @@ static void WriteConstructors (JavaPeerInfo type, TextWriter writer)
154167
public {{simpleClassName}} ({{parameters}})
155168
{
156169
super ({{superArgs}});
170+
171+
""");
172+
173+
if (!type.CannotRegisterInStaticConstructor) {
174+
writer.Write ($$"""
157175
if (getClass () == {{simpleClassName}}.class) nctor_{{ctor.ConstructorIndex}} ({{args}});
176+
177+
""");
178+
}
179+
180+
writer.Write ($$"""
158181
}
159182
160183
@@ -197,6 +220,10 @@ static void WriteFields (JavaPeerInfo type, TextWriter writer)
197220

198221
static void WriteMethods (JavaPeerInfo type, TextWriter writer)
199222
{
223+
string registerNativesLine = type.CannotRegisterInStaticConstructor
224+
? "\t\t__md_registerNatives ();\n"
225+
: "";
226+
200227
foreach (var method in type.MarshalMethods) {
201228
if (method.IsConstructor) {
202229
continue;
@@ -222,7 +249,7 @@ static void WriteMethods (JavaPeerInfo type, TextWriter writer)
222249
@Override
223250
public {{javaReturnType}} {{method.JniName}} ({{parameters}}){{throwsClause}}
224251
{
225-
{{returnPrefix}}{{method.NativeCallbackName}} ({{args}});
252+
{{registerNativesLine}} {{returnPrefix}}{{method.NativeCallbackName}} ({{args}});
226253
}
227254
public native {{javaReturnType}} {{method.NativeCallbackName}} ({{parameters}});
228255
@@ -233,7 +260,7 @@ static void WriteMethods (JavaPeerInfo type, TextWriter writer)
233260
234261
{{access}} {{javaReturnType}} {{method.JniName}} ({{parameters}}){{throwsClause}}
235262
{
236-
{{returnPrefix}}{{method.NativeCallbackName}} ({{args}});
263+
{{registerNativesLine}} {{returnPrefix}}{{method.NativeCallbackName}} ({{args}});
237264
}
238265
{{access}} native {{javaReturnType}} {{method.NativeCallbackName}} ({{parameters}});
239266

src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/Model/TypeMapAssemblyData.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,11 @@ sealed record TypeMapAttributeData
7979
/// </summary>
8080
sealed class JavaPeerProxyData
8181
{
82+
/// <summary>
83+
/// JNI type name this proxy represents, e.g. "java/lang/Object".
84+
/// </summary>
85+
public required string JniName { get; init; }
86+
8287
/// <summary>
8388
/// Simple type name, e.g., "Java_Lang_Object_Proxy".
8489
/// </summary>

src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -173,7 +173,7 @@ static JavaPeerProxyData BuildProxyType (JavaPeerInfo peer, HashSet<string> used
173173
{
174174
// Use managed type name for proxy naming to guarantee uniqueness across aliases
175175
// (two types with the same JNI name will have different managed names).
176-
var proxyTypeName = peer.ManagedTypeName.Replace ('.', '_').Replace ('+', '_') + "_Proxy";
176+
var proxyTypeName = peer.ManagedTypeName.Replace ('.', '_').Replace ('+', '_').Replace ('`', '_') + "_Proxy";
177177

178178
// Guard against name collisions (e.g., "My.Type" and "My_Type" both map to "My_Type_Proxy")
179179
if (!usedProxyNames.Add (proxyTypeName)) {
@@ -187,6 +187,7 @@ static JavaPeerProxyData BuildProxyType (JavaPeerInfo peer, HashSet<string> used
187187
}
188188

189189
var proxy = new JavaPeerProxyData {
190+
JniName = peer.JavaName,
190191
TypeName = proxyTypeName,
191192
TargetType = new TypeRefData {
192193
ManagedTypeName = peer.ManagedTypeName,

src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/PEAssemblyBuilder.cs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ TypeDefinitionHandle GetOrCreateSizedType (int size)
239239
int typeMethodStart = Metadata.GetRowCount (TableIndex.MethodDef) + 1;
240240

241241
var handle = Metadata.AddTypeDefinition (
242-
TypeAttributes.NestedPrivate | TypeAttributes.ExplicitLayout | TypeAttributes.Sealed | TypeAttributes.AnsiClass,
242+
TypeAttributes.NestedAssembly | TypeAttributes.ExplicitLayout | TypeAttributes.Sealed | TypeAttributes.AnsiClass,
243243
default,
244244
Metadata.GetOrAddString ($"__utf8_{size}"),
245245
Metadata.AddTypeReference (SystemRuntimeRef,
@@ -259,7 +259,7 @@ TypeDefinitionHandle GetOrCreateSizedType (int size)
259259
/// </summary>
260260
public MethodDefinitionHandle EmitBody (string name, MethodAttributes attrs,
261261
Action<BlobEncoder> encodeSig, Action<InstructionEncoder> emitIL)
262-
=> EmitBody (name, attrs, encodeSig, emitIL, encodeLocals: null);
262+
=> EmitBody (name, attrs, encodeSig, emitIL, encodeLocals: null, useBranches: false);
263263

264264
/// <summary>
265265
/// Emits a method body and definition with optional local variable declarations.
@@ -271,7 +271,7 @@ public MethodDefinitionHandle EmitBody (string name, MethodAttributes attrs,
271271
/// </param>
272272
public MethodDefinitionHandle EmitBody (string name, MethodAttributes attrs,
273273
Action<BlobEncoder> encodeSig, Action<InstructionEncoder> emitIL,
274-
Action<BlobBuilder>? encodeLocals)
274+
Action<BlobBuilder>? encodeLocals, bool useBranches = false)
275275
{
276276
_sigBlob.Clear ();
277277
encodeSig (new BlobEncoder (_sigBlob));
@@ -287,7 +287,9 @@ public MethodDefinitionHandle EmitBody (string name, MethodAttributes attrs,
287287
}
288288

289289
_codeBlob.Clear ();
290-
var encoder = new InstructionEncoder (_codeBlob);
290+
var encoder = useBranches
291+
? new InstructionEncoder (_codeBlob, new ControlFlowBuilder ())
292+
: new InstructionEncoder (_codeBlob);
291293
emitIL (encoder);
292294

293295
while (ILBuilder.Count % 4 != 0) {

tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/JcwJavaSourceGeneratorTests.cs

Lines changed: 61 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,16 +138,20 @@ public void Generate_AcwType_HasRegisterNativesStaticBlock ()
138138
public void Generate_ApplicationType_SkipsRegisterNatives ()
139139
{
140140
var java = GenerateFixture ("my/app/MyApplication");
141-
Assert.DoesNotContain ("registerNatives", java);
142141
Assert.DoesNotContain ("static {", java);
142+
Assert.DoesNotContain ("if (getClass () == MyApplication.class) nctor_0 ();", java);
143+
AssertContainsLine ("private static synchronized void __md_registerNatives ()\n", java);
144+
AssertContainsLine ("mono.android.Runtime.registerNatives (MyApplication.class);\n", java);
143145
}
144146

145147
[Fact]
146148
public void Generate_InstrumentationType_SkipsRegisterNatives ()
147149
{
148150
var java = GenerateFixture ("my/app/MyInstrumentation");
149-
Assert.DoesNotContain ("registerNatives", java);
150151
Assert.DoesNotContain ("static {", java);
152+
Assert.DoesNotContain ("if (getClass () == MyInstrumentation.class) nctor_0 ();", java);
153+
AssertContainsLine ("private static synchronized void __md_registerNatives ()\n", java);
154+
AssertContainsLine ("mono.android.Runtime.registerNatives (MyInstrumentation.class);\n", java);
151155
}
152156

153157
}
@@ -257,6 +261,61 @@ public void Generate_MarshalMethod_HasOverrideAndNativeDeclaration ()
257261
AssertContainsLine ("public native void n_OnCreate_Landroid_os_Bundle_ (android.os.Bundle p0);\n", java);
258262
}
259263

264+
[Fact]
265+
public void Generate_OverrideAcrossIntermediateMcwBase_HasMethodStub ()
266+
{
267+
var java = GenerateFixture ("my/app/SelectableList");
268+
AssertContainsLine ("@Override\n", java);
269+
AssertContainsLine ("public void setSelection (int p0)\n", java);
270+
AssertContainsLine ("n_SetSelection_I (p0);\n", java);
271+
AssertContainsLine ("public native void n_SetSelection_I (int p0);\n", java);
272+
}
273+
274+
[Fact]
275+
public void Generate_OverrideAcrossGenericIntermediateMcwBase_HasMethodStub ()
276+
{
277+
var java = GenerateFixture ("my/app/GenericSelectableList");
278+
AssertContainsLine ("@Override\n", java);
279+
AssertContainsLine ("public void setSelection (int p0)\n", java);
280+
AssertContainsLine ("n_SetSelection_I (p0);\n", java);
281+
AssertContainsLine ("public native void n_SetSelection_I (int p0);\n", java);
282+
}
283+
284+
[Fact]
285+
public void Generate_DeferredRegistrationType_LazilyRegistersBeforeNativeCallback ()
286+
{
287+
var type = new JavaPeerInfo {
288+
JavaName = "my/app/DeferredInstrumentation",
289+
CompatJniName = "my/app/DeferredInstrumentation",
290+
ManagedTypeName = "MyApp.DeferredInstrumentation",
291+
ManagedTypeNamespace = "MyApp",
292+
ManagedTypeShortName = "DeferredInstrumentation",
293+
AssemblyName = "App",
294+
BaseJavaName = "android/app/Instrumentation",
295+
CannotRegisterInStaticConstructor = true,
296+
MarshalMethods = new List<MarshalMethodInfo> {
297+
new () {
298+
JniName = "onCreate",
299+
JniSignature = "(Landroid/os/Bundle;)V",
300+
ManagedMethodName = "OnCreate",
301+
NativeCallbackName = "n_OnCreate_Landroid_os_Bundle_",
302+
Connector = "GetOnCreate_Landroid_os_Bundle_Handler",
303+
},
304+
new () {
305+
JniName = "onStart",
306+
JniSignature = "()V",
307+
ManagedMethodName = "OnStart",
308+
NativeCallbackName = "n_OnStart",
309+
Connector = "GetOnStartHandler",
310+
},
311+
},
312+
};
313+
314+
var java = GenerateToString (type);
315+
AssertContainsLine ("__md_registerNatives ();\n\t\tn_OnCreate_Landroid_os_Bundle_ (p0);\n", java);
316+
AssertContainsLine ("__md_registerNatives ();\n\t\tn_OnStart ();\n", java);
317+
}
318+
260319
}
261320

262321
public class NestedType

tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapAssemblyGeneratorTests.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,7 @@ public void Generate_ProxyType_UsesGenericJavaPeerProxyBase ()
139139
objectProxyBaseType.DecodeSignature (SignatureTypeProvider.Instance, genericContext: null));
140140
}
141141

142+
[Fact]
142143
public void Generate_HasIgnoresAccessChecksToAttribute ()
143144
{
144145
var peers = ScanFixtures ();

tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/TestFixtures/TestTypes.cs

Lines changed: 91 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,20 @@ public class Service : Java.Lang.Object
4949
{
5050
protected Service (IntPtr handle, JniHandleOwnership transfer) : base (handle, transfer) { }
5151
}
52+
53+
[Register ("android/app/Application", DoNotGenerateAcw = true)]
54+
public class Application : Java.Lang.Object
55+
{
56+
public Application () { }
57+
protected Application (IntPtr handle, JniHandleOwnership transfer) : base (handle, transfer) { }
58+
}
59+
60+
[Register ("android/app/Instrumentation", DoNotGenerateAcw = true)]
61+
public class Instrumentation : Java.Lang.Object
62+
{
63+
public Instrumentation () { }
64+
protected Instrumentation (IntPtr handle, JniHandleOwnership transfer) : base (handle, transfer) { }
65+
}
5266
}
5367

5468
namespace Android.App.Backup
@@ -317,8 +331,11 @@ public void PublicMethod () { }
317331
protected void ProtectedMethod () { }
318332
}
319333

334+
[Register ("my/app/BaseApplication")]
335+
public abstract class BaseApplication : Android.App.Application { }
336+
320337
[Application (Name = "my.app.MyApplication", BackupAgent = typeof (MyBackupAgent), ManageSpaceActivity = typeof (MyManageSpaceActivity))]
321-
public class MyApplication : Java.Lang.Object { }
338+
public class MyApplication : BaseApplication { }
322339

323340
/// <summary>
324341
/// Has [ExportField] methods that should produce Java field declarations.
@@ -335,8 +352,14 @@ protected ExportFieldExample (IntPtr handle, JniHandleOwnership transfer) : base
335352
public string GetValue () => "";
336353
}
337354

355+
[Register ("my/app/BaseInstrumentation")]
356+
public abstract class BaseInstrumentation : Android.App.Instrumentation { }
357+
358+
[Register ("my/app/IntermediateInstrumentation")]
359+
public abstract class IntermediateInstrumentation : BaseInstrumentation { }
360+
338361
[Instrumentation (Name = "my.app.MyInstrumentation")]
339-
public class MyInstrumentation : Java.Lang.Object { }
362+
public class MyInstrumentation : IntermediateInstrumentation { }
340363

341364
[Register ("my/app/MyBackupAgent")]
342365
public class MyBackupAgent : Android.App.Backup.BackupAgent
@@ -751,6 +774,72 @@ protected OverloadDerived (IntPtr handle, JniHandleOwnership transfer) : base (h
751774
public override void Process (int value) { }
752775
}
753776

777+
/// <summary>
778+
/// Declares a registered abstract method above an intermediate MCW base type.
779+
/// Mirrors AdapterView.SetSelection(int) for AbsListView-derived test fixtures.
780+
/// </summary>
781+
[Register ("my/app/SelectionHost", DoNotGenerateAcw = true)]
782+
public abstract class SelectionHost : Java.Lang.Object
783+
{
784+
protected SelectionHost (IntPtr handle, JniHandleOwnership transfer) : base (handle, transfer) { }
785+
786+
[Register ("setSelection", "(I)V", "GetSetSelection_IHandler")]
787+
public abstract void SetSelection (int position);
788+
}
789+
790+
/// <summary>
791+
/// Intermediate MCW base that inherits the registered method without redeclaring it.
792+
/// </summary>
793+
[Register ("my/app/SelectionContainer", DoNotGenerateAcw = true)]
794+
public abstract class SelectionContainer : SelectionHost
795+
{
796+
protected SelectionContainer (IntPtr handle, JniHandleOwnership transfer) : base (handle, transfer) { }
797+
}
798+
799+
/// <summary>
800+
/// Generic base used to verify override discovery through a generic-instantiated base type.
801+
/// Mirrors AdapterView&lt;T&gt; in the real Mono.Android hierarchy.
802+
/// </summary>
803+
[Register ("my/app/GenericSelectionHost", DoNotGenerateAcw = true)]
804+
public abstract class GenericSelectionHost<T> : Java.Lang.Object where T : class
805+
{
806+
protected GenericSelectionHost (IntPtr handle, JniHandleOwnership transfer) : base (handle, transfer) { }
807+
808+
[Register ("setSelection", "(I)V", "GetSetSelection_IHandler")]
809+
public abstract void SetSelection (int position);
810+
}
811+
812+
/// <summary>
813+
/// Intermediate MCW base that closes the generic base.
814+
/// </summary>
815+
[Register ("my/app/GenericSelectionContainer", DoNotGenerateAcw = true)]
816+
public abstract class GenericSelectionContainer : GenericSelectionHost<string>
817+
{
818+
protected GenericSelectionContainer (IntPtr handle, JniHandleOwnership transfer) : base (handle, transfer) { }
819+
}
820+
821+
/// <summary>
822+
/// Overrides a registered method declared above the first MCW base in the hierarchy.
823+
/// </summary>
824+
[Register ("my/app/SelectableList")]
825+
public class SelectableList : SelectionContainer
826+
{
827+
protected SelectableList (IntPtr handle, JniHandleOwnership transfer) : base (handle, transfer) { }
828+
829+
public override void SetSelection (int position) { }
830+
}
831+
832+
/// <summary>
833+
/// Overrides a registered method declared above a generic-instantiated MCW base.
834+
/// </summary>
835+
[Register ("my/app/GenericSelectableList")]
836+
public class GenericSelectableList : GenericSelectionContainer
837+
{
838+
protected GenericSelectableList (IntPtr handle, JniHandleOwnership transfer) : base (handle, transfer) { }
839+
840+
public override void SetSelection (int position) { }
841+
}
842+
754843
/// <summary>
755844
/// Has a ctor with unsigned primitive params to test JNI mapping.
756845
/// </summary>

0 commit comments

Comments
 (0)