Skip to content

Commit 6bc87e8

Browse files
authored
[jcw-gen] Use + for nested types, not / (#1304)
Context: dotnet/android#9747 Context: https://discord.com/channels/732297728826277939/732297837953679412/1336353039031734352 Context: https://discord.com/channels/732297728826277939/732297837953679412/1336358257769316372 Context: #1302 Context: dotnet/android#9750 The `[Register]` attribute provides "connector method" names, and for interface methods this will also include the name of the type which declares the method, which itself may be in a nested type: namespace Android.App { public partial class Application { public partial interface IActivityLifecycleCallbacks : IJavaObject, IJavaPeerable { [Register ( name: "onActivityCreated", signature: "(Landroid/app/Activity;Landroid/os/Bundle;)V", connector: "GetOnActivityCreated_Landroid_app_Activity_Landroid_os_Bundle_Handler:Android.App.Application/IActivityLifecycleCallbacksInvoker, Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null")] void OnActivityCreated (Android.App.Activity activity, Android.OS.Bundle? savedInstanceState); // … } } } The `connector` value is copied as-is into Java Callable Wrappers, as part of the `__md_methods` value and `Runtime.register()` call. Given the C# type: partial class MauiApplication : Application { partial class ActivityLifecycleCallbacks : Java.Lang.Object, Application.IActivityLifecycleCallbacks { public void OnActivityCreated (Activity activity, Bundle? savedInstanceState) => … } } then `Java.Interop.Tools.JavaCallableWrappers` will produce: // Java Callable Wrapper /* partial */ class MauiApplication_ActivityLifecycleCallbacks { public static final String __md_methods; static { __md_methods = // … "n_onActivityCreated:(Landroid/app/Activity;Landroid/os/Bundle;)V:GetOnActivityCreated_Landroid_app_Activity_Landroid_os_Bundle_Handler:Android.App.Application/IActivityLifecycleCallbacksInvoker, Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null\n" + // … ""; mono.android.Runtime.register ("Microsoft.Maui.MauiApplication+ActivityLifecycleCallbacks, Microsoft.Maui", MauiApplication_ActivityLifecycleCallbacks.class, __md_methods); } } The `signature` and `connector` values from the `[Register(…)]` on the method declaration are copied as-is into `__md_methods`. As the `connector` value contains a `/`, `__md_methods` does as well. This has worked fine for nearly 15+ years…on Mono/MonoVM. This *fails* on NativeAOT and CoreCLR, as: Type.GetType ("Android.App.Application/IActivityLifecycleCallbacksInvoker, Mono.Android, …", throwOnError:true) fails with: TypeLoadException: Could not resolve type 'Android.App.Application/IActivityLifecycleCallbacksInvoker' in assembly 'Mono.Android, …'. The reason for the failure is that when using Reflection APIs such as `Type.GetType()`, the [`Type.AssemblyQualifiedName` grammar][0] must be followed, and that grammar uses `+` to "Precede a nested class", *not* `/`. (`/` isn't even in the Reflection grammar!) (Aside: where does `/` come from then? It's the *IL* separator for nested types!) For eventual CoreCLR and NativeAOT support, then, we need to replace `/` with `+` *somewhere* before it hits `Type.GetType()`. There are (at least?) three places to do so: 1. Within `JniRuntime.JniTypeManager.RegisterNativeMembers()` or equivalent override. 2. Within `generator`, updating the contents of `[Register]`. 3. Within `Java.Interop.Tools.JavaCallableWrappers`. (1) is rejected out of hand as it would be additional work done on- device at runtime. Why do that if we don't need to? (2) was attempted in #1302 & dotnet/android#9750. It turned into a bit of a boondoggle, because there are lots of linker steps which interpret the `connector` value on `[Register]`, and it "just worked" that the `connector` value contained IL names, as the linker steps deal in IL! Trying to update `connector` to instead contain Reflection syntax required finding all the places in the linker that used `connector` values, which was tedious & brittle. "Just" (2) is also inadequate, as it would require new binding assemblies to take advantage of, so (2) *also* needed (3). Which brings us to (3), the current approach: *don't* alter the semantics of the `connect` value within `[Register]`, and instead require that `Java.Interop.Tools.JavaCallableWrappers` replace all instances of `/` with `+` within `__md_methods`. This is needed *anyway*, for compatibility with existing bindings, and also avoids lots of pain that (2) encountered. With this approach, `generator` output is unchanged, and `jcw-gen` output for `MauiApplication.ActivityLifecycleCallbacks` becomes: // Java Callable Wrapper /* partial */ class MauiApplication_ActivityLifecycleCallbacks { public static final String __md_methods; static { __md_methods = // … "n_onActivityCreated:(Landroid/app/Activity;Landroid/os/Bundle;)V:GetOnActivityCreated_Landroid_app_Activity_Landroid_os_Bundle_Handler:Android.App.Application+IActivityLifecycleCallbacksInvoker, Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null\n" + // … ""; mono.android.Runtime.register ("Microsoft.Maui.MauiApplication+ActivityLifecycleCallbacks, Microsoft.Maui", MauiApplication_ActivityLifecycleCallbacks.class, __md_methods); } } [0]: https://learn.microsoft.com/en-us/dotnet/api/system.type.assemblyqualifiedname?view=net-9.0#remarks
1 parent dd3c1d0 commit 6bc87e8

File tree

7 files changed

+96
-6
lines changed

7 files changed

+96
-6
lines changed

src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers.Adapters/CecilImporter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,7 @@ static CallableWrapperMethod CreateMethod (MethodDefinition methodDefinition, Ca
269269
static CallableWrapperMethod CreateMethod (string name, CallableWrapperType declaringType, string? signature, string? connector, string? managedParameters, string? outerType, string? superCall)
270270
{
271271
signature = signature ?? throw new ArgumentNullException ("`connector` cannot be null.", nameof (connector));
272-
var method_name = "n_" + name + ":" + signature + ":" + connector;
272+
var method_name = "n_" + name + ":" + signature + ":" + connector?.Replace ('/', '+');
273273

274274
var method = new CallableWrapperMethod (declaringType, name, method_name, signature);
275275

src/Java.Interop.Tools.TypeNameMappings/Java.Interop.Tools.TypeNameMappings/JavaNativeTypeManager.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ static string ToCliTypePart (string part)
150150
for (int i = 0; i < parts.Length; ++i) {
151151
parts [i] = ToPascalCase (parts [i], 1);
152152
}
153-
return string.Join ("/", parts);
153+
return string.Join ("+", parts);
154154
}
155155

156156
static string ToPascalCase (string value, int minLength)

tests/Java.Interop.Export-Tests/Java.Interop.Export-Tests.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@
2626
<ProjectReference Include="..\..\src\Java.Interop.Export\Java.Interop.Export.csproj" />
2727
<ProjectReference Include="..\..\src\Java.Runtime.Environment\Java.Runtime.Environment.csproj" />
2828
<ProjectReference Include="..\TestJVM\TestJVM.csproj" />
29+
<ProjectReference
30+
Include="..\..\tools\jcw-gen\jcw-gen.csproj"
31+
ReferenceOutputAssembly="false"
32+
/>
2933
</ItemGroup>
3034

3135
<ItemGroup>

tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGeneratorTests.cs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,55 @@ public void monodroidClearReferences ()
104104
Assert.AreEqual (expected, actual);
105105
}
106106

107+
[Test]
108+
public void GenerateTypeMentioningNestedInvoker ()
109+
{
110+
var actual = Generate (typeof (ApplicationName.ActivityLifecycleCallbacks));
111+
var expected = """
112+
package application;
113+
114+
115+
public class Name_ActivityLifecycleCallbacks
116+
extends java.lang.Object
117+
implements
118+
mono.android.IGCUserPeer,
119+
java.lang.Object
120+
{
121+
/** @hide */
122+
public static final String __md_methods;
123+
static {
124+
__md_methods =
125+
"n_onActivityCreated:(Landroid/app/Activity;Landroid/os/Bundle;)V:GetOnActivityCreated_Landroid_app_Activity_Landroid_os_Bundle_Handler:Android.App.Application+IActivityLifecycleCallbacksInvoker, Mono.Android\n" +
126+
"";
127+
mono.android.Runtime.register ("Xamarin.Android.ToolsTests.ApplicationName+ActivityLifecycleCallbacks, Java.Interop.Tools.JavaCallableWrappers-Tests", Name_ActivityLifecycleCallbacks.class, __md_methods);
128+
}
129+
130+
public void onActivityCreated (android.app.Activity p0, android.os.Bundle p1)
131+
{
132+
n_onActivityCreated (p0, p1);
133+
}
134+
135+
private native void n_onActivityCreated (android.app.Activity p0, android.os.Bundle p1);
136+
137+
private java.util.ArrayList refList;
138+
public void monodroidAddReference (java.lang.Object obj)
139+
{
140+
if (refList == null)
141+
refList = new java.util.ArrayList ();
142+
refList.add (obj);
143+
}
144+
145+
public void monodroidClearReferences ()
146+
{
147+
if (refList != null)
148+
refList.clear ();
149+
}
150+
}
151+
152+
""";
153+
Assert.AreEqual (expected, actual);
154+
}
155+
107156
static string Generate (Type type, string applicationJavaClass = null, string monoRuntimeInit = null, JavaPeerStyle style = JavaPeerStyle.XAJavaInterop1)
108157
{
109158
var reader_options = new CallableWrapperReaderOptions {

tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers/SupportDeclarations.cs

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,26 @@ class Application : Java.Lang.Object
2222
protected virtual void OnCreate ()
2323
{
2424
}
25+
26+
[Register ("android/app/Application$ActivityLifecycleCallbacks", DoNotGenerateAcw = true)]
27+
public partial interface IActivityLifecycleCallbacks {
28+
[Register (
29+
name: "onActivityCreated",
30+
signature: "(Landroid/app/Activity;Landroid/os/Bundle;)V",
31+
connector: "GetOnActivityCreated_Landroid_app_Activity_Landroid_os_Bundle_Handler:Android.App.Application/IActivityLifecycleCallbacksInvoker, Mono.Android")]
32+
void OnActivityCreated (Android.App.Activity activity, global::Android.OS.Bundle savedInstanceState);
33+
}
34+
35+
internal class IActivityLifecycleCallbacksInvoker : Java.Lang.Object, IActivityLifecycleCallbacks {
36+
static Delegate GetOnActivityCreated_Landroid_app_Activity_Landroid_os_Bundle_Handler ()
37+
{
38+
return null;
39+
}
40+
41+
public void OnActivityCreated (Android.App.Activity activity, global::Android.OS.Bundle savedInstanceState)
42+
{
43+
}
44+
}
2545
}
2646

2747
[Register ("android/app/Activity", DoNotGenerateAcw = true)]
@@ -43,6 +63,14 @@ public virtual void OnCreate (Java.Lang.Object arguments)
4363
}
4464
}
4565

66+
namespace Android.OS {
67+
68+
[Register ("android/os/Bundle", DoNotGenerateAcw = true)]
69+
class Bundle : Java.Lang.Object
70+
{
71+
}
72+
}
73+
4674
namespace Android.Runtime {
4775

4876
interface IJavaObject
@@ -72,6 +100,7 @@ static class SupportDeclarations
72100
typeof (AbstractClass),
73101
typeof (ActivityName),
74102
typeof (ApplicationName),
103+
typeof (ApplicationName.ActivityLifecycleCallbacks),
75104
typeof (DefaultName),
76105
typeof (DefaultName.A),
77106
typeof (DefaultName.A.B),
@@ -139,6 +168,12 @@ class ActivityName : Java.Lang.Object
139168
[Application (Name = "application.Name")]
140169
class ApplicationName : Application
141170
{
171+
public class ActivityLifecycleCallbacks : Java.Lang.Object, Application.IActivityLifecycleCallbacks
172+
{
173+
public void OnActivityCreated (Activity activity, global::Android.OS.Bundle savedInstanceState)
174+
{
175+
}
176+
}
142177
}
143178

144179
class IndirectApplication : ApplicationName

tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers/TypeNameMapGeneratorTests.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ public void WriteJavaToManaged ()
5050
v.WriteJavaToManaged (o);
5151
var a = ToArray (o);
5252
Save (a, "__j2m");
53-
var length = 190;
53+
var length = 193;
5454
var offset = 76;
5555
var e =
5656
"version=1\u0000" +
@@ -59,6 +59,7 @@ public void WriteJavaToManaged ()
5959
"value-offset=" + offset + "\u0000" +
6060
GetJ2MEntryLine (typeof (ActivityName), "activity/Name", offset, length) +
6161
GetJ2MEntryLine (typeof (ApplicationName), "application/Name", offset, length) +
62+
GetJ2MEntryLine (typeof (ApplicationName.ActivityLifecycleCallbacks), "application/Name_ActivityLifecycleCallbacks", offset, length) +
6263
GetJ2MEntryLine (typeof (DefaultName), "crc64197ae30a36756915/DefaultName", offset, length) +
6364
GetJ2MEntryLine (typeof (DefaultName.A), "crc64197ae30a36756915/DefaultName_A", offset, length) +
6465
GetJ2MEntryLine (typeof (DefaultName.A.B), "crc64197ae30a36756915/DefaultName_A_B", offset, length) +
@@ -128,8 +129,8 @@ public void WriteManagedToJava ()
128129
v.WriteManagedToJava (o);
129130
var a = ToArray (o);
130131
Save (a, "__m2j");
131-
var length = 190;
132-
var offset = 114;
132+
var length = 193;
133+
var offset = 117;
133134
var e =
134135
"version=1\u0000" +
135136
$"entry-count={types.Count}\u0000" +
@@ -138,6 +139,7 @@ public void WriteManagedToJava ()
138139
GetM2JEntryLine (typeof (AbstractClass), "my/AbstractClass", offset, length) +
139140
GetM2JEntryLine (typeof (AbstractClassInvoker), "my/AbstractClass", offset, length) +
140141
GetM2JEntryLine (typeof (ActivityName), "activity/Name", offset, length) +
142+
GetM2JEntryLine (typeof (ApplicationName.ActivityLifecycleCallbacks), "application/Name_ActivityLifecycleCallbacks", offset, length) +
141143
GetM2JEntryLine (typeof (ApplicationName), "application/Name", offset, length) +
142144
GetM2JEntryLine (typeof (DefaultName.A.B), "crc64197ae30a36756915/DefaultName_A_B", offset, length) +
143145
GetM2JEntryLine (typeof (DefaultName.A), "crc64197ae30a36756915/DefaultName_A", offset, length) +

tools/generator/Java.Interop.Tools.Generator.ObjectModel/ClassGen.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ public static void GenerateTypeRegistrations (CodeGenerationOptions opt, Generat
181181
int ls = reg.Key.LastIndexOf ('/');
182182
string package = ls >= 0 ? reg.Key.Substring (0, ls) : "";
183183

184-
if (JavaNativeTypeManager.ToCliType (reg.Key) == reg.Value)
184+
if (JavaNativeTypeManager.ToCliType (reg.Key) == reg.Value.Replace ('/', '+'))
185185
continue;
186186
if (!mapping.TryGetValue (package, out var v))
187187
mapping.Add (package, v = new List<KeyValuePair<string, string>> ());

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy