Skip to content

Commit 79061b4

Browse files
[Xamarin.Android.Build.Tasks] make "managed typemap" runtime agnostic
For NativeAOT, we implemented a "managed" typemap that is trimmer-safe. In order to test its performance characteristics, we can make this typemap useable for Mono and CoreCLR as well: * Move `NativeAotTypeManager`, `NativeAotValueManager`, and `TypeMapping` types to `Mono.Android.dll` * Rename `NativeAot*` to `Managed*` * Add a new private `$(_AndroidTypeMapImplementation)` MSBuild property that can be set to `llvm-ir` or `managed`. * Add a new trimmer feature flag `Android.Runtime.RuntimeFeature.ManagedTypeMap` that when `true` uses the managed typemap on any runtime. I added a test that verifies `dotnet run` succeeds for all typemap implementations. Note that NativeAOT will *only* support the managed typemap.
1 parent 43f82b7 commit 79061b4

File tree

20 files changed

+87
-50
lines changed

20 files changed

+87
-50
lines changed

build-tools/create-packs/Microsoft.Android.Runtime.proj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,6 @@ projects that use the Microsoft.Android framework in .NET 6+.
4646
/>
4747
<_AndroidRuntimePackAssemblies
4848
Include="$(_MonoAndroidNETOutputRoot)$(AndroidLatestStableApiLevel)\System.IO.Hashing.dll"
49-
Condition=" '$(AndroidRuntime)' == 'NativeAOT' "
5049
NoSymbols="true"
5150
/>
5251
<_AndroidRuntimePackAssemblies Include="$(_MonoAndroidNETOutputRoot)$(AndroidLatestStableApiLevel)\Mono.Android.Export.dll" />

src/Microsoft.Android.Runtime.NativeAOT/Android.Runtime.NativeAOT/JavaInteropRuntime.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,11 +37,11 @@ static void init (IntPtr jnienv, IntPtr klass)
3737
var settings = new DiagnosticSettings ();
3838
settings.AddDebugDotnetLog ();
3939

40-
var typeManager = new NativeAotTypeManager ();
40+
var typeManager = new ManagedTypeManager ();
4141
var options = new NativeAotRuntimeOptions {
4242
EnvironmentPointer = jnienv,
4343
TypeManager = typeManager,
44-
ValueManager = new NativeAotValueManager (typeManager),
44+
ValueManager = new ManagedValueManager (typeManager),
4545
UseMarshalMemberBuilder = false,
4646
JniGlobalReferenceLogWriter = settings.GrefLog,
4747
JniLocalReferenceLogWriter = settings.LrefLog,

src/Microsoft.Android.Runtime.NativeAOT/Java.Interop/JreRuntime.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
using System.Reflection;
1111
using System.Runtime.InteropServices;
1212
using System.Threading;
13+
using Android.Runtime;
1314
using Microsoft.Android.Runtime;
1415

1516
namespace Java.Interop {
@@ -58,10 +59,10 @@ static NativeAotRuntimeOptions CreateJreVM (NativeAotRuntimeOptions builder)
5859
throw new InvalidOperationException ($"Member `{nameof (NativeAotRuntimeOptions)}.{nameof (NativeAotRuntimeOptions.JvmLibraryPath)}` must be set.");
5960

6061
#if NET
61-
builder.TypeManager ??= new NativeAotTypeManager ();
62+
builder.TypeManager ??= new ManagedTypeManager ();
6263
#endif // NET
6364

64-
builder.ValueManager ??= new NativeAotValueManager (builder.TypeManager);
65+
builder.ValueManager ??= new ManagedValueManager (builder.TypeManager);
6566
builder.ObjectReferenceManager ??= new ManagedObjectReferenceManager (builder.JniGlobalReferenceLogWriter, builder.JniLocalReferenceLogWriter);
6667

6768
if (builder.InvocationPointer != IntPtr.Zero || builder.EnvironmentPointer != IntPtr.Zero)

src/Microsoft.Android.Runtime.NativeAOT/Microsoft.Android.Runtime.NativeAOT.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@
3838
<Target Name="_CopyToPackDirs">
3939
<ItemGroup>
4040
<_RuntimePackFiles Include="$(OutputPath)Microsoft.Android.Runtime.NativeAOT.dll" AndroidRID="%(AndroidAbiAndRuntimeFlavor.AndroidRID)" AndroidRuntime="%(AndroidAbiAndRuntimeFlavor.AndroidRuntime)" />
41-
<_RuntimePackFiles Include="$(OutputPath)System.IO.Hashing.dll" AndroidRID="%(AndroidAbiAndRuntimeFlavor.AndroidRID)" AndroidRuntime="%(AndroidAbiAndRuntimeFlavor.AndroidRuntime)" />
4241
</ItemGroup>
4342
<Message Importance="high" Text="$(TargetPath) %(AndroidAbiAndRuntimeFlavor.AndroidRID)" />
4443
<Copy

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

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,11 @@ namespace Microsoft.Android.Sdk.ILLink;
1919
/// </summary>
2020
public class TypeMappingStep : BaseStep
2121
{
22-
const string AssemblyName = "Microsoft.Android.Runtime.NativeAOT";
23-
const string TypeName = "Microsoft.Android.Runtime.TypeMapping";
22+
const string AssemblyName = "Mono.Android";
23+
const string TypeName = "Android.Runtime.ManagedTypeMapping";
2424
const string SystemIOHashingAssemblyPathCustomData = "SystemIOHashingAssemblyPath";
2525
readonly IDictionary<string, List<TypeDefinition>> TypeMappings = new Dictionary<string, List<TypeDefinition>> (StringComparer.Ordinal);
26-
AssemblyDefinition? MicrosoftAndroidRuntimeNativeAot;
26+
AssemblyDefinition? MonoAndroidAssembly;
2727

2828
delegate ulong HashMethod (ReadOnlySpan<byte> data, long seed = 0);
2929
HashMethod? _hashMethod;
@@ -36,7 +36,7 @@ protected override void Process ()
3636
protected override void ProcessAssembly (AssemblyDefinition assembly)
3737
{
3838
if (assembly.Name.Name == AssemblyName) {
39-
MicrosoftAndroidRuntimeNativeAot = assembly;
39+
MonoAndroidAssembly = assembly;
4040
return;
4141
}
4242
if (Annotations?.GetAction (assembly) == AssemblyAction.Delete)
@@ -51,11 +51,11 @@ protected override void EndProcess ()
5151
{
5252
Context.LogMessage ($"Writing {TypeMappings.Count} typemap entries");
5353

54-
if (MicrosoftAndroidRuntimeNativeAot is null) {
54+
if (MonoAndroidAssembly is null) {
5555
throw new InvalidOperationException ($"Unable to find {AssemblyName} assembly");
5656
}
5757

58-
var module = MicrosoftAndroidRuntimeNativeAot.MainModule;
58+
var module = MonoAndroidAssembly.MainModule;
5959
var type = module.GetType (TypeName);
6060
if (type is null) {
6161
throw new InvalidOperationException ($"Unable to find {TypeName} type");

src/Mono.Android/Android.Runtime/AndroidRuntime.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,8 +101,13 @@ public AndroidRuntimeOptions (IntPtr jnienv,
101101
ClassLoader_LoadClass_id= classLoader_loadClass;
102102
InvocationPointer = vm;
103103
ObjectReferenceManager = new AndroidObjectReferenceManager ();
104-
TypeManager = new AndroidTypeManager (jniAddNativeMethodRegistrationAttributePresent);
105-
ValueManager = new AndroidValueManager ();
104+
if (RuntimeFeature.ManagedTypeMap) {
105+
TypeManager = new ManagedTypeManager ();
106+
ValueManager = new ManagedValueManager (TypeManager);
107+
} else {
108+
TypeManager = new AndroidTypeManager (jniAddNativeMethodRegistrationAttributePresent);
109+
ValueManager = new AndroidValueManager ();
110+
}
106111
UseMarshalMemberBuilder = false;
107112
JniAddNativeMethodRegistrationAttributePresent = jniAddNativeMethodRegistrationAttributePresent;
108113
}
Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
1+
using System;
2+
using System.Collections.Generic;
13
using System.Diagnostics.CodeAnalysis;
24
using System.Reflection;
35
using Java.Interop;
46
using Java.Interop.Tools.TypeNameMappings;
57

6-
namespace Microsoft.Android.Runtime;
8+
namespace Android.Runtime;
79

8-
partial class NativeAotTypeManager : JniRuntime.JniTypeManager {
10+
internal class ManagedTypeManager : JniRuntime.JniTypeManager {
911

1012
const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
1113
internal const DynamicallyAccessedMemberTypes Methods = DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods;
1214
internal const DynamicallyAccessedMemberTypes MethodsAndPrivateNested = Methods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes;
1315

14-
public NativeAotTypeManager ()
16+
public ManagedTypeManager ()
1517
{
1618
}
1719

@@ -127,7 +129,7 @@ public override void RegisterNativeMembers (
127129

128130
protected override IEnumerable<Type> GetTypesForSimpleReference (string jniSimpleReference)
129131
{
130-
if (TypeMapping.TryGetType (jniSimpleReference, out var target)) {
132+
if (ManagedTypeMapping.TryGetType (jniSimpleReference, out var target)) {
131133
yield return target;
132134
}
133135
foreach (var t in base.GetTypesForSimpleReference (jniSimpleReference)) {
@@ -141,7 +143,7 @@ protected override IEnumerable<string> GetSimpleReferences (Type type)
141143
yield return r;
142144
}
143145

144-
if (TypeMapping.TryGetJavaClassName (type, out var javaClassName)) {
146+
if (ManagedTypeMapping.TryGetJavaClassName (type, out var javaClassName)) {
145147
yield return javaClassName;
146148
}
147149
}
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1+
using System;
12
using System.Buffers.Binary;
23
using System.Diagnostics;
34
using System.Diagnostics.CodeAnalysis;
45
using System.IO.Hashing;
56
using System.Runtime.InteropServices;
67
using System.Text;
7-
using Android.Runtime;
88

9-
namespace Microsoft.Android.Runtime;
9+
namespace Android.Runtime;
1010

11-
internal static class TypeMapping
11+
internal static class ManagedTypeMapping
1212
{
1313
internal static bool TryGetType (string javaClassName, [NotNullWhen (true)] out Type? type)
1414
{
Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -11,19 +11,18 @@
1111
using System.Runtime.CompilerServices;
1212
using System.Runtime.InteropServices;
1313
using System.Threading;
14-
using Android.Runtime;
1514
using Java.Interop;
1615

17-
namespace Microsoft.Android.Runtime;
16+
namespace Android.Runtime;
1817

19-
class NativeAotValueManager : JniRuntime.JniValueManager
18+
internal class ManagedValueManager : JniRuntime.JniValueManager
2019
{
2120
const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
2221

2322
readonly JniRuntime.JniTypeManager TypeManager;
2423
Dictionary<int, List<IJavaPeerable>>? RegisteredInstances = new Dictionary<int, List<IJavaPeerable>>();
2524

26-
public NativeAotValueManager(JniRuntime.JniTypeManager typeManager) =>
25+
public ManagedValueManager(JniRuntime.JniTypeManager typeManager) =>
2726
TypeManager = typeManager;
2827

2928
public override void WaitForGCBridgeProcessing ()
@@ -33,7 +32,7 @@ public override void WaitForGCBridgeProcessing ()
3332
public override void CollectPeers ()
3433
{
3534
if (RegisteredInstances == null)
36-
throw new ObjectDisposedException (nameof (NativeAotValueManager));
35+
throw new ObjectDisposedException (nameof (ManagedValueManager));
3736

3837
var peers = new List<IJavaPeerable> ();
3938

@@ -62,7 +61,7 @@ public override void CollectPeers ()
6261
public override void AddPeer (IJavaPeerable value)
6362
{
6463
if (RegisteredInstances == null)
65-
throw new ObjectDisposedException (nameof (NativeAotValueManager));
64+
throw new ObjectDisposedException (nameof (ManagedValueManager));
6665

6766
var r = value.PeerReference;
6867
if (!r.IsValid)
@@ -127,7 +126,7 @@ void WarnNotReplacing (int key, IJavaPeerable ignoreValue, IJavaPeerable keepVal
127126
public override IJavaPeerable? PeekPeer (JniObjectReference reference)
128127
{
129128
if (RegisteredInstances == null)
130-
throw new ObjectDisposedException (nameof (NativeAotValueManager));
129+
throw new ObjectDisposedException (nameof (ManagedValueManager));
131130

132131
if (!reference.IsValid)
133132
return null;
@@ -153,7 +152,7 @@ void WarnNotReplacing (int key, IJavaPeerable ignoreValue, IJavaPeerable keepVal
153152
public override void RemovePeer (IJavaPeerable value)
154153
{
155154
if (RegisteredInstances == null)
156-
throw new ObjectDisposedException (nameof (NativeAotValueManager));
155+
throw new ObjectDisposedException (nameof (ManagedValueManager));
157156

158157
if (value == null)
159158
throw new ArgumentNullException (nameof (value));
@@ -243,7 +242,7 @@ void ActivateViaReflection (JniObjectReference reference, ConstructorInfo cinfo,
243242
public override List<JniSurfacedPeerInfo> GetSurfacedPeers ()
244243
{
245244
if (RegisteredInstances == null)
246-
throw new ObjectDisposedException (nameof (NativeAotValueManager));
245+
throw new ObjectDisposedException (nameof (ManagedValueManager));
247246

248247
lock (RegisteredInstances) {
249248
var peers = new List<JniSurfacedPeerInfo> (RegisteredInstances.Count);
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System;
2+
using System.Diagnostics.CodeAnalysis;
3+
4+
namespace Android.Runtime;
5+
6+
static class RuntimeFeature
7+
{
8+
const string FeatureSwitchPrefix = "Android.Runtime.RuntimeFeature.";
9+
10+
[FeatureSwitchDefinition ($"{FeatureSwitchPrefix}{nameof (ManagedTypeMap)}")]
11+
internal static bool ManagedTypeMap { get; } =
12+
AppContext.TryGetSwitch ($"{FeatureSwitchPrefix}{nameof (ManagedTypeMap)}", out bool isEnabled) ? isEnabled : false;
13+
}

0 commit comments

Comments
 (0)