Skip to content

Commit 2bec237

Browse files
[nativeaot] fix typemap logic involving duplicates
This is WIP. The bug I'm seeing now is `Java.Interop.JavaObject` is in the typemap in favor of `Java.Lang.Object`. There was also some missing logic regarding `*Invoker` types and abstract types. The new test assertion can verify the entire typemap, so we should assert a couple types are in there.
1 parent a6b0f13 commit 2bec237

File tree

2 files changed

+57
-6
lines changed

2 files changed

+57
-6
lines changed

src/Microsoft.Android.Sdk.ILLink/TypeMappingStep.cs

Lines changed: 31 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public class TypeMappingStep : BaseStep
1616
{
1717
const string AssemblyName = "Microsoft.Android.Runtime.NativeAOT";
1818
const string TypeName = "Microsoft.Android.Runtime.NativeAotTypeManager";
19-
readonly IDictionary<string, TypeDefinition> TypeMappings = new Dictionary<string, TypeDefinition> (StringComparer.Ordinal);
19+
readonly IDictionary<string, List<TypeDefinition>> TypeMappings = new Dictionary<string, List<TypeDefinition>> (StringComparer.Ordinal);
2020
AssemblyDefinition? MicrosoftAndroidRuntimeNativeAot;
2121

2222
protected override void ProcessAssembly (AssemblyDefinition assembly)
@@ -66,7 +66,7 @@ protected override void EndProcess ()
6666
var il = method.Body.GetILProcessor ();
6767
var addMethod = module.ImportReference (typeof (IDictionary<string, Type>).GetMethod ("Add"));
6868
var getTypeFromHandle = module.ImportReference (typeof (Type).GetMethod ("GetTypeFromHandle"));
69-
foreach (var (javaKey, typeDefinition) in TypeMappings) {
69+
foreach (var (javaName, list) in TypeMappings) {
7070
/*
7171
* IL_0000: ldarg.0
7272
* IL_0001: ldfld class [System.Runtime]System.Collections.Generic.IDictionary`2<string, class [System.Runtime]System.Type> Microsoft.Android.Runtime.NativeAotTypeManager::TypeMappings
@@ -77,22 +77,47 @@ protected override void EndProcess ()
7777
*/
7878
il.Emit (Mono.Cecil.Cil.OpCodes.Ldarg_0);
7979
il.Emit (Mono.Cecil.Cil.OpCodes.Ldfld, field);
80-
il.Emit (Mono.Cecil.Cil.OpCodes.Ldstr, javaKey);
81-
il.Emit (Mono.Cecil.Cil.OpCodes.Ldtoken, module.ImportReference (typeDefinition));
80+
il.Emit (Mono.Cecil.Cil.OpCodes.Ldstr, javaName);
81+
il.Emit (Mono.Cecil.Cil.OpCodes.Ldtoken, module.ImportReference (SelectTypeDefinition (javaName, list)));
8282
il.Emit (Mono.Cecil.Cil.OpCodes.Call, getTypeFromHandle);
8383
il.Emit (Mono.Cecil.Cil.OpCodes.Callvirt, addMethod);
8484
}
8585

8686
il.Emit (Mono.Cecil.Cil.OpCodes.Ret);
8787
}
8888

89+
TypeDefinition SelectTypeDefinition (string javaName, List<TypeDefinition> list)
90+
{
91+
if (list.Count == 1)
92+
return list[0];
93+
94+
var best = list[0];
95+
foreach (var type in list) {
96+
if (type == best)
97+
continue;
98+
if ((type.IsAbstract || type.IsInterface) &&
99+
!best.IsAbstract &&
100+
!best.IsInterface &&
101+
type.IsAssignableFrom (best, Context)) {
102+
best = type;
103+
}
104+
}
105+
foreach (var type in list) {
106+
if (type == best)
107+
continue;
108+
Context.LogMessage ($"Duplicate typemap entry for {javaName} => {type.FullName}");
109+
}
110+
return best;
111+
}
112+
89113
void ProcessType (AssemblyDefinition assembly, TypeDefinition type)
90114
{
91115
if (type.HasJavaPeer (Context)) {
92116
var javaName = JavaNativeTypeManager.ToJniName (type, Context);
93-
if (!TypeMappings.TryAdd (javaName, type)) {
94-
Context.LogMessage ($"Duplicate typemap entry for {javaName}");
117+
if (!TypeMappings.TryGetValue (javaName, out var list)) {
118+
TypeMappings.Add (javaName, list = new List<TypeDefinition> ());
95119
}
120+
list.Add (type);
96121
}
97122

98123
if (!type.HasNestedTypes)

src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ public void NativeAOT ()
135135
proj.SetProperty ("PublishAot", "true");
136136
proj.SetProperty ("PublishAotUsingRuntimePack", "true");
137137
proj.SetProperty ("AndroidNdkDirectory", AndroidNdkPath);
138+
proj.SetProperty ("_ExtraTrimmerArgs", "--verbose");
138139

139140
using var b = CreateApkBuilder ();
140141
Assert.IsTrue (b.Build (proj), "Build should have succeeded.");
@@ -165,6 +166,31 @@ public void NativeAOT ()
165166
Assert.IsNotNull (method, $"{linkedMonoAndroidAssembly} should contain {typeName}.{methodName}");
166167
}
167168

169+
var linkedRuntimeAssembly = Path.Combine (intermediate, "linked", "Microsoft.Android.Runtime.NativeAOT.dll");
170+
FileAssert.Exists (linkedRuntimeAssembly);
171+
using (var assembly = AssemblyDefinition.ReadAssembly (linkedRuntimeAssembly)) {
172+
var type = assembly.MainModule.Types.FirstOrDefault (t => t.Name == "NativeAotTypeManager");
173+
Assert.IsNotNull (type, $"{linkedRuntimeAssembly} should contain NativeAotTypeManager");
174+
var method = type.Methods.FirstOrDefault (m => m.Name == "InitializeTypeMappings");
175+
Assert.IsNotNull (method, "NativeAotTypeManager should contain InitializeTypeMappings");
176+
177+
var typemap = new Dictionary<string, TypeReference> ();
178+
foreach (var i in method.Body.Instructions) {
179+
if (i.OpCode != Mono.Cecil.Cil.OpCodes.Ldstr)
180+
continue;
181+
if (i.Operand is not string javaName)
182+
continue;
183+
if (i.Next.Operand is not TypeReference t)
184+
continue;
185+
typemap.Add (javaName, t);
186+
}
187+
if (typemap.TryGetValue ("java/lang/Object", out var reference)) {
188+
Assert.AreEqual ("Java.Lang.Object", $"{reference.Namespace}.{reference.Name}");
189+
} else {
190+
Assert.Fail ("InitializeTypeMappings should contain Ldstr \"java/lang/Object\"!");
191+
}
192+
}
193+
168194
var dexFile = Path.Combine (intermediate, "android", "bin", "classes.dex");
169195
FileAssert.Exists (dexFile);
170196
foreach (var className in mono_classes) {

0 commit comments

Comments
 (0)