From 94d9a7bccb9796eb78af2d3c1a0b8ecf80c19a08 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Sat, 8 Feb 2025 16:45:47 +0100 Subject: [PATCH 01/11] Generate get function pointer methods --- .../PreserveLists/Mono.Android.xml | 1 + .../Android.Runtime/JNIEnvInit.cs | 3 + .../ManagedMarshalMethodsLookupTable.cs | 36 +++ src/Mono.Android/Mono.Android.csproj | 1 + .../Tasks/GenerateJavaStubs.cs | 15 +- .../Tasks/GeneratePackageManagerJava.cs | 5 +- .../Xamarin.Android.Build.Tests/BuildTest2.cs | 2 +- .../Utilities/EnvironmentHelper.cs | 6 + .../Utilities/ApplicationConfig.cs | 1 + ...pplicationConfigNativeAssemblyGenerator.cs | 2 + .../ManagedMarshalMethodsLookupGenerator.cs | 244 ++++++++++++++++++ .../ManagedMarshalMethodsLookupInfo.cs | 96 +++++++ .../MarshalMethodsAssemblyRewriter.cs | 13 +- .../MarshalMethodsNativeAssemblyGenerator.cs | 20 +- .../Utilities/NativeCodeGenState.cs | 2 + .../Xamarin.Android.Common.targets | 5 + .../mono/monodroid/monodroid-glue-internal.hh | 3 + src/native/mono/monodroid/monodroid-glue.cc | 13 +- .../monodroid/xamarin-android-app-context.cc | 45 ++++ .../xamarin-app-stub/application_dso_stub.cc | 1 + .../mono/xamarin-app-stub/xamarin-app.hh | 1 + 21 files changed, 500 insertions(+), 15 deletions(-) create mode 100644 src/Mono.Android/Java.Interop/ManagedMarshalMethodsLookupTable.cs create mode 100644 src/Xamarin.Android.Build.Tasks/Utilities/ManagedMarshalMethodsLookupGenerator.cs create mode 100644 src/Xamarin.Android.Build.Tasks/Utilities/ManagedMarshalMethodsLookupInfo.cs diff --git a/src/Microsoft.Android.Sdk.ILLink/PreserveLists/Mono.Android.xml b/src/Microsoft.Android.Sdk.ILLink/PreserveLists/Mono.Android.xml index aa0a341e0bb..aee7513a8e2 100644 --- a/src/Microsoft.Android.Sdk.ILLink/PreserveLists/Mono.Android.xml +++ b/src/Microsoft.Android.Sdk.ILLink/PreserveLists/Mono.Android.xml @@ -34,6 +34,7 @@ --> + diff --git a/src/Mono.Android/Android.Runtime/JNIEnvInit.cs b/src/Mono.Android/Android.Runtime/JNIEnvInit.cs index 2499b693cf2..7e93806c21c 100644 --- a/src/Mono.Android/Android.Runtime/JNIEnvInit.cs +++ b/src/Mono.Android/Android.Runtime/JNIEnvInit.cs @@ -32,6 +32,7 @@ internal struct JnienvInitializeArgs { public bool jniRemappingInUse; public bool marshalMethodsEnabled; public IntPtr grefGCUserPeerable; + public bool managedMarshalMethodsLookupEnabled; } #pragma warning restore 0649 @@ -39,6 +40,7 @@ internal struct JnienvInitializeArgs { internal static bool IsRunningOnDesktop; internal static bool jniRemappingInUse; internal static bool MarshalMethodsEnabled; + internal static bool ManagedMarshalMethodsLookupEnabled; internal static bool PropagateExceptions; internal static BoundExceptionType BoundExceptionType; internal static int gref_gc_threshold; @@ -96,6 +98,7 @@ internal static unsafe void Initialize (JnienvInitializeArgs* args) jniRemappingInUse = args->jniRemappingInUse; MarshalMethodsEnabled = args->marshalMethodsEnabled; + ManagedMarshalMethodsLookupEnabled = args->managedMarshalMethodsLookupEnabled; java_class_loader = args->grefLoader; BoundExceptionType = (BoundExceptionType)args->ioExceptionType; diff --git a/src/Mono.Android/Java.Interop/ManagedMarshalMethodsLookupTable.cs b/src/Mono.Android/Java.Interop/ManagedMarshalMethodsLookupTable.cs new file mode 100644 index 00000000000..c621a6dadd4 --- /dev/null +++ b/src/Mono.Android/Java.Interop/ManagedMarshalMethodsLookupTable.cs @@ -0,0 +1,36 @@ +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +using Android.Runtime; + +namespace Java.Interop; + +internal class ManagedMarshalMethodsLookupTable +{ + [UnmanagedCallersOnly] + static unsafe void GetFunctionPointer (int assemblyIndex, int classIndex, int methodIndex, IntPtr target) + { + try + { + IntPtr result = GetFunctionPointer (assemblyIndex, classIndex, methodIndex); + if (result == IntPtr.Zero || result == (IntPtr)(-1)) + { + throw new InvalidOperationException ($"Failed to get function pointer for ({assemblyIndex}, {classIndex}, {methodIndex})"); + } + + *(IntPtr*)target = result; + } + catch (Exception ex) + { + AndroidEnvironment.UnhandledException (ex); + } + } + + static IntPtr GetFunctionPointer (int assemblyIndex, int classIndex, int methodIndex) + { + // ManagedMarshalMethodsLookupGenerator generates the body of this method is generated at app build time + throw new NotImplementedException (); + } +} diff --git a/src/Mono.Android/Mono.Android.csproj b/src/Mono.Android/Mono.Android.csproj index 6c409057f53..4ace3c02152 100644 --- a/src/Mono.Android/Mono.Android.csproj +++ b/src/Mono.Android/Mono.Android.csproj @@ -312,6 +312,7 @@ + diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs index a908645c829..7901854c66d 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs @@ -54,6 +54,7 @@ public class GenerateJavaStubs : AndroidTask public bool LinkingEnabled { get; set; } public bool HaveMultipleRIDs { get; set; } public bool EnableMarshalMethods { get; set; } + public bool EnableManagedMarshalMethodsLookup { get; set; } public string ManifestTemplate { get; set; } public string[] MergedManifestDocuments { get; set; } @@ -244,8 +245,16 @@ void Run (bool useMarshalMethods) foreach (var kvp in nativeCodeGenStates) { NativeCodeGenState state = kvp.Value; - RewriteMarshalMethods (state, brokenExceptionTransitionsEnabled); - state.Classifier.AddSpecialCaseMethods (); + if (!EnableManagedMarshalMethodsLookup) { + RewriteMarshalMethods (state, brokenExceptionTransitionsEnabled); + state.Classifier.AddSpecialCaseMethods (); + } else { + // We need to run `AddSpecialCaseMethods` before `RewriteMarshalMethods` so that we can see the special case + // methods (such as TypeManager.n_Activate_mm) when generating the managed lookup tables. + state.Classifier.AddSpecialCaseMethods (); + state.ManagedMarshalMethodsLookupInfo = new ManagedMarshalMethodsLookupInfo (Log); + RewriteMarshalMethods (state, brokenExceptionTransitionsEnabled); + } Log.LogDebugMessage ($"[{state.TargetArch}] Number of generated marshal methods: {state.Classifier.MarshalMethods.Count}"); if (state.Classifier.RejectedMethodCount > 0) { @@ -459,7 +468,7 @@ void RewriteMarshalMethods (NativeCodeGenState state, bool brokenExceptionTransi return; } - var rewriter = new MarshalMethodsAssemblyRewriter (Log, state.TargetArch, state.Classifier, state.Resolver); + var rewriter = new MarshalMethodsAssemblyRewriter (Log, state.TargetArch, state.Classifier, state.Resolver, state.ManagedMarshalMethodsLookupInfo); rewriter.Rewrite (brokenExceptionTransitionsEnabled); } diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs index 1bb04982727..3c087a6ccec 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs @@ -62,6 +62,7 @@ public class GeneratePackageManagerJava : AndroidTask public bool EnablePreloadAssembliesDefault { get; set; } public bool EnableMarshalMethods { get; set; } + public bool EnableManagedMarshalMethodsLookup { get; set; } public string RuntimeConfigBinFilePath { get; set; } public string BoundExceptionType { get; set; } @@ -339,6 +340,7 @@ void AddEnvironment () JniRemappingReplacementTypeCount = jniRemappingNativeCodeInfo == null ? 0 : jniRemappingNativeCodeInfo.ReplacementTypeCount, JniRemappingReplacementMethodIndexEntryCount = jniRemappingNativeCodeInfo == null ? 0 : jniRemappingNativeCodeInfo.ReplacementMethodIndexEntryCount, MarshalMethodsEnabled = EnableMarshalMethods, + ManagedMarshalMethodsLookupEnabled = EnableManagedMarshalMethodsLookup, IgnoreSplitConfigs = ShouldIgnoreSplitConfigs (), }; LLVMIR.LlvmIrModule appConfigModule = appConfigAsmGen.Construct (); @@ -367,7 +369,8 @@ void AddEnvironment () Log, assemblyCount, uniqueAssemblyNames, - EnsureCodeGenState (targetArch) + EnsureCodeGenState (targetArch), + EnableManagedMarshalMethodsLookup ); } else { marshalMethodsAsmGen = new MarshalMethodsNativeAssemblyGenerator ( diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs index 77045ab29c0..4090f21d2c6 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/BuildTest2.cs @@ -415,7 +415,7 @@ public void XA1037PropertyDeprecatedWarning (string property, string value, bool XamarinAndroidProject proj = isBindingProject ? new XamarinAndroidBindingProject () : new XamarinAndroidApplicationProject (); proj.IsRelease = isRelease; proj.SetProperty (property, value); - + using (ProjectBuilder b = isBindingProject ? CreateDllBuilder (Path.Combine ("temp", TestName)) : CreateApkBuilder (Path.Combine ("temp", TestName))) { Assert.IsTrue (b.Build (proj), "Build should have succeeded."); Assert.IsTrue (StringAssertEx.ContainsText (b.LastBuildOutput, $"The '{property}' MSBuild property is deprecated and will be removed"), diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/EnvironmentHelper.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/EnvironmentHelper.cs index 075f0154f98..f151e755b0a 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/EnvironmentHelper.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/EnvironmentHelper.cs @@ -65,6 +65,7 @@ public sealed class ApplicationConfig public uint jni_remapping_replacement_method_index_entry_count; public uint mono_components_mask; public string android_package_name = String.Empty; + public bool managed_marshal_methods_lookup_enabled; } const uint ApplicationConfigFieldCount = 26; @@ -335,6 +336,11 @@ static ApplicationConfig ReadApplicationConfig (EnvironmentFile envFile) Assert.IsTrue (expectedPointerTypes.Contains (field [0]), $"Unexpected pointer field type in '{envFile.Path}:{item.LineNumber}': {field [0]}"); pointers.Add (field [1].Trim ()); break; + + case 26: // managed_marshal_methods_lookup_enabled: bool / .byte + AssertFieldType (envFile.Path, parser.SourceFilePath, ".byte", field [0], item.LineNumber); + ret.managed_marshal_methods_lookup_enabled = ConvertFieldToBool ("managed_marshal_methods_lookup_enabled", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]); + break; } fieldCount++; } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfig.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfig.cs index 9a2335f9e75..40ea29ee665 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfig.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfig.cs @@ -58,5 +58,6 @@ sealed class ApplicationConfig [NativeAssembler (NumberFormat = LLVMIR.LlvmIrVariableNumberFormat.Hexadecimal)] public uint mono_components_mask; public string android_package_name = String.Empty; + public bool managed_marshal_methods_lookup_enabled; } } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs index 50d079388dc..9d895f40129 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs @@ -187,6 +187,7 @@ sealed class XamarinAndroidBundledAssembly public PackageNamingPolicy PackageNamingPolicy { get; set; } public List NativeLibraries { get; set; } public bool MarshalMethodsEnabled { get; set; } + public bool ManagedMarshalMethodsLookupEnabled { get; set; } public bool IgnoreSplitConfigs { get; set; } public ApplicationConfigNativeAssemblyGenerator (IDictionary environmentVariables, IDictionary systemProperties, TaskLoggingHelper log) @@ -231,6 +232,7 @@ protected override void Construct (LlvmIrModule module) have_runtime_config_blob = HaveRuntimeConfigBlob, have_assemblies_blob = HaveAssemblyStore, marshal_methods_enabled = MarshalMethodsEnabled, + managed_marshal_methods_lookup_enabled = ManagedMarshalMethodsLookupEnabled, ignore_split_configs = IgnoreSplitConfigs, bound_stream_io_exception_type = (byte)BoundExceptionType, package_naming_policy = (uint)PackageNamingPolicy, diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ManagedMarshalMethodsLookupGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ManagedMarshalMethodsLookupGenerator.cs new file mode 100644 index 00000000000..f1d365c9e01 --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ManagedMarshalMethodsLookupGenerator.cs @@ -0,0 +1,244 @@ +using System; +using System.Collections.Generic; + +using Microsoft.Android.Build.Tasks; +using Microsoft.Build.Utilities; +using Mono.Cecil; +using Mono.Cecil.Cil; +using Xamarin.Android.Tools; + +namespace Xamarin.Android.Tasks; + +class ManagedMarshalMethodsLookupGenerator +{ + readonly ManagedMarshalMethodsLookupInfo managedMarshalMethodsLookupInfo; + readonly TaskLoggingHelper log; + readonly AndroidTargetArch targetArch; + readonly TypeDefinition functionPointerLookup; + + readonly Dictionary> typeReferenceCache = new (); + + public ManagedMarshalMethodsLookupGenerator ( + TaskLoggingHelper log, + AndroidTargetArch targetArch, + ManagedMarshalMethodsLookupInfo managedMarshalMethodsLookupInfo, + TypeDefinition functionPointerLookup) + { + this.log = log; + this.targetArch = targetArch; + this.managedMarshalMethodsLookupInfo = managedMarshalMethodsLookupInfo; + this.functionPointerLookup = functionPointerLookup; + } + + public void Generate (IEnumerable> marshalMethods) + { + foreach (IList methodList in marshalMethods) { + foreach (MarshalMethodEntry method in methodList) { + managedMarshalMethodsLookupInfo.AddNativeCallbackWrapper (method.NativeCallbackWrapper); + } + } + + foreach (var assemblyInfo in managedMarshalMethodsLookupInfo.AssemblyLookup.Values) { + foreach (var classInfo in assemblyInfo.ClassLookup.Values) { + classInfo.GetFunctionPointerMethod = GenerateGetFunctionPointer (classInfo); + } + + assemblyInfo.GetFunctionPointerMethod = GenerateGetFunctionPointerPerAssembly (assemblyInfo); + } + + GenerateGlobalGetFunctionPointerMethod (); + } + + MethodDefinition GenerateGetFunctionPointer (ManagedMarshalMethodsLookupInfo.ClassLookupInfo classLookup) + { + var declaringType = classLookup.DeclaringType; + log.LogDebugMessage ($"Generating `GetFunctionPointer` for {declaringType.FullName} ({classLookup.MethodLookup.Count} UCO methods)"); + + var intType = ImportReference (declaringType.Module, typeof (int)); + var intPtrType = ImportReference (declaringType.Module, typeof (System.IntPtr)); + + var getFunctionPointerMethod = classLookup.GetFunctionPointerMethod = new MethodDefinition ("GetFunctionPointer", MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, intPtrType); + + getFunctionPointerMethod.DeclaringType = declaringType; + declaringType.Methods.Add (getFunctionPointerMethod); + + var methodIndexParameter = new ParameterDefinition ("methodIndex", ParameterAttributes.None, intType); + getFunctionPointerMethod.Parameters.Add (methodIndexParameter); + + // we're setting the index here as we are creating the actual switch targets array to guarantee the indexing will be in sync + var targets = new Instruction [classLookup.MethodLookup.Count]; + uint methodIndex = 0; + foreach (var methodLookup in classLookup.MethodLookup.Values) { + methodLookup.Index = methodIndex++; + targets [methodLookup.Index] = Instruction.Create (OpCodes.Ldftn, methodLookup.NativeCallbackWrapper); + } + + var invalidUcoTarget = Instruction.Create (OpCodes.Nop); + + var il = getFunctionPointerMethod.Body.GetILProcessor (); + il.Emit (OpCodes.Ldarg, methodIndexParameter); + il.Emit (OpCodes.Switch, targets); + + il.Emit (OpCodes.Br_S, invalidUcoTarget); + + for (var k = 0; k < targets.Length; k++) { + il.Append (targets [k]); + il.Emit (OpCodes.Ret); + } + + // no hit? this shouldn't happen + il.Append (invalidUcoTarget); + il.Emit (OpCodes.Ldc_I4_M1); + il.Emit (OpCodes.Conv_I); + il.Emit (OpCodes.Ret); + + // in the case of private/private protected/protected nested types, we need to generate proxy method(s) in the parent type(s) + // so that we can call the actual GetFunctionPointer method from our assembly-level GetFunctionPointer method + while (declaringType.IsNested && (declaringType.IsNestedPrivate || declaringType.IsNestedFamily || declaringType.IsNestedFamilyAndAssembly)) { + var parentType = declaringType.DeclaringType; + + var parentProxyMethod = new MethodDefinition ($"GetFunctionPointer_{declaringType.Name}", MethodAttributes.Assembly | MethodAttributes.Static | MethodAttributes.HideBySig, intPtrType); + parentProxyMethod.DeclaringType = parentType; + parentType.Methods.Add (parentProxyMethod); + + var parentMethodIndexParameter = new ParameterDefinition ("methodIndex", ParameterAttributes.None, intType); + parentProxyMethod.Parameters.Add (parentMethodIndexParameter); + + var proxyIl = parentProxyMethod.Body.GetILProcessor (); + proxyIl.Emit (OpCodes.Ldarg, parentMethodIndexParameter); + proxyIl.Emit (OpCodes.Call, getFunctionPointerMethod); + proxyIl.Emit (OpCodes.Ret); + + declaringType = parentType; + getFunctionPointerMethod = parentProxyMethod; + } + + return getFunctionPointerMethod; + } + + MethodDefinition GenerateGetFunctionPointerPerAssembly (ManagedMarshalMethodsLookupInfo.AssemblyLookupInfo assemblyInfo) + { + var module = assemblyInfo.Assembly.MainModule; + + var intType = ImportReference (module, typeof (int)); + var intPtrType = ImportReference (module, typeof (System.IntPtr)); + var objectType = ImportReference (module, typeof (object)); + + var lookupType = new TypeDefinition ("Java.Interop", "__ManagedMarshalMethodsLookupTable__", TypeAttributes.Public | TypeAttributes.Sealed, objectType); + module.Types.Add (lookupType); + + var getFunctionPointerMethod = new MethodDefinition ($"GetFunctionPointer", MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, intPtrType); + getFunctionPointerMethod.DeclaringType = lookupType; + lookupType.Methods.Add (getFunctionPointerMethod); + + var classIndexParameter = new ParameterDefinition ("classIndex", ParameterAttributes.None, intType); + getFunctionPointerMethod.Parameters.Add (classIndexParameter); + + var methodIndexParameter = new ParameterDefinition ("methodIndex", ParameterAttributes.None, intType); + getFunctionPointerMethod.Parameters.Add (methodIndexParameter); + + // we're setting the index here as we are creating the actual switch targets array to guarantee the indexing will be in sync + var targets = new Instruction [assemblyInfo.ClassLookup.Count]; + uint classIndex = 0; + foreach (var classInfo in assemblyInfo.ClassLookup.Values) { + classInfo.Index = classIndex++; + targets [classInfo.Index] = Instruction.Create (OpCodes.Call, module.Import (classInfo.GetFunctionPointerMethod)); + } + + var il = getFunctionPointerMethod.Body.GetILProcessor (); + il.Emit (OpCodes.Ldarg, methodIndexParameter); + + il.Emit (OpCodes.Ldarg, classIndexParameter); + il.Emit (OpCodes.Switch, targets); + + var defaultTarget = Instruction.Create (OpCodes.Nop); + il.Emit (OpCodes.Br, defaultTarget); + + for (int i = 0; i < targets.Length; i++) { + il.Append (targets [i]); // call + il.Emit (OpCodes.Ret); + } + + // no hit? this shouldn't happen + il.Append (defaultTarget); + il.Emit (OpCodes.Pop); // methodIndex + il.Emit (OpCodes.Ldc_I4_M1); + il.Emit (OpCodes.Conv_I); + il.Emit (OpCodes.Ret); + + return getFunctionPointerMethod; + } + + void GenerateGlobalGetFunctionPointerMethod () + { + var module = functionPointerLookup.Module; + + var intType = ImportReference (module, typeof (int)); + var intPtrType = ImportReference (module, typeof (System.IntPtr)); + + var getFunctionPointerMethod = FindMethod (functionPointerLookup, "GetFunctionPointer", parametersCount: 3, required: true); + getFunctionPointerMethod.Body.Instructions.Clear (); + + // we're setting the index here as we are creating the actual switch targets array to guarantee the indexing will be in sync + var targets = new Instruction [managedMarshalMethodsLookupInfo.AssemblyLookup.Count]; + uint assemblyIndex = 0; + foreach (var assemblyInfo in managedMarshalMethodsLookupInfo.AssemblyLookup.Values) { + assemblyInfo.Index = assemblyIndex++; + targets [assemblyInfo.Index] = Instruction.Create (OpCodes.Call, module.Import (assemblyInfo.GetFunctionPointerMethod)); + } + + var il = getFunctionPointerMethod.Body.GetILProcessor (); + il.Emit (OpCodes.Ldarg_1); // classIndex + il.Emit (OpCodes.Ldarg_2); // methodIndex + + il.Emit (OpCodes.Ldarg_0); // assemblyIndex + il.Emit (OpCodes.Switch, targets); + + var defaultTarget = Instruction.Create (OpCodes.Nop); + il.Emit (OpCodes.Br_S, defaultTarget); + + for (int i = 0; i < targets.Length; i++) { + il.Append (targets [i]); // call + il.Emit (OpCodes.Ret); + } + + // no hit? this shouldn't happen + il.Append (defaultTarget); + il.Emit (OpCodes.Pop); // methodIndex + il.Emit (OpCodes.Pop); // classIndex + il.Emit (OpCodes.Ldc_I4_M1); + il.Emit (OpCodes.Conv_I); + il.Emit (OpCodes.Ret); + } + + TypeReference ImportReference (ModuleDefinition module, Type type) + { + if (!typeReferenceCache.TryGetValue (module, out var cache)) { + typeReferenceCache [module] = cache = new (); + } + + if (!cache.TryGetValue (type, out var typeReference)) { + cache [type] = typeReference = module.ImportReference (type); + } + + return typeReference; + } + + MethodDefinition? FindMethod (TypeDefinition type, string methodName, int parametersCount, bool required) + { + log.LogDebugMessage ($"[{targetArch}] Looking for method '{methodName}' with {parametersCount} params in type {type}"); + foreach (MethodDefinition method in type.Methods) { + log.LogDebugMessage ($"[{targetArch}] method: {method.Name}"); + if (method.Parameters.Count == parametersCount && String.Compare (methodName, method.Name, StringComparison.Ordinal) == 0) { + log.LogDebugMessage ($"[{targetArch}] match!"); + return method; + } + } + + if (required) { + throw new InvalidOperationException ($"[{targetArch}] Internal error: required method '{methodName}' in type {type} not found"); + } + + return null; + } +} diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ManagedMarshalMethodsLookupInfo.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ManagedMarshalMethodsLookupInfo.cs new file mode 100644 index 00000000000..d6d856a89cc --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ManagedMarshalMethodsLookupInfo.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; + +using Java.Interop.Tools.Cecil; +using Mono.Cecil; +using Xamarin.Android.Tools; +using Microsoft.Android.Build.Tasks; +using Microsoft.Build.Utilities; + +namespace Xamarin.Android.Tasks; + +class ManagedMarshalMethodsLookupInfo (TaskLoggingHelper log) +{ + readonly TaskLoggingHelper _log = log; + + public Dictionary AssemblyLookup { get; } = new (StringComparer.Ordinal); + + public (uint AssemblyIndex, uint ClassIndex, uint MethodIndex) GetIndex (MethodDefinition nativeCallbackWrapper) + { + var (assemblyName, className, methodName) = GetNames (nativeCallbackWrapper); + + if (!AssemblyLookup.TryGetValue (assemblyName, out var assemblyInfo)) { + throw new InvalidOperationException ($"Assembly '{assemblyName}' not found in the lookup indexes."); + } + + if (!assemblyInfo.ClassLookup.TryGetValue (className, out var classInfo)) { + throw new InvalidOperationException ($"Class '{className}' not found in the lookup indexes."); + } + + if (!classInfo.MethodLookup.TryGetValue (methodName, out var methodLookup)) { + throw new InvalidOperationException ($"Method '{methodName}' not found in the lookup indexes."); + } + + if (assemblyInfo.Index < 0 || classInfo.Index < 0 || methodLookup.Index < 0) { + throw new InvalidOperationException ($"Invalid index values for {assemblyName}/{className}/{methodName}: {assemblyInfo.Index}, {classInfo.Index}, {methodLookup.Index}"); + } + + return (assemblyInfo.Index, classInfo.Index, methodLookup.Index); + } + + public void AddNativeCallbackWrapper (MethodDefinition nativeCallbackWrapper) + { + var (assemblyName, className, methodName) = GetNames (nativeCallbackWrapper); + + if (!AssemblyLookup.TryGetValue (assemblyName, out var assemblyInfo)) { + AssemblyLookup [assemblyName] = assemblyInfo = new AssemblyLookupInfo (); + } + + if (!assemblyInfo.ClassLookup.TryGetValue (className, out var classInfo)) { + assemblyInfo.ClassLookup [className] = classInfo = new ClassLookupInfo (); + } + + if (!classInfo.MethodLookup.TryGetValue (methodName, out var methodInfo)) { + classInfo.MethodLookup [methodName] = methodInfo = new MethodLookupInfo (); + } else { + _log.LogDebugMessage ($"Method '{assemblyName}'/'{className}'/'{methodName}' already has an associated UnmanagedCallersOnly method."); + return; + } + + assemblyInfo.Assembly = nativeCallbackWrapper.DeclaringType.Module.Assembly; + classInfo.DeclaringType = nativeCallbackWrapper.DeclaringType; + methodInfo.NativeCallbackWrapper = nativeCallbackWrapper; + } + + private static (string, string, string) GetNames (MethodDefinition nativeCallback) + { + var type = nativeCallback.DeclaringType; + var assemblyName = type.Module.Assembly.Name.Name; + var className = type.FullName; + var methodName = nativeCallback.Name; + + return (assemblyName, className, methodName); + } + + internal sealed class AssemblyLookupInfo + { + public uint Index { get; set; } = uint.MaxValue; + public AssemblyDefinition Assembly { get; set; } + public MethodDefinition? GetFunctionPointerMethod { get; set; } + public Dictionary ClassLookup { get; } = new (StringComparer.Ordinal); + } + + internal sealed class ClassLookupInfo + { + public uint Index { get; set; } = uint.MaxValue; + public TypeDefinition DeclaringType { get; set; } + public MethodDefinition? GetFunctionPointerMethod { get; set; } + public Dictionary MethodLookup { get; } = new (StringComparer.Ordinal); + } + + internal sealed class MethodLookupInfo + { + public uint Index { get; set; } = uint.MaxValue; + public MethodDefinition NativeCallbackWrapper { get; set; } + } +} diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsAssemblyRewriter.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsAssemblyRewriter.cs index 9f1034bd6b8..5de2f6392d8 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsAssemblyRewriter.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsAssemblyRewriter.cs @@ -25,13 +25,15 @@ sealed class AssemblyImports readonly MarshalMethodsClassifier classifier; readonly XAAssemblyResolver resolver; readonly AndroidTargetArch targetArch; + readonly ManagedMarshalMethodsLookupInfo? managedMarshalMethodsLookupInfo; - public MarshalMethodsAssemblyRewriter (TaskLoggingHelper log, AndroidTargetArch targetArch, MarshalMethodsClassifier classifier, XAAssemblyResolver resolver) + public MarshalMethodsAssemblyRewriter (TaskLoggingHelper log, AndroidTargetArch targetArch, MarshalMethodsClassifier classifier, XAAssemblyResolver resolver, ManagedMarshalMethodsLookupInfo? managedMarshalMethodsLookupInfo) { this.log = log ?? throw new ArgumentNullException (nameof (log)); this.targetArch = targetArch; this.classifier = classifier ?? throw new ArgumentNullException (nameof (classifier));; this.resolver = resolver ?? throw new ArgumentNullException (nameof (resolver));; + this.managedMarshalMethodsLookupInfo = managedMarshalMethodsLookupInfo; } // TODO: do away with broken exception transitions, there's no point in supporting them @@ -103,6 +105,15 @@ public void Rewrite (bool brokenExceptionTransitions) } } + if (managedMarshalMethodsLookupInfo is not null) { + // TODO the code should probably go to different assemblies than Mono.Android (to avoid recursive dependencies) + var rootAssembly = resolver.Resolve ("Mono.Android") ?? throw new InvalidOperationException ($"[{targetArch}] Internal error: unable to load the Mono.Android assembly"); + var managedMarshalMethodsLookupTableType = FindType (rootAssembly, "Java.Interop.ManagedMarshalMethodsLookupTable", required: true); + + var managedMarshalMethodLookupGenerator = new ManagedMarshalMethodsLookupGenerator (log, targetArch, managedMarshalMethodsLookupInfo, managedMarshalMethodsLookupTableType); + managedMarshalMethodLookupGenerator.Generate (classifier.MarshalMethods.Values); + } + foreach (AssemblyDefinition asm in classifier.Assemblies) { string? path = asm.MainModule.FileName; if (String.IsNullOrEmpty (path)) { diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsNativeAssemblyGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsNativeAssemblyGenerator.cs index cf2e1bf15e3..6b7e9d83ebc 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsNativeAssemblyGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsNativeAssemblyGenerator.cs @@ -237,6 +237,7 @@ public MarshalMethodAssemblyIndexValuePlaceholder (MarshalMethodInfo mmi, Assemb readonly LlvmIrCallMarker defaultCallMarker; readonly bool generateEmptyCode; + readonly bool managedMarshalMethodsLookupEnabled; readonly AndroidTargetArch targetArch; readonly NativeCodeGenState? codeGenState; @@ -256,12 +257,13 @@ public MarshalMethodsNativeAssemblyGenerator (TaskLoggingHelper log, AndroidTarg /// /// Constructor to be used ONLY when marshal methods are ENABLED /// - public MarshalMethodsNativeAssemblyGenerator (TaskLoggingHelper log, int numberOfAssembliesInApk, ICollection uniqueAssemblyNames, NativeCodeGenState codeGenState) + public MarshalMethodsNativeAssemblyGenerator (TaskLoggingHelper log, int numberOfAssembliesInApk, ICollection uniqueAssemblyNames, NativeCodeGenState codeGenState, bool managedMarshalMethodsLookupEnabled) : base (log) { this.numberOfAssembliesInApk = numberOfAssembliesInApk; this.uniqueAssemblyNames = uniqueAssemblyNames ?? throw new ArgumentNullException (nameof (uniqueAssemblyNames)); this.codeGenState = codeGenState ?? throw new ArgumentNullException (nameof (codeGenState)); + this.managedMarshalMethodsLookupEnabled = managedMarshalMethodsLookupEnabled; generateEmptyCode = false; defaultCallMarker = LlvmIrCallMarker.Tail; @@ -703,12 +705,16 @@ void WriteBody (LlvmIrFunctionBody body) LlvmIrLocalVariable getFuncPtrResult = func.CreateLocalVariable (typeof(IntPtr), "get_func_ptr"); body.Load (writeState.GetFunctionPtrVariable, getFuncPtrResult, tbaa: module.TbaaAnyPointer); - var placeholder = new MarshalMethodAssemblyIndexValuePlaceholder (method, writeState.AssemblyCacheState); - LlvmIrInstructions.Call call = body.Call ( - writeState.GetFunctionPtrFunction, - arguments: new List { placeholder, method.ClassCacheIndex, nativeCallback.MetadataToken.ToUInt32 (), backingField }, - funcPointer: getFuncPtrResult - ); + List getFunctionPointerArguments; + if (managedMarshalMethodsLookupEnabled) { + (uint assemblyIndex, uint classIndex, uint methodIndex) = codeGenState.ManagedMarshalMethodsLookupInfo.GetIndex (nativeCallback); + getFunctionPointerArguments = new List { assemblyIndex, classIndex, methodIndex, backingField }; + } else { + var placeholder = new MarshalMethodAssemblyIndexValuePlaceholder (method, writeState.AssemblyCacheState); + getFunctionPointerArguments = new List { placeholder, method.ClassCacheIndex, nativeCallback.MetadataToken.ToUInt32 (), backingField }; + } + + LlvmIrInstructions.Call call = body.Call (writeState.GetFunctionPtrFunction, arguments: getFunctionPointerArguments, funcPointer: getFuncPtrResult); LlvmIrLocalVariable cb2 = func.CreateLocalVariable (typeof(IntPtr), "cb2"); body.Load (backingField, cb2, tbaa: module.TbaaAnyPointer); diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/NativeCodeGenState.cs b/src/Xamarin.Android.Build.Tasks/Utilities/NativeCodeGenState.cs index 5a541f2d09c..f89c8a501a1 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/NativeCodeGenState.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/NativeCodeGenState.cs @@ -35,6 +35,8 @@ class NativeCodeGenState public TypeDefinitionCache TypeCache { get; } public bool JniAddNativeMethodRegistrationAttributePresent { get; set; } + public ManagedMarshalMethodsLookupInfo? ManagedMarshalMethodsLookupInfo { get; set; } + public NativeCodeGenState (AndroidTargetArch arch, TypeDefinitionCache tdCache, XAAssemblyResolver resolver, List allJavaTypes, List javaTypesForJCW, MarshalMethodsClassifier? classifier) { TargetArch = arch; diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index 3123c94ff3b..e8256960f9d 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -338,6 +338,9 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. False <_AndroidUseMarshalMethods Condition=" '$(AndroidIncludeDebugSymbols)' == 'True' ">False <_AndroidUseMarshalMethods Condition=" '$(AndroidIncludeDebugSymbols)' != 'True' ">$(AndroidEnableMarshalMethods) + + <_AndroidUseManagedMarshalMethodsLookup Condition=" '$(_AndroidUseManagedMarshalMethodsLookup)' == '' and '$(AndroidEnableMarshalMethods)' == 'True' and '$(_AndroidRuntime)' != 'MonoVM' ">True + <_AndroidUseManagedMarshalMethodsLookup Condition=" '$(_AndroidUseManagedMarshalMethodsLookup)' == '' ">False @@ -1539,6 +1542,7 @@ because xbuild doesn't support framework reference assemblies. SkipJniAddNativeMethodRegistrationAttributeScan="$(_SkipJniAddNativeMethodRegistrationAttributeScan)" CheckedBuild="$(_AndroidCheckedBuild)" EnableMarshalMethods="$(_AndroidUseMarshalMethods)" + EnableManagedMarshalMethodsLookup="$(_AndroidUseManagedMarshalMethodsLookup)" LinkingEnabled="$(_LinkingEnabled)" HaveMultipleRIDs="$(_HaveMultipleRIDs)" IntermediateOutputDirectory="$(IntermediateOutputPath)" @@ -1772,6 +1776,7 @@ because xbuild doesn't support framework reference assemblies. RuntimeConfigBinFilePath="$(_BinaryRuntimeConfigPath)" UseAssemblyStore="$(AndroidUseAssemblyStore)" EnableMarshalMethods="$(_AndroidUseMarshalMethods)" + EnableManagedMarshalMethodsLookup="$(_AndroidUseManagedMarshalMethodsLookup)" CustomBundleConfigFile="$(AndroidBundleConfigurationFile)" > diff --git a/src/native/mono/monodroid/monodroid-glue-internal.hh b/src/native/mono/monodroid/monodroid-glue-internal.hh index f5125cb9a5a..d4a7c9a49b2 100644 --- a/src/native/mono/monodroid/monodroid-glue-internal.hh +++ b/src/native/mono/monodroid/monodroid-glue-internal.hh @@ -102,6 +102,7 @@ namespace xamarin::android::internal bool jniRemappingInUse; bool marshalMethodsEnabled; jobject grefGCUserPeerable; + bool managedMarshalMethodsLookupEnabled; }; using jnienv_initialize_fn = void (*) (JnienvInitializeArgs*); @@ -234,6 +235,8 @@ namespace xamarin::android::internal static void get_function_pointer (uint32_t mono_image_index, uint32_t class_index, uint32_t method_token, void*& target_ptr) noexcept; static void get_function_pointer_at_startup (uint32_t mono_image_index, uint32_t class_token, uint32_t method_token, void*& target_ptr) noexcept; static void get_function_pointer_at_runtime (uint32_t mono_image_index, uint32_t class_token, uint32_t method_token, void*& target_ptr) noexcept; + static get_function_pointer_fn get_managed_marshal_methods_lookup_uco () noexcept; + static void managed_marshal_method_lookup (uint32_t assembly_index, uint32_t class_index, uint32_t method_index, void*& target_ptr) noexcept; #endif // def RELEASE #if defined (DEBUG) diff --git a/src/native/mono/monodroid/monodroid-glue.cc b/src/native/mono/monodroid/monodroid-glue.cc index fac103d595e..a7d20c185c9 100644 --- a/src/native/mono/monodroid/monodroid-glue.cc +++ b/src/native/mono/monodroid/monodroid-glue.cc @@ -694,7 +694,11 @@ MonodroidRuntime::mono_runtime_init ([[maybe_unused]] JNIEnv *env, [[maybe_unuse #if defined (RELEASE) if (application_config.marshal_methods_enabled) { - xamarin_app_init (env, get_function_pointer_at_startup); + if (!application_config.managed_marshal_methods_lookup_enabled) { + xamarin_app_init (env, get_function_pointer_at_startup); + } else { + xamarin_app_init (env, managed_marshal_method_lookup); + } } #endif // def RELEASE && def ANDROID && def NET } @@ -849,6 +853,7 @@ MonodroidRuntime::init_android_runtime (JNIEnv *env, jclass runtimeClass, jobjec init.jniAddNativeMethodRegistrationAttributePresent = application_config.jni_add_native_method_registration_attribute_present ? 1 : 0; init.jniRemappingInUse = application_config.jni_remapping_replacement_type_count > 0 || application_config.jni_remapping_replacement_method_index_entry_count > 0; init.marshalMethodsEnabled = application_config.marshal_methods_enabled; + init.managedMarshalMethodsLookupEnabled = application_config.managed_marshal_methods_lookup_enabled; java_System = RuntimeUtil::get_class_from_runtime_field (env, runtimeClass, "java_lang_System", true); java_System_identityHashCode = env->GetStaticMethodID (java_System, "identityHashCode", "(Ljava/lang/Object;)I"); @@ -1550,7 +1555,11 @@ MonodroidRuntime::Java_mono_android_Runtime_initInternal (JNIEnv *env, jclass kl #if defined (RELEASE) if (application_config.marshal_methods_enabled) { - xamarin_app_init (env, get_function_pointer_at_runtime); + if (!application_config.managed_marshal_methods_lookup_enabled) { + xamarin_app_init (env, get_function_pointer_at_runtime); + } else { + xamarin_app_init (env, managed_marshal_method_lookup); + } } #endif // def RELEASE && def ANDROID && def NET MonodroidState::mark_startup_done (); diff --git a/src/native/mono/monodroid/xamarin-android-app-context.cc b/src/native/mono/monodroid/xamarin-android-app-context.cc index 9ef193ddeb7..b6d3d2a8051 100644 --- a/src/native/mono/monodroid/xamarin-android-app-context.cc +++ b/src/native/mono/monodroid/xamarin-android-app-context.cc @@ -132,3 +132,48 @@ MonodroidRuntime::get_function_pointer_at_runtime (uint32_t mono_image_index, ui { get_function_pointer (mono_image_index, class_index, method_token, target_ptr); } + +get_function_pointer_fn +MonodroidRuntime::get_managed_marshal_methods_lookup_uco () noexcept +{ + if (default_alc == nullptr) { + Helpers::abort_application ("The default assembly load context is not set"sv); + } + + MonoAssembly *mono_android_assembly = Util::monodroid_load_assembly (default_alc, SharedConstants::MONO_ANDROID_ASSEMBLY_NAME.data ()); + MonoImage *mono_android_image = mono_assembly_get_image (mono_android_assembly); + + MonoClass *managed_marshal_methods_lookup_table_class = mono_class_from_name (mono_android_image, "Java.Interop", "ManagedMarshalMethodsLookupTable"); + if (managed_marshal_methods_lookup_table_class == nullptr) { + Helpers::abort_application ("The Java.Interop.ManagedMarshalMethodsLookupTable class could not be found in Mono.Android"sv); + } + + MonoMethod *get_function_pointer_method = mono_class_get_method_from_name (managed_marshal_methods_lookup_table_class, "GetFunctionPointer", 4); + if (get_function_pointer_method == nullptr) { + Helpers::abort_application ("The Java.Interop.ManagedMarshalMethodsLookupTable.GetFunctionPointer method could not be found"sv); + } + + MonoError error; + auto get_function_pointer_uco = reinterpret_cast (mono_method_get_unmanaged_callers_only_ftnptr (get_function_pointer_method, &error)); + + if (error.error_code != MONO_ERROR_NONE) { + const char *message = mono_error_get_message (&error); + Helpers::abort_application ( + message == nullptr ? "Failure to obtain Java.Interop.FunctionPointerResolver.InitializeFunctionPointer UCO method"sv : message + ); + } + + return get_function_pointer_uco; +} + +void +MonodroidRuntime::managed_marshal_method_lookup (uint32_t assembly_index, uint32_t class_index, uint32_t method_index, void*& target_ptr) noexcept +{ + static get_function_pointer_fn get_function_pointer_uco; + + if (get_function_pointer_uco == nullptr) { + get_function_pointer_uco = get_managed_marshal_methods_lookup_uco (); + } + + get_function_pointer_uco (assembly_index, class_index, method_index, target_ptr); +} diff --git a/src/native/mono/xamarin-app-stub/application_dso_stub.cc b/src/native/mono/xamarin-app-stub/application_dso_stub.cc index c026be2b346..0e3b0d01ae0 100644 --- a/src/native/mono/xamarin-app-stub/application_dso_stub.cc +++ b/src/native/mono/xamarin-app-stub/application_dso_stub.cc @@ -67,6 +67,7 @@ const ApplicationConfig application_config = { .jni_remapping_replacement_method_index_entry_count = 2, .mono_components_mask = MonoComponent::None, .android_package_name = android_package_name, + .managed_marshal_methods_lookup_enabled = false, }; const char* const mono_aot_mode_name = "normal"; diff --git a/src/native/mono/xamarin-app-stub/xamarin-app.hh b/src/native/mono/xamarin-app-stub/xamarin-app.hh index 8216cad0848..365df553c7b 100644 --- a/src/native/mono/xamarin-app-stub/xamarin-app.hh +++ b/src/native/mono/xamarin-app-stub/xamarin-app.hh @@ -258,6 +258,7 @@ struct ApplicationConfig uint32_t jni_remapping_replacement_method_index_entry_count; MonoComponent mono_components_mask; const char *android_package_name; + bool managed_marshal_methods_lookup_enabled; }; struct DSOApkEntry From 408d8f6eb4b6d6e0a60cf700a4e82a70bc5da21c Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Tue, 18 Feb 2025 09:00:30 +0100 Subject: [PATCH 02/11] Add install and run test --- .../Tests/InstallAndRunTests.cs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs b/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs index df7e7d9241d..76831ef20b5 100644 --- a/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs +++ b/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs @@ -1332,5 +1332,32 @@ public void NativeAOTSample () throw; } } + + [Test] + public void EnableManagedMarshalMethodsLookup () + { + string [] properties = [ + "Configuration=Release", + "AndroidUseMarshalMethods=true", + "_AndroidUseManagedMarshalMethodsLookup=true", + ]; + var projectDirectory = Path.Combine (XABuildPaths.TopDirectory, "samples", "HelloWorld", "HelloWorld"); + try { + var dotnet = new DotNetCLI (Path.Combine (projectDirectory, "HelloWorld.DotNet.csproj")); + Assert.IsTrue (dotnet.Build (target: "Run", parameters: properties), "`dotnet build -t:Run` should succeed"); + + bool didLaunch = WaitForActivityToStart ("example", "MainActivity", + Path.Combine (projectDirectory, "logcat.log"), 30); + Assert.IsTrue (didLaunch, "Activity should have started."); + } catch { + foreach (var file in Directory.GetFiles (projectDirectory, "*.log", SearchOption.AllDirectories)) { + TestContext.AddTestAttachment (file); + } + foreach (var bl in Directory.GetFiles (projectDirectory, "*.binlog", SearchOption.AllDirectories)) { + TestContext.AddTestAttachment (bl); + } + throw; + } + } } } From 6c039cd5c05b5445225f19410a731da8e69fc225 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Tue, 18 Feb 2025 16:06:20 +0100 Subject: [PATCH 03/11] Fix application config field count --- .../Xamarin.Android.Build.Tests/Utilities/EnvironmentHelper.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/EnvironmentHelper.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/EnvironmentHelper.cs index f151e755b0a..7a86601ce43 100644 --- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/EnvironmentHelper.cs +++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/Utilities/EnvironmentHelper.cs @@ -68,7 +68,7 @@ public sealed class ApplicationConfig public bool managed_marshal_methods_lookup_enabled; } - const uint ApplicationConfigFieldCount = 26; + const uint ApplicationConfigFieldCount = 27; const string ApplicationConfigSymbolName = "application_config"; const string AppEnvironmentVariablesSymbolName = "app_environment_variables"; From 9acc5340f374371b23722d1bc6a2d70b616a7963 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Tue, 18 Feb 2025 16:58:57 +0100 Subject: [PATCH 04/11] Update test --- .../Tests/InstallAndRunTests.cs | 36 ++++++++----------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs b/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs index 76831ef20b5..13b5d2a46c5 100644 --- a/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs +++ b/tests/MSBuildDeviceIntegration/Tests/InstallAndRunTests.cs @@ -1334,30 +1334,22 @@ public void NativeAOTSample () } [Test] - public void EnableManagedMarshalMethodsLookup () + public void AppStartsWithManagedMarshalMethodsLookupEnabled () { - string [] properties = [ - "Configuration=Release", - "AndroidUseMarshalMethods=true", - "_AndroidUseManagedMarshalMethodsLookup=true", - ]; - var projectDirectory = Path.Combine (XABuildPaths.TopDirectory, "samples", "HelloWorld", "HelloWorld"); - try { - var dotnet = new DotNetCLI (Path.Combine (projectDirectory, "HelloWorld.DotNet.csproj")); - Assert.IsTrue (dotnet.Build (target: "Run", parameters: properties), "`dotnet build -t:Run` should succeed"); + var proj = new XamarinAndroidApplicationProject { IsRelease = true }; + proj.SetProperty ("AndroidUseMarshalMethods", "true"); + proj.SetProperty ("_AndroidUseManagedMarshalMethodsLookup", "true"); - bool didLaunch = WaitForActivityToStart ("example", "MainActivity", - Path.Combine (projectDirectory, "logcat.log"), 30); - Assert.IsTrue (didLaunch, "Activity should have started."); - } catch { - foreach (var file in Directory.GetFiles (projectDirectory, "*.log", SearchOption.AllDirectories)) { - TestContext.AddTestAttachment (file); - } - foreach (var bl in Directory.GetFiles (projectDirectory, "*.binlog", SearchOption.AllDirectories)) { - TestContext.AddTestAttachment (bl); - } - throw; - } + using var builder = CreateApkBuilder (); + builder.Save (proj); + + var dotnet = new DotNetCLI (Path.Combine (Root, builder.ProjectDirectory, proj.ProjectFilePath)); + Assert.IsTrue (dotnet.Build (), "`dotnet build` should succeed"); + Assert.IsTrue (dotnet.Run (), "`dotnet run --no-build` should succeed"); + + bool didLaunch = WaitForActivityToStart (proj.PackageName, "MainActivity", + Path.Combine (Root, builder.ProjectDirectory, "logcat.log"), 30); + Assert.IsTrue (didLaunch, "Activity should have started."); } } } From d1df4afdcf2fcb5490b8bd0dc3fb8f33d68f7012 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 19 Feb 2025 07:41:34 +0100 Subject: [PATCH 05/11] Update MSBuild --- .../Xamarin.Android.Common.targets | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets index e8256960f9d..02f09933a4c 100644 --- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets +++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets @@ -339,7 +339,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved. <_AndroidUseMarshalMethods Condition=" '$(AndroidIncludeDebugSymbols)' == 'True' ">False <_AndroidUseMarshalMethods Condition=" '$(AndroidIncludeDebugSymbols)' != 'True' ">$(AndroidEnableMarshalMethods) - <_AndroidUseManagedMarshalMethodsLookup Condition=" '$(_AndroidUseManagedMarshalMethodsLookup)' == '' and '$(AndroidEnableMarshalMethods)' == 'True' and '$(_AndroidRuntime)' != 'MonoVM' ">True + <_AndroidUseManagedMarshalMethodsLookup Condition=" '$(_AndroidUseManagedMarshalMethodsLookup)' == '' and '$(_AndroidUseMarshalMethods)' == 'True' and '$(_AndroidRuntime)' != 'MonoVM' ">True <_AndroidUseManagedMarshalMethodsLookup Condition=" '$(_AndroidUseManagedMarshalMethodsLookup)' == '' ">False @@ -2159,7 +2159,7 @@ because xbuild doesn't support framework reference assemblies. <_ApkOutputPath>$(_BaseZipIntermediate) - + - + - + Date: Wed, 19 Feb 2025 16:39:32 +0100 Subject: [PATCH 06/11] Use unspeakable method name to avoid naming collisions --- .../Utilities/ManagedMarshalMethodsLookupGenerator.cs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ManagedMarshalMethodsLookupGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ManagedMarshalMethodsLookupGenerator.cs index f1d365c9e01..b79b738ba3c 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/ManagedMarshalMethodsLookupGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ManagedMarshalMethodsLookupGenerator.cs @@ -52,12 +52,13 @@ public void Generate (IEnumerable> marshalMethods) MethodDefinition GenerateGetFunctionPointer (ManagedMarshalMethodsLookupInfo.ClassLookupInfo classLookup) { var declaringType = classLookup.DeclaringType; - log.LogDebugMessage ($"Generating `GetFunctionPointer` for {declaringType.FullName} ({classLookup.MethodLookup.Count} UCO methods)"); + log.LogDebugMessage ($"Generating `GetFunctionPointer` for {declaringType.FullName} ({classLookup.MethodLookup.Count} UCO methods)"); var intType = ImportReference (declaringType.Module, typeof (int)); var intPtrType = ImportReference (declaringType.Module, typeof (System.IntPtr)); - var getFunctionPointerMethod = classLookup.GetFunctionPointerMethod = new MethodDefinition ("GetFunctionPointer", MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, intPtrType); + // an "unspeakable" method name is used to avoid conflicts with user-defined methods + var getFunctionPointerMethod = classLookup.GetFunctionPointerMethod = new MethodDefinition ("GetFunctionPointer", MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, intPtrType); getFunctionPointerMethod.DeclaringType = declaringType; declaringType.Methods.Add (getFunctionPointerMethod); @@ -97,7 +98,8 @@ MethodDefinition GenerateGetFunctionPointer (ManagedMarshalMethodsLookupInfo.Cla while (declaringType.IsNested && (declaringType.IsNestedPrivate || declaringType.IsNestedFamily || declaringType.IsNestedFamilyAndAssembly)) { var parentType = declaringType.DeclaringType; - var parentProxyMethod = new MethodDefinition ($"GetFunctionPointer_{declaringType.Name}", MethodAttributes.Assembly | MethodAttributes.Static | MethodAttributes.HideBySig, intPtrType); + // an "unspeakable" method name is used to avoid conflicts with user-defined methods + var parentProxyMethod = new MethodDefinition ($"GetFunctionPointer_{declaringType.Name}", MethodAttributes.Assembly | MethodAttributes.Static | MethodAttributes.HideBySig, intPtrType); parentProxyMethod.DeclaringType = parentType; parentType.Methods.Add (parentProxyMethod); From 12e6a01ce613dbbf5fb552783a7ef3ada185d458 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Fri, 21 Feb 2025 11:01:29 +0100 Subject: [PATCH 07/11] Address feedback --- .../Android.Runtime/JNIEnvInit.cs | 10 ++++- .../ManagedMarshalMethodsLookupTable.cs | 15 +++---- .../ManagedMarshalMethodsLookupGenerator.cs | 4 +- src/native/mono/monodroid/monodroid-glue.cc | 4 +- .../monodroid/xamarin-android-app-context.cc | 45 ------------------- 5 files changed, 18 insertions(+), 60 deletions(-) diff --git a/src/Mono.Android/Android.Runtime/JNIEnvInit.cs b/src/Mono.Android/Android.Runtime/JNIEnvInit.cs index 7e93806c21c..be273dc7ab6 100644 --- a/src/Mono.Android/Android.Runtime/JNIEnvInit.cs +++ b/src/Mono.Android/Android.Runtime/JNIEnvInit.cs @@ -40,7 +40,6 @@ internal struct JnienvInitializeArgs { internal static bool IsRunningOnDesktop; internal static bool jniRemappingInUse; internal static bool MarshalMethodsEnabled; - internal static bool ManagedMarshalMethodsLookupEnabled; internal static bool PropagateExceptions; internal static BoundExceptionType BoundExceptionType; internal static int gref_gc_threshold; @@ -98,7 +97,6 @@ internal static unsafe void Initialize (JnienvInitializeArgs* args) jniRemappingInUse = args->jniRemappingInUse; MarshalMethodsEnabled = args->marshalMethodsEnabled; - ManagedMarshalMethodsLookupEnabled = args->managedMarshalMethodsLookupEnabled; java_class_loader = args->grefLoader; BoundExceptionType = (BoundExceptionType)args->ioExceptionType; @@ -120,9 +118,17 @@ internal static unsafe void Initialize (JnienvInitializeArgs* args) } } + if (args->managedMarshalMethodsLookupEnabled) { + delegate* unmanaged getFunctionPointer = &ManagedMarshalMethodsLookupTable.GetFunctionPointer; + xamarin_app_init (args->env, getFunctionPointer); + } + SetSynchronizationContext (); } + [DllImport ("xamarin-app")] + static extern unsafe void xamarin_app_init (IntPtr env, delegate* unmanaged get_function_pointer); + // NOTE: prevents Android.App.Application static ctor from running [MethodImpl (MethodImplOptions.NoInlining)] static void SetSynchronizationContext () => diff --git a/src/Mono.Android/Java.Interop/ManagedMarshalMethodsLookupTable.cs b/src/Mono.Android/Java.Interop/ManagedMarshalMethodsLookupTable.cs index c621a6dadd4..cd15fb3e337 100644 --- a/src/Mono.Android/Java.Interop/ManagedMarshalMethodsLookupTable.cs +++ b/src/Mono.Android/Java.Interop/ManagedMarshalMethodsLookupTable.cs @@ -10,21 +10,18 @@ namespace Java.Interop; internal class ManagedMarshalMethodsLookupTable { [UnmanagedCallersOnly] - static unsafe void GetFunctionPointer (int assemblyIndex, int classIndex, int methodIndex, IntPtr target) + internal static unsafe void GetFunctionPointer (int assemblyIndex, int classIndex, int methodIndex, IntPtr* target) { - try - { + try { IntPtr result = GetFunctionPointer (assemblyIndex, classIndex, methodIndex); - if (result == IntPtr.Zero || result == (IntPtr)(-1)) - { + if (result == IntPtr.Zero || result == (IntPtr)(-1)) { throw new InvalidOperationException ($"Failed to get function pointer for ({assemblyIndex}, {classIndex}, {methodIndex})"); } - *(IntPtr*)target = result; - } - catch (Exception ex) - { + *target = result; + } catch (Exception ex) { AndroidEnvironment.UnhandledException (ex); + AndroidEnvironment.FailFast ("GetFunctionPointer failed: should not be reached"); } } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ManagedMarshalMethodsLookupGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ManagedMarshalMethodsLookupGenerator.cs index b79b738ba3c..17ed80a69cd 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/ManagedMarshalMethodsLookupGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ManagedMarshalMethodsLookupGenerator.cs @@ -144,7 +144,7 @@ MethodDefinition GenerateGetFunctionPointerPerAssembly (ManagedMarshalMethodsLoo uint classIndex = 0; foreach (var classInfo in assemblyInfo.ClassLookup.Values) { classInfo.Index = classIndex++; - targets [classInfo.Index] = Instruction.Create (OpCodes.Call, module.Import (classInfo.GetFunctionPointerMethod)); + targets [classInfo.Index] = Instruction.Create (OpCodes.Call, module.ImportReference (classInfo.GetFunctionPointerMethod)); } var il = getFunctionPointerMethod.Body.GetILProcessor (); @@ -186,7 +186,7 @@ void GenerateGlobalGetFunctionPointerMethod () uint assemblyIndex = 0; foreach (var assemblyInfo in managedMarshalMethodsLookupInfo.AssemblyLookup.Values) { assemblyInfo.Index = assemblyIndex++; - targets [assemblyInfo.Index] = Instruction.Create (OpCodes.Call, module.Import (assemblyInfo.GetFunctionPointerMethod)); + targets [assemblyInfo.Index] = Instruction.Create (OpCodes.Call, module.ImportReference (assemblyInfo.GetFunctionPointerMethod)); } var il = getFunctionPointerMethod.Body.GetILProcessor (); diff --git a/src/native/mono/monodroid/monodroid-glue.cc b/src/native/mono/monodroid/monodroid-glue.cc index a7d20c185c9..adf962e481f 100644 --- a/src/native/mono/monodroid/monodroid-glue.cc +++ b/src/native/mono/monodroid/monodroid-glue.cc @@ -697,7 +697,7 @@ MonodroidRuntime::mono_runtime_init ([[maybe_unused]] JNIEnv *env, [[maybe_unuse if (!application_config.managed_marshal_methods_lookup_enabled) { xamarin_app_init (env, get_function_pointer_at_startup); } else { - xamarin_app_init (env, managed_marshal_method_lookup); + // xamarin_app_init is called via p/invoke from JNIEnvInit.Initialize } } #endif // def RELEASE && def ANDROID && def NET @@ -1558,7 +1558,7 @@ MonodroidRuntime::Java_mono_android_Runtime_initInternal (JNIEnv *env, jclass kl if (!application_config.managed_marshal_methods_lookup_enabled) { xamarin_app_init (env, get_function_pointer_at_runtime); } else { - xamarin_app_init (env, managed_marshal_method_lookup); + // xamarin_app_init is called via p/invoke from JNIEnvInit.Initialize } } #endif // def RELEASE && def ANDROID && def NET diff --git a/src/native/mono/monodroid/xamarin-android-app-context.cc b/src/native/mono/monodroid/xamarin-android-app-context.cc index b6d3d2a8051..9ef193ddeb7 100644 --- a/src/native/mono/monodroid/xamarin-android-app-context.cc +++ b/src/native/mono/monodroid/xamarin-android-app-context.cc @@ -132,48 +132,3 @@ MonodroidRuntime::get_function_pointer_at_runtime (uint32_t mono_image_index, ui { get_function_pointer (mono_image_index, class_index, method_token, target_ptr); } - -get_function_pointer_fn -MonodroidRuntime::get_managed_marshal_methods_lookup_uco () noexcept -{ - if (default_alc == nullptr) { - Helpers::abort_application ("The default assembly load context is not set"sv); - } - - MonoAssembly *mono_android_assembly = Util::monodroid_load_assembly (default_alc, SharedConstants::MONO_ANDROID_ASSEMBLY_NAME.data ()); - MonoImage *mono_android_image = mono_assembly_get_image (mono_android_assembly); - - MonoClass *managed_marshal_methods_lookup_table_class = mono_class_from_name (mono_android_image, "Java.Interop", "ManagedMarshalMethodsLookupTable"); - if (managed_marshal_methods_lookup_table_class == nullptr) { - Helpers::abort_application ("The Java.Interop.ManagedMarshalMethodsLookupTable class could not be found in Mono.Android"sv); - } - - MonoMethod *get_function_pointer_method = mono_class_get_method_from_name (managed_marshal_methods_lookup_table_class, "GetFunctionPointer", 4); - if (get_function_pointer_method == nullptr) { - Helpers::abort_application ("The Java.Interop.ManagedMarshalMethodsLookupTable.GetFunctionPointer method could not be found"sv); - } - - MonoError error; - auto get_function_pointer_uco = reinterpret_cast (mono_method_get_unmanaged_callers_only_ftnptr (get_function_pointer_method, &error)); - - if (error.error_code != MONO_ERROR_NONE) { - const char *message = mono_error_get_message (&error); - Helpers::abort_application ( - message == nullptr ? "Failure to obtain Java.Interop.FunctionPointerResolver.InitializeFunctionPointer UCO method"sv : message - ); - } - - return get_function_pointer_uco; -} - -void -MonodroidRuntime::managed_marshal_method_lookup (uint32_t assembly_index, uint32_t class_index, uint32_t method_index, void*& target_ptr) noexcept -{ - static get_function_pointer_fn get_function_pointer_uco; - - if (get_function_pointer_uco == nullptr) { - get_function_pointer_uco = get_managed_marshal_methods_lookup_uco (); - } - - get_function_pointer_uco (assembly_index, class_index, method_index, target_ptr); -} From 8f080e10d0cc55c88d32dabd56e95aa3eac708b1 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Fri, 21 Feb 2025 11:34:47 +0100 Subject: [PATCH 08/11] Remove trimmer directive --- src/Microsoft.Android.Sdk.ILLink/PreserveLists/Mono.Android.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/src/Microsoft.Android.Sdk.ILLink/PreserveLists/Mono.Android.xml b/src/Microsoft.Android.Sdk.ILLink/PreserveLists/Mono.Android.xml index aee7513a8e2..aa0a341e0bb 100644 --- a/src/Microsoft.Android.Sdk.ILLink/PreserveLists/Mono.Android.xml +++ b/src/Microsoft.Android.Sdk.ILLink/PreserveLists/Mono.Android.xml @@ -34,7 +34,6 @@ --> - From 25580eb1ad0463f3eeeb94529c011e3d26eebdb4 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Fri, 21 Feb 2025 13:07:04 +0100 Subject: [PATCH 09/11] Remove method declarations from header file --- src/native/mono/monodroid/monodroid-glue-internal.hh | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/native/mono/monodroid/monodroid-glue-internal.hh b/src/native/mono/monodroid/monodroid-glue-internal.hh index d4a7c9a49b2..9dcefe36759 100644 --- a/src/native/mono/monodroid/monodroid-glue-internal.hh +++ b/src/native/mono/monodroid/monodroid-glue-internal.hh @@ -235,8 +235,6 @@ namespace xamarin::android::internal static void get_function_pointer (uint32_t mono_image_index, uint32_t class_index, uint32_t method_token, void*& target_ptr) noexcept; static void get_function_pointer_at_startup (uint32_t mono_image_index, uint32_t class_token, uint32_t method_token, void*& target_ptr) noexcept; static void get_function_pointer_at_runtime (uint32_t mono_image_index, uint32_t class_token, uint32_t method_token, void*& target_ptr) noexcept; - static get_function_pointer_fn get_managed_marshal_methods_lookup_uco () noexcept; - static void managed_marshal_method_lookup (uint32_t assembly_index, uint32_t class_index, uint32_t method_index, void*& target_ptr) noexcept; #endif // def RELEASE #if defined (DEBUG) From 4f5a2343a8c11495bd069a5620e2fc7b3739b5d0 Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Sat, 22 Feb 2025 12:44:26 +0100 Subject: [PATCH 10/11] Use get_function_pointer placeholder before the xamarin_app_init method is called via p/invoke --- src/native/mono/monodroid/monodroid-glue-internal.hh | 1 + src/native/mono/monodroid/monodroid-glue.cc | 4 +++- src/native/mono/monodroid/xamarin-android-app-context.cc | 6 ++++++ 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/src/native/mono/monodroid/monodroid-glue-internal.hh b/src/native/mono/monodroid/monodroid-glue-internal.hh index 9dcefe36759..c956f6956a3 100644 --- a/src/native/mono/monodroid/monodroid-glue-internal.hh +++ b/src/native/mono/monodroid/monodroid-glue-internal.hh @@ -235,6 +235,7 @@ namespace xamarin::android::internal static void get_function_pointer (uint32_t mono_image_index, uint32_t class_index, uint32_t method_token, void*& target_ptr) noexcept; static void get_function_pointer_at_startup (uint32_t mono_image_index, uint32_t class_token, uint32_t method_token, void*& target_ptr) noexcept; static void get_function_pointer_at_runtime (uint32_t mono_image_index, uint32_t class_token, uint32_t method_token, void*& target_ptr) noexcept; + static void get_function_pointer_placeholder (uint32_t mono_image_index, uint32_t class_token, uint32_t method_token, void*& target_ptr) noexcept; #endif // def RELEASE #if defined (DEBUG) diff --git a/src/native/mono/monodroid/monodroid-glue.cc b/src/native/mono/monodroid/monodroid-glue.cc index adf962e481f..2f59342c7bd 100644 --- a/src/native/mono/monodroid/monodroid-glue.cc +++ b/src/native/mono/monodroid/monodroid-glue.cc @@ -697,7 +697,9 @@ MonodroidRuntime::mono_runtime_init ([[maybe_unused]] JNIEnv *env, [[maybe_unuse if (!application_config.managed_marshal_methods_lookup_enabled) { xamarin_app_init (env, get_function_pointer_at_startup); } else { - // xamarin_app_init is called via p/invoke from JNIEnvInit.Initialize + // xamarin_app_init will be called with the actual get_function_pointer method + // via p/invoke from JNIEnvInit.Initialize + xamarin_app_init (env, get_function_pointer_placeholder); } } #endif // def RELEASE && def ANDROID && def NET diff --git a/src/native/mono/monodroid/xamarin-android-app-context.cc b/src/native/mono/monodroid/xamarin-android-app-context.cc index 9ef193ddeb7..6da594a16cc 100644 --- a/src/native/mono/monodroid/xamarin-android-app-context.cc +++ b/src/native/mono/monodroid/xamarin-android-app-context.cc @@ -132,3 +132,9 @@ MonodroidRuntime::get_function_pointer_at_runtime (uint32_t mono_image_index, ui { get_function_pointer (mono_image_index, class_index, method_token, target_ptr); } + +void +MonodroidRuntime::get_function_pointer_placeholder (uint32_t mono_image_index, uint32_t class_index, uint32_t method_token, void*& target_ptr) noexcept +{ + Helpers::abort_application ("The callback for get_function_pointer has not been initialized yet. Failed to resolve ({}, {}, {})."sv, mono_image_index, class_index, method_token); +} From 8e12d37359fbfc2edd49cc737f3a1abe04098b4a Mon Sep 17 00:00:00 2001 From: Simon Rozsival Date: Wed, 26 Feb 2025 18:08:46 +0100 Subject: [PATCH 11/11] Cleanup --- src/native/mono/monodroid/monodroid-glue.cc | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/native/mono/monodroid/monodroid-glue.cc b/src/native/mono/monodroid/monodroid-glue.cc index c962128b065..59ce50c7bec 100644 --- a/src/native/mono/monodroid/monodroid-glue.cc +++ b/src/native/mono/monodroid/monodroid-glue.cc @@ -1557,12 +1557,9 @@ MonodroidRuntime::Java_mono_android_Runtime_initInternal (JNIEnv *env, jclass kl } #if defined (RELEASE) - if (application_config.marshal_methods_enabled) { - if (!application_config.managed_marshal_methods_lookup_enabled) { - xamarin_app_init (env, get_function_pointer_at_runtime); - } else { - // xamarin_app_init is called via p/invoke from JNIEnvInit.Initialize - } + // when managed marshal methods lookups are enabled, xamarin_app_init is called via p/invoke from JNIEnvInit.Initialize + if (application_config.marshal_methods_enabled && !application_config.managed_marshal_methods_lookup_enabled) { + xamarin_app_init (env, get_function_pointer_at_runtime); } #endif // def RELEASE && def ANDROID && def NET MonodroidState::mark_startup_done ();