diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs index c6b76468af3..12d163a2564 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs @@ -126,21 +126,28 @@ void Run (DirectoryAssemblyResolver res) bool haveMonoAndroid = false; var allTypemapAssemblies = new HashSet (StringComparer.OrdinalIgnoreCase); var userAssemblies = new Dictionary (StringComparer.OrdinalIgnoreCase); + var tokenIdCollection = new NativeRuntimeTokenIdCollection (Log); foreach (var assembly in ResolvedAssemblies) { bool value; + string fileName = Path.GetFileName (assembly.ItemSpec); + + if (String.Compare ("Java.Interop.dll", fileName, StringComparison.CurrentCultureIgnoreCase) == 0) { + tokenIdCollection.ProcessJavaInterop (assembly.ItemSpec); + } + if (bool.TryParse (assembly.GetMetadata (AndroidSkipJavaStubGeneration), out value) && value) { Log.LogDebugMessage ($"Skipping Java Stub Generation for {assembly.ItemSpec}"); continue; } bool addAssembly = false; - string fileName = Path.GetFileName (assembly.ItemSpec); if (!hasExportReference && String.Compare ("Mono.Android.Export.dll", fileName, StringComparison.OrdinalIgnoreCase) == 0) { hasExportReference = true; addAssembly = true; } else if (!haveMonoAndroid && String.Compare ("Mono.Android.dll", fileName, StringComparison.OrdinalIgnoreCase) == 0) { haveMonoAndroid = true; addAssembly = true; + tokenIdCollection.ProcessMonoAndroid (assembly.ItemSpec); } else if (MonoAndroidHelper.FrameworkAssembliesToTreatAsUserAssemblies.Contains (fileName)) { if (!bool.TryParse (assembly.GetMetadata (AndroidSkipJavaStubGeneration), out value) || !value) { string name = Path.GetFileNameWithoutExtension (fileName); @@ -180,7 +187,7 @@ void Run (DirectoryAssemblyResolver res) // Step 2 - Generate type maps // Type mappings need to use all the assemblies, always. - WriteTypeMappings (allJavaTypes, cache); + WriteTypeMappings (allJavaTypes, tokenIdCollection, cache); var javaTypes = new List (); foreach (TypeDefinition td in allJavaTypes) { @@ -423,11 +430,12 @@ void SaveResource (string resource, string filename, string destDir, Func types, TypeDefinitionCache cache) + void WriteTypeMappings (List types, NativeRuntimeTokenIdCollection tokenIdCollection, TypeDefinitionCache cache) { var tmg = new TypeMapGenerator ((string message) => Log.LogDebugMessage (message), SupportedAbis); if (!tmg.Generate (Debug, SkipJniAddNativeMethodRegistrationAttributeScan, types, cache, TypemapOutputDirectory, GenerateNativeAssembly, out ApplicationConfigTaskState appConfState)) throw new XamarinAndroidException (4308, Properties.Resources.XA4308); + appConfState.TokenIdCollection = tokenIdCollection; GeneratedBinaryTypeMaps = tmg.GeneratedBinaryTypeMaps.ToArray (); BuildEngine4.RegisterTaskObjectAssemblyLocal (ApplicationConfigTaskState.RegisterTaskObjectKey, appConfState, RegisteredTaskObjectLifetime.Build); } diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs index 054ced26455..9b2a73edfb1 100644 --- a/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs +++ b/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs @@ -282,6 +282,7 @@ void AddEnvironment () InstantRunEnabled = InstantRunEnabled, JniAddNativeMethodRegistrationAttributePresent = appConfState != null ? appConfState.JniAddNativeMethodRegistrationAttributePresent : false, HaveRuntimeConfigBlob = haveRuntimeConfigBlob, + TokenIdCollection = appConfState?.TokenIdCollection ?? new NativeRuntimeTokenIdCollection (Log), }; using (var sw = MemoryStreamPool.Shared.CreateStreamWriter ()) { diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs index 0b7f9009180..b39398ee4c1 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs @@ -23,6 +23,7 @@ class ApplicationConfigNativeAssemblyGenerator : NativeAssemblyGenerator public bool InstantRunEnabled { get; set; } public bool JniAddNativeMethodRegistrationAttributePresent { get; set; } public bool HaveRuntimeConfigBlob { get; set; } + public NativeRuntimeTokenIdCollection TokenIdCollection { get; set; } public PackageNamingPolicy PackageNamingPolicy { get; set; } @@ -92,6 +93,86 @@ protected override void WriteSymbols (StreamWriter output) return size; }); + WriteDataSection (output, "managed_token_ids"); + WriteSymbol (output, "managed_token_ids", TargetProvider.GetStructureAlignment (true), packed: false, isGlobal: true, alwaysWriteSize: true, structureWriter: () => { + // Order of fields and their types must correspond *exactly* to that in + // src/monodroid/jni/xamarin-app.h ManagedTokenIds structure + + WriteCommentLine (output, "android_runtime_jnienv"); + uint size = WriteData (output, TokenIdCollection.AndroidRuntimeJnienv); + + WriteCommentLine (output, "android_runtime_jnienv_initialize"); + size += WriteData (output, TokenIdCollection.AndroidRuntimeJnienvInitialize); + + WriteCommentLine (output, "android_runtime_jnienv_registerjninatives"); + size += WriteData (output, TokenIdCollection.AndroidRuntimeJnienvRegisterJniNatives); + + WriteCommentLine (output, "android_runtime_jnienv_bridgeprocessing"); + size += WriteData (output, TokenIdCollection.AndroidRuntimeJnienvBridgeProcessing); + + WriteCommentLine (output, "java_lang_object"); + size += WriteData (output, TokenIdCollection.JavaLangObject); + + WriteCommentLine (output, "java_lang_object_handle"); + size += WriteData (output, TokenIdCollection.JavaLangObjectHandle); + + WriteCommentLine (output, "java_lang_object_handle_type"); + size += WriteData (output, TokenIdCollection.JavaLangObjectHandleType); + + WriteCommentLine (output, "java_lang_object_refs_added"); + size += WriteData (output, TokenIdCollection.JavaLangObjectRefsAdded); + + WriteCommentLine (output, "java_lang_object_weak_handle"); + size += WriteData (output, TokenIdCollection.JavaLangObjectWeakHandle); + + WriteCommentLine (output, "java_lang_throwable"); + size += WriteData (output, TokenIdCollection.JavaLangThrowable); + + WriteCommentLine (output, "java_lang_throwable_handle"); + size += WriteData (output, TokenIdCollection.JavaLangThrowableHandle); + + WriteCommentLine (output, "java_lang_throwable_handle_type"); + size += WriteData (output, TokenIdCollection.JavaLangThrowableHandleType); + + WriteCommentLine (output, "java_lang_throwable_refs_added"); + size += WriteData (output, TokenIdCollection.JavaLangThrowableRefsAdded); + + WriteCommentLine (output, "java_lang_throwable_weak_handle"); + size += WriteData (output, TokenIdCollection.JavaLangThrowableWeakHandle); + + WriteCommentLine (output, "java_interop_javaobject"); + size += WriteData (output, TokenIdCollection.JavaInteropJavaObject); + + WriteCommentLine (output, "java_interop_javaobject_handle"); + size += WriteData (output, TokenIdCollection.JavaInteropJavaObjectHandle); + + WriteCommentLine (output, "java_interop_javaobject_handle_type"); + size += WriteData (output, TokenIdCollection.JavaInteropJavaObjectHandleType); + + WriteCommentLine (output, "java_interop_javaobject_refs_added"); + size += WriteData (output, TokenIdCollection.JavaInteropJavaObjectRefsAdded); + + WriteCommentLine (output, "java_interop_javaobject_weak_handle"); + size += WriteData (output, TokenIdCollection.JavaInteropJavaObjectWeakHandle); + + WriteCommentLine (output, "java_interop_javaexception"); + size += WriteData (output, TokenIdCollection.JavaInteropJavaException); + + WriteCommentLine (output, "java_interop_javaexception_handle"); + size += WriteData (output, TokenIdCollection.JavaInteropJavaExceptionHandle); + + WriteCommentLine (output, "java_interop_javaexception_handle_type"); + size += WriteData (output, TokenIdCollection.JavaInteropJavaExceptionHandleType); + + WriteCommentLine (output, "java_interop_javaexception_refs_added"); + size += WriteData (output, TokenIdCollection.JavaInteropJavaExceptionRefsAdded); + + WriteCommentLine (output, "java_interop_javaexception_weak_handle"); + size += WriteData (output, TokenIdCollection.JavaInteropJavaExceptionWeakHandle); + + return size; + }); + stringLabel = GetStringLabel (); WriteData (output, MonoAOTMode ?? String.Empty, stringLabel); WriteDataSection (output, "mono_aot_mode_name"); diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigTaskState.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigTaskState.cs index 5cdd3a6e6a2..6cecebe48a1 100644 --- a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigTaskState.cs +++ b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigTaskState.cs @@ -5,5 +5,6 @@ class ApplicationConfigTaskState public const string RegisterTaskObjectKey = "Xamarin.Android.Tasks.ApplicationConfigTaskState"; public bool JniAddNativeMethodRegistrationAttributePresent { get; set; } = false; + public NativeRuntimeTokenIdCollection TokenIdCollection { get; set; } = default; } } diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/NativeRuntimeTokenIdCollection.cs b/src/Xamarin.Android.Build.Tasks/Utilities/NativeRuntimeTokenIdCollection.cs new file mode 100644 index 00000000000..4ad8708ba0a --- /dev/null +++ b/src/Xamarin.Android.Build.Tasks/Utilities/NativeRuntimeTokenIdCollection.cs @@ -0,0 +1,368 @@ +using System; +using System.Collections.Generic; +using System.IO; + +using Microsoft.Android.Build.Tasks; +using Microsoft.Build.Utilities; + +using Mono.Cecil; + +namespace Xamarin.Android.Tasks +{ + class NativeRuntimeTokenIdCollection + { + struct ParameterValidator + { + public string FullTypeName { get; } + public Func? IsValid { get; } + + public ParameterValidator (string fullTypeName, Func? validator = null) + { + if (fullTypeName.Length == 0) { + throw new ArgumentException ("must not be an empty string", nameof (fullTypeName)); + } + + FullTypeName = fullTypeName; + IsValid = validator; + } + } + + TaskLoggingHelper log; + + public uint AndroidRuntimeJnienv { get; private set; } = 0; + public uint AndroidRuntimeJnienvInitialize { get; private set; } = 0; + public uint AndroidRuntimeJnienvRegisterJniNatives { get; private set; } = 0; + public uint AndroidRuntimeJnienvBridgeProcessing { get; private set; } = 0; + + public uint JavaLangObject { get; private set; } = 0; + public uint JavaLangObjectHandle { get; private set; } = 0; + public uint JavaLangObjectHandleType { get; private set; } = 0; + public uint JavaLangObjectRefsAdded { get; private set; } = 0; + public uint JavaLangObjectWeakHandle { get; private set; } = 0; + + public uint JavaLangThrowable { get; private set; } = 0; + public uint JavaLangThrowableHandle { get; private set; } = 0; + public uint JavaLangThrowableHandleType { get; private set; } = 0; + public uint JavaLangThrowableRefsAdded { get; private set; } = 0; + public uint JavaLangThrowableWeakHandle { get; private set; } = 0; + + public uint JavaInteropJavaObject { get; private set; } = 0; + public uint JavaInteropJavaObjectHandle { get; private set; } = 0; + public uint JavaInteropJavaObjectHandleType { get; private set; } = 0; + public uint JavaInteropJavaObjectRefsAdded { get; private set; } = 0; + public uint JavaInteropJavaObjectWeakHandle { get; private set; } = 0; + + public uint JavaInteropJavaException { get; private set; } = 0; + public uint JavaInteropJavaExceptionHandle { get; private set; } = 0; + public uint JavaInteropJavaExceptionHandleType { get; private set; } = 0; + public uint JavaInteropJavaExceptionRefsAdded { get; private set; } = 0; + public uint JavaInteropJavaExceptionWeakHandle { get; private set; } = 0; + + public NativeRuntimeTokenIdCollection (TaskLoggingHelper log) + { + this.log = log; + } + + public void ProcessMonoAndroid (string fullPath) + { + AssemblyDefinition asm = AssemblyDefinition.ReadAssembly (fullPath); + + TypeDefinition? androidRuntimeJNIEnv = null; + TypeDefinition? javaLangObject = null; + TypeDefinition? javaLangThrowable = null; + + foreach (ModuleDefinition module in asm.Modules) { + if (!module.HasTypes) { + continue; + } + + foreach (TypeDefinition type in module.Types) { + if (IsNeededClass ("Android.Runtime", "JNIEnv", type, ref androidRuntimeJNIEnv)) { + continue; + } + + if (IsNeededClass ("Java.Lang", "Object", type, ref javaLangObject)) { + continue; + } + + if (IsNeededClass ("Java.Lang", "Throwable", type, ref javaLangThrowable)) { + continue; + } + + if (HaveAllClasses ()) { + break; + } + } + + if (HaveAllClasses ()) { + break; + } + } + + if (!HaveAllClasses ()) { + throw new InvalidOperationException ($"Couldn't find all required classes in {fullPath}"); + } + + AndroidRuntimeJnienv = EnsureValidTokenId (androidRuntimeJNIEnv); + JavaLangObject = EnsureValidTokenId (javaLangObject); + JavaLangThrowable = EnsureValidTokenId (javaLangThrowable); + + var initializeParams = new List { + new ParameterValidator ("Android.Runtime.JnienvInitializeArgs*", (MethodDefinition m, ParameterDefinition p) => { + if (p.ParameterType.IsPointer) { + return true; + } + + LogInvalidParameterInfo (m, p, "must be a pointer"); + return false; + }), + }; + AndroidRuntimeJnienvInitialize = FindMethodTokenId (androidRuntimeJNIEnv, "Initialize", "System.Void", true, initializeParams); + + var registerJniNativesParams = new List { + new ParameterValidator ("System.IntPtr"), // typeName_ptr + new ParameterValidator ("System.Int32"), // typeName_len + new ParameterValidator ("System.IntPtr"), // jniClass + new ParameterValidator ("System.IntPtr"), // methods_ptr + new ParameterValidator ("System.Int32"), // methods_len + }; + AndroidRuntimeJnienvRegisterJniNatives = FindMethodTokenId (androidRuntimeJNIEnv, "RegisterJniNatives", "System.Void", true, registerJniNativesParams); + AndroidRuntimeJnienvBridgeProcessing = FindFieldTokenId (androidRuntimeJNIEnv, "BridgeProcessing", "System.Boolean modreq(System.Runtime.CompilerServices.IsVolatile)", true); + + const string handleTypeType = "Android.Runtime.JObjectRefType"; + (JavaLangObjectHandle, JavaLangObjectHandleType, JavaLangObjectRefsAdded, JavaLangObjectWeakHandle) = FindOsBridgeFields (javaLangObject, handleTypeType); + (JavaLangThrowableHandle, JavaLangThrowableHandleType, JavaLangThrowableRefsAdded, JavaLangThrowableWeakHandle) = FindOsBridgeFields (javaLangThrowable, handleTypeType); + + bool HaveAllClasses () + { + return androidRuntimeJNIEnv != null && javaLangObject != null && javaLangThrowable != null; + } + } + + public void ProcessJavaInterop (string fullPath) + { + AssemblyDefinition asm = AssemblyDefinition.ReadAssembly (fullPath); + + TypeDefinition? javaInteropJavaObject = null; + TypeDefinition? javaInteropJavaException = null; + + foreach (ModuleDefinition module in asm.Modules) { + if (!module.HasTypes) { + continue; + } + + foreach (TypeDefinition type in module.Types) { + if (IsNeededClass ("Java.Interop", "JavaObject", type, ref javaInteropJavaObject)) { + continue; + } + + if (IsNeededClass ("Java.Interop", "JavaException", type, ref javaInteropJavaException)) { + continue; + } + + if (HaveAllClasses ()) { + break; + } + } + + if (HaveAllClasses ()) { + break; + } + } + + if (!HaveAllClasses ()) { + throw new InvalidOperationException ($"Couldn't find all required classes in {fullPath}"); + } + + JavaInteropJavaObject = EnsureValidTokenId (javaInteropJavaObject); + JavaInteropJavaException = EnsureValidTokenId (javaInteropJavaException); + + const string handleTypeType = "Java.Interop.JniObjectReferenceType"; + (JavaInteropJavaObjectHandle, JavaInteropJavaObjectHandleType, JavaInteropJavaObjectRefsAdded, JavaInteropJavaObjectWeakHandle) = FindOsBridgeFields (javaInteropJavaObject, handleTypeType); + (JavaInteropJavaExceptionHandle, JavaInteropJavaExceptionHandleType, JavaInteropJavaExceptionRefsAdded, JavaInteropJavaExceptionWeakHandle) = FindOsBridgeFields (javaInteropJavaException, handleTypeType); + + bool HaveAllClasses () + { + return javaInteropJavaObject != null && javaInteropJavaException != null; + } + } + + (uint handle, uint handleType, uint refsAdded, uint weakHandle) FindOsBridgeFields (TypeDefinition type, string handleTypeType) + { + return ( + FindFieldTokenId (type, "handle", "System.IntPtr", false), + FindFieldTokenId (type, "handle_type", handleTypeType, false), + FindFieldTokenId (type, "refs_added", "System.Int32", false), + FindFieldTokenId (type, "weak_handle", "System.IntPtr", false) + ); + } + + void LogInvalidParameterInfo (MethodDefinition m, ParameterDefinition p, string message) + { + log.LogDebugMessage ($"Method '{m.FullName}', parameter {p.Index} ({p.Name}): {message}"); + } + + uint FindFieldTokenId (TypeDefinition type, string fieldName, string expectedType, bool shouldBeStatic) + { + FieldDefinition? field = null; + foreach (FieldDefinition f in type.Fields) { + if (String.Compare (fieldName, f.Name, StringComparison.Ordinal) != 0) { + continue; + } + + if (String.Compare (expectedType, f.FieldType.FullName, StringComparison.Ordinal) != 0) { + log.LogDebugMessage ($"Field '{f.FullName}' has incorrect type '{f.FieldType.FullName}', expected '{expectedType}'"); + break; + } + + if (f.IsStatic != shouldBeStatic) { + log.LogDebugMessage ($"Field '{f.FullName}' should be static"); + break; + } + + field = f; + break; + } + + if (field == null) { + throw new InvalidOperationException ($"Failed to find required '{fieldName}' field in '{type.FullName}'"); + } + + return EnsureValidTokenId (field); + } + + uint FindMethodTokenId (TypeDefinition type, string methodName, string expectedReturnType, bool shouldBeStatic, List? parameters = null) + { + List methods = GetMethods (type, methodName); + + int expectedParameterCount = parameters == null ? 0 : parameters.Count; + MethodDefinition? method = null; + foreach (MethodDefinition m in methods) { + log.LogDebugMessage ($"Checking method {m.FullName}"); + if (!MethodMeetsBasicRequirements (m, shouldBeStatic, expectedParameterCount, expectedReturnType)) { + continue; + } + + if (expectedParameterCount == 0) { + break; + } + + bool allParamsValid = true; + for (int i = 0; i < expectedParameterCount; i++) { + ParameterDefinition p = m.Parameters[i]; + ParameterValidator v = parameters[i]; + + if (String.Compare (p.ParameterType.FullName, v.FullTypeName, StringComparison.Ordinal) != 0) { + log.LogDebugMessage ($"Parameter {i} of method '{m.FullName}' has incorrect type '{p.ParameterType.FullName}', expected '{v.FullTypeName}'"); + allParamsValid = false; + break; + } + + if (v.IsValid == null) { + continue; + } + + if (!v.IsValid (m, p)) { + allParamsValid = false; + break; + } + } + + if (!allParamsValid) { + continue; + } + + method = m; + break; + } + + if (method == null) { + throw new InvalidOperationException ($"Failed to find required '{methodName}' method overload in '{type.FullName}'"); + } + + return EnsureValidTokenId (method); + } + + bool MethodMeetsBasicRequirements (MethodDefinition method, bool shouldBeStatic, int expectedParameterCount, string returnTypeName) + { + if (method.IsAbstract) { + return LogFailure ("cannot be abstract"); + } + + if (expectedParameterCount > 0 && !method.HasParameters) { + return LogFailure ($"does not have parameters, expected {expectedParameterCount}"); + } + + if (method.Parameters.Count != expectedParameterCount) { + return LogFailure ($"does not have correct number of parameters, expected {expectedParameterCount} but found {method.Parameters.Count}"); + } + + if (method.IsStatic != shouldBeStatic) { + string not = shouldBeStatic ? String.Empty : " not"; + return LogFailure ($"should{not} be static"); + } + + if (String.Compare (method.ReturnType.FullName, returnTypeName, StringComparison.Ordinal) != 0) { + return LogFailure ($"return type is '{method.ReturnType.FullName}', expected '{returnTypeName}'"); + } + + return true; + + bool LogFailure (string reason) + { + log.LogDebugMessage ($"Method '{method.FullName}' {reason}"); + return false; + } + } + + bool IsNeededClass (string ns, string name, TypeDefinition type, ref TypeDefinition? storedType) + { + if (storedType != null || !type.IsClass) { + return false; + } + + if (String.Compare (ns, type.Namespace, StringComparison.Ordinal) != 0 || String.Compare (name, type.Name, StringComparison.Ordinal) != 0) { + return false; + } + + storedType = type; + return true; + } + + List GetMethods (TypeDefinition type, string name) + { + if (!type.HasMethods) { + ThrowNoMethod (); + } + + var ret = new List (); + foreach (MethodDefinition method in type.Methods) { + if (method.IsAbstract || String.Compare (method.Name, name, StringComparison.Ordinal) != 0) { + continue; + } + + ret.Add (method); + } + + if (ret.Count == 0) { + ThrowNoMethod (); + } + + return ret; + + void ThrowNoMethod () + { + throw new InvalidOperationException ($"'{type.FullName}' does not contain the required method '{name}'"); + } + } + + uint EnsureValidTokenId (MemberReference member) + { + uint ret = member.MetadataToken.ToUInt32 (); + if (ret == 0) { + throw new InvalidOperationException ($"Member {member.FullName} doesn't have a valid token ID"); + } + return ret; + } + } +} diff --git a/src/monodroid/jni/application_dso_stub.cc b/src/monodroid/jni/application_dso_stub.cc index 0173b9cdf6b..32ebe8142a4 100644 --- a/src/monodroid/jni/application_dso_stub.cc +++ b/src/monodroid/jni/application_dso_stub.cc @@ -29,27 +29,69 @@ const TypeMapModule map_modules[] = {}; const TypeMapJava map_java[] = {}; #endif +// The pragmas will go away once we switch to C++20, where the designator becomes part of the language standard. Both +// gcc and clang support it now as an extension, though. +#if defined (__clang__) +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wc99-designator" +#endif + CompressedAssemblies compressed_assemblies = { - /*.count = */ 0, - /*.descriptors = */ nullptr, + .count = 0, + .descriptors = nullptr, }; ApplicationConfig application_config = { - /*.uses_mono_llvm =*/ false, - /*.uses_mono_aot =*/ false, - /*.uses_assembly_preload =*/ false, - /*.is_a_bundled_app =*/ false, - /*.broken_exception_transitions =*/ false, - /*.instant_run_enabled =*/ false, - /*.jni_add_native_method_registration_attribute_present =*/ false, - /*.have_runtime_config_blob =*/ false, - /*.bound_exception_type =*/ 0, // System - /*.package_naming_policy =*/ 0, - /*.environment_variable_count =*/ 0, - /*.system_property_count =*/ 0, - /*.android_package_name =*/ "com.xamarin.test", + .uses_mono_llvm = false, + .uses_mono_aot = false, + .uses_assembly_preload = false, + .is_a_bundled_app = false, + .broken_exception_transitions = false, + .instant_run_enabled = false, + .jni_add_native_method_registration_attribute_present = false, + .have_runtime_config_blob = false, + .bound_exception_type = 0, // System + .package_naming_policy = 0, + .environment_variable_count = 0, + .system_property_count = 0, + .android_package_name = "com.xamarin.test", +}; + +ManagedTokenIds managed_token_ids = { + .android_runtime_jnienv = 0, + .android_runtime_jnienv_initialize = 0, + .android_runtime_jnienv_registerjninatives = 0, + .android_runtime_jnienv_bridgeprocessing = 0, + + .java_lang_object = 0, + .java_lang_object_handle = 0, + .java_lang_object_handle_type = 0, + .java_lang_object_refs_added = 0, + .java_lang_object_weak_handle = 0, + + .java_lang_throwable = 0, + .java_lang_throwable_handle = 0, + .java_lang_throwable_handle_type = 0, + .java_lang_throwable_refs_added = 0, + .java_lang_throwable_weak_handle = 0, + + .java_interop_javaobject = 0, + .java_interop_javaobject_handle = 0, + .java_interop_javaobject_handle_type = 0, + .java_interop_javaobject_refs_added = 0, + .java_interop_javaobject_weak_handle = 0, + + .java_interop_javaexception = 0, + .java_interop_javaexception_handle = 0, + .java_interop_javaexception_handle_type = 0, + .java_interop_javaexception_refs_added = 0, + .java_interop_javaexception_weak_handle = 0, }; +#if defined (__clang__) +#pragma clang diagnostic pop +#endif + const char* mono_aot_mode_name = ""; const char* app_environment_variables[] = {}; const char* app_system_properties[] = {}; diff --git a/src/monodroid/jni/basic-utilities.hh b/src/monodroid/jni/basic-utilities.hh index ff0a0ae07b1..92fb0fa33e3 100644 --- a/src/monodroid/jni/basic-utilities.hh +++ b/src/monodroid/jni/basic-utilities.hh @@ -24,6 +24,14 @@ namespace xamarin::android { class BasicUtilities { + public: + static constexpr bool is_running_on_desktop = +#if ANDROID + false; +#else + true; +#endif + public: FILE *monodroid_fopen (const char* filename, const char* mode); int monodroid_stat (const char *path, monodroid_stat_t *s); diff --git a/src/monodroid/jni/designer-assemblies.cc b/src/monodroid/jni/designer-assemblies.cc index cae48195833..3a0982febc1 100644 --- a/src/monodroid/jni/designer-assemblies.cc +++ b/src/monodroid/jni/designer-assemblies.cc @@ -43,7 +43,7 @@ DesignerAssemblies::try_load_assembly (MonoDomain *domain, MonoAssemblyName *nam * to select the loading context to use (it would require access to the MonoAssemblyLoadRequest API) * which mean we can't properly do either loading from memory or call LoadFrom */ - MonoClass *assembly_klass = utils.monodroid_get_class_from_name (domain, "mscorlib", "System.Reflection", "Assembly"); + MonoClass *assembly_klass = utils.monodroid_get_class (domain, "mscorlib", "System.Reflection", "Assembly"); if (entry_bytes_len > 0) { MonoClass *byte_klass = mono_get_byte_class (); diff --git a/src/monodroid/jni/monodroid-glue-internal.hh b/src/monodroid/jni/monodroid-glue-internal.hh index 0a416a77036..6952868bf8e 100644 --- a/src/monodroid/jni/monodroid-glue-internal.hh +++ b/src/monodroid/jni/monodroid-glue-internal.hh @@ -108,12 +108,6 @@ namespace xamarin::android::internal private: static constexpr size_t SMALL_STRING_PARSE_BUFFER_LEN = 50; - static constexpr bool is_running_on_desktop = -#if ANDROID - false; -#else - true; -#endif #define MAKE_API_DSO_NAME(_ext_) "libxa-internal-api." # _ext_ #if defined (WINDOWS) @@ -155,8 +149,12 @@ namespace xamarin::android::internal return java_System_identityHashCode; } - jclass get_java_class_TimeZone () const + jclass get_java_class_TimeZone () { + if (XA_UNLIKELY (java_TimeZone == nullptr)) { + lookup_java_timezone (); + } + return java_TimeZone; } @@ -192,6 +190,9 @@ namespace xamarin::android::internal char* get_java_class_name_for_TypeManager (jclass klass); private: + void lookup_java_timezone (); + static MonoMethod* monodroid_get_required_method (MonoImage *image, MonoClass *klass, uint32_t tokenID, int param_count, char const *type_name, char const *method_name); + #if defined (ANDROID) static void mono_log_handler (const char *log_domain, const char *log_level, const char *message, mono_bool fatal, void *user_data); static void mono_log_standard_streams_handler (const char *str, mono_bool is_stdout); @@ -229,7 +230,7 @@ namespace xamarin::android::internal #if !defined (NET6) void setup_bundled_app (const char *dso_name); #endif // ndef NET6 - void init_android_runtime (MonoDomain *domain, JNIEnv *env, jclass runtimeClass, jobject loader); + void init_android_runtime (MonoDomain *domain, JNIEnv *env, jclass runtimeClass, jobject loader, MonoClass*& jnienv, MonoClassField*& jnienv_bridge_processing_field); void set_environment_variable_for_directory (const char *name, jstring_wrapper &value, bool createDirectory, mode_t mode); void set_environment_variable_for_directory (const char *name, jstring_wrapper &value) @@ -283,7 +284,8 @@ namespace xamarin::android::internal jclass java_System; jmethodID java_System_identityHashCode; jmethodID Class_getName; - jclass java_TimeZone; + jclass java_TimeZone = nullptr; + jclass java_mono_Runtime = nullptr; timing_period jit_time; FILE *jit_log; MonoProfilerHandle profiler_handle; @@ -311,6 +313,7 @@ namespace xamarin::android::internal static std::mutex api_init_lock; static void *api_dso_handle; #endif // !def NET6 + static std::mutex jni_lookup_lock; }; } #endif diff --git a/src/monodroid/jni/monodroid-glue.cc b/src/monodroid/jni/monodroid-glue.cc index 47dbad2322c..06ae54826b4 100644 --- a/src/monodroid/jni/monodroid-glue.cc +++ b/src/monodroid/jni/monodroid-glue.cc @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -98,6 +99,7 @@ using namespace xamarin::android::internal; // implementation details as it would prevent mkbundle from working #include "mkbundle-api.h" +std::mutex MonodroidRuntime::jni_lookup_lock; #if !defined (NET6) #include "config.include" #include "machine.config.include" @@ -886,7 +888,7 @@ MonodroidRuntime::create_domain (JNIEnv *env, jstring_array_wrapper &runtimeApks } #endif // def NET6 - if (!have_mono_mkbundle_init && user_assemblies_count == 0 && androidSystem.count_override_assemblies () == 0 && !is_running_on_desktop) { + if (!have_mono_mkbundle_init && user_assemblies_count == 0 && androidSystem.count_override_assemblies () == 0 && !Util::is_running_on_desktop) { log_fatal (LOG_DEFAULT, "No assemblies found in '%s' or '%s'. Assuming this is part of Fast Deployment. Exiting...", androidSystem.get_override_dir (0), (AndroidSystem::MAX_OVERRIDES > 1 && androidSystem.get_override_dir (1) != nullptr) ? androidSystem.get_override_dir (1) : ""); @@ -910,7 +912,7 @@ MonodroidRuntime::create_domain (JNIEnv *env, jstring_array_wrapper &runtimeApks domain = utils.monodroid_create_appdomain (root_domain, domain_name.get (), /*shadow_copy:*/ 1, /*shadow_directory:*/ androidSystem.get_override_dir (0)); } - if constexpr (is_running_on_desktop) { + if constexpr (Util::is_running_on_desktop) { if (is_root_domain) { // Check that our corlib is coherent with the version of Mono we loaded otherwise // tell the IDE that the project likely need to be recompiled. @@ -957,25 +959,39 @@ MonodroidRuntime::LocalRefsAreIndirect (JNIEnv *env, jclass runtimeClass, int ve inline void MonodroidRuntime::lookup_bridge_info (MonoDomain *domain, MonoImage *image, const OSBridge::MonoJavaGCBridgeType *type, OSBridge::MonoJavaGCBridgeInfo *info) { - info->klass = utils.monodroid_get_class_from_image (domain, image, type->_namespace, type->_typename); - info->handle = mono_class_get_field_from_name (info->klass, const_cast ("handle")); - info->handle_type = mono_class_get_field_from_name (info->klass, const_cast ("handle_type")); - info->refs_added = mono_class_get_field_from_name (info->klass, const_cast ("refs_added")); - info->weak_handle = mono_class_get_field_from_name (info->klass, const_cast ("weak_handle")); - if (info->klass == nullptr || info->handle == nullptr || info->handle_type == nullptr || - info->refs_added == nullptr || info->weak_handle == nullptr) { + info->klass = utils.monodroid_get_required_class (domain, image, type->class_token_id, type->_namespace, type->_typename); + info->handle = utils.monodroid_get_class_field (info->klass, type->handle_token_id, "handle"); + info->handle_type = utils.monodroid_get_class_field (info->klass, type->handle_type_token_id, "handle_type"); + info->refs_added = utils.monodroid_get_class_field (info->klass, type->refs_added_token_id, "refs_added"); + info->weak_handle = utils.monodroid_get_class_field (info->klass, type->weak_handle_token_id, "weak_handle"); + if (XA_UNLIKELY (info->klass == nullptr || info->handle == nullptr || info->handle_type == nullptr || info->refs_added == nullptr || info->weak_handle == nullptr)) { log_fatal (LOG_DEFAULT, "The type `%s.%s` is missing required instance fields! handle=%p handle_type=%p refs_added=%p weak_handle=%p", type->_namespace, type->_typename, info->handle, info->handle_type, info->refs_added, info->weak_handle); - exit (FATAL_EXIT_MONO_MISSING_SYMBOLS); + abort (); + } +} + +force_inline MonoMethod* +MonodroidRuntime::monodroid_get_required_method (MonoImage *image, MonoClass *klass, uint32_t tokenID, int param_count, char const *type_name, char const *method_name) +{ + MonoMethod *method; + + if constexpr (Util::is_running_on_desktop) { + method = mono_class_get_method_from_name (klass, method_name, param_count); + } else { + method = mono_get_method (image, tokenID, klass); } + abort_unless (method != nullptr, "INTERNAL ERROR: Unable to find method `%s.%s`", type_name, method_name); + + return method; } void -MonodroidRuntime::init_android_runtime (MonoDomain *domain, JNIEnv *env, jclass runtimeClass, jobject loader) +MonodroidRuntime::init_android_runtime (MonoDomain *domain, JNIEnv *env, jclass runtimeClass, jobject loader, MonoClass*& jnienv, MonoClassField*& jnienv_bridge_processing_field) { mono_add_internal_call ("Java.Interop.TypeManager::monodroid_typemap_java_to_managed", reinterpret_cast(typemap_java_to_managed)); mono_add_internal_call ("Android.Runtime.JNIEnv::monodroid_typemap_managed_to_java", reinterpret_cast(typemap_managed_to_java)); @@ -987,7 +1003,7 @@ MonodroidRuntime::init_android_runtime (MonoDomain *domain, JNIEnv *env, jclass init.version = env->GetVersion (); init.androidSdkVersion = android_api_level; init.localRefsAreIndirect = LocalRefsAreIndirect (env, runtimeClass, init.androidSdkVersion); - init.isRunningOnDesktop = is_running_on_desktop ? 1 : 0; + init.isRunningOnDesktop = Util::is_running_on_desktop ? 1 : 0; init.brokenExceptionTransitions = application_config.broken_exception_transitions ? 1 : 0; init.packageNamingPolicy = static_cast(application_config.package_naming_policy); init.boundExceptionType = application_config.bound_exception_type; @@ -1011,15 +1027,10 @@ MonodroidRuntime::init_android_runtime (MonoDomain *domain, JNIEnv *env, jclass lookup_bridge_info (domain, image, &osBridge.get_java_gc_bridge_type (i), &osBridge.get_java_gc_bridge_info (i)); } - // TODO: try looking up the method by its token - MonoClass *runtime = utils.monodroid_get_class_from_image (domain, image, "Android.Runtime", "JNIEnv"); - MonoMethod *method = mono_class_get_method_from_name (runtime, "Initialize", 1); - - if (method == nullptr) { - log_fatal (LOG_DEFAULT, "INTERNAL ERROR: Unable to find Android.Runtime.JNIEnv.Initialize!"); - exit (FATAL_EXIT_MISSING_INIT); - } + MonoClass *runtime = utils.monodroid_get_required_class (domain, image, managed_token_ids.android_runtime_jnienv, "Android.Runtime", "JNIEnv"); + jnienv = runtime; + MonoMethod *method = monodroid_get_required_method (image, runtime, managed_token_ids.android_runtime_jnienv_initialize, 1, "Android.Runtime.JNIEnv", "Initialize"); MonoAssembly *ji_assm = utils.monodroid_load_assembly (domain, "Java.Interop"); MonoImage *ji_image = mono_assembly_get_image (ji_assm); for ( ; i < OSBridge::NUM_XA_GC_BRIDGE_TYPES + OSBridge::NUM_JI_GC_BRIDGE_TYPES; ++i) { @@ -1029,20 +1040,15 @@ MonodroidRuntime::init_android_runtime (MonoDomain *domain, JNIEnv *env, jclass /* If running on desktop, we may be swapping in a new Mono.Android image when calling this * so always make sure we have the freshest handle to the method. */ - if (registerType == nullptr || is_running_on_desktop) { - registerType = mono_class_get_method_from_name (runtime, "RegisterJniNatives", 5); - } - if (registerType == nullptr) { - log_fatal (LOG_DEFAULT, "INTERNAL ERROR: Unable to find Android.Runtime.JNIEnv.RegisterJniNatives!"); - exit (FATAL_EXIT_CANNOT_FIND_JNIENV); - } - MonoClass *android_runtime_jnienv = runtime; - MonoClassField *bridge_processing_field = mono_class_get_field_from_name (runtime, const_cast ("BridgeProcessing")); - if (android_runtime_jnienv ==nullptr || bridge_processing_field == nullptr) { - log_fatal (LOG_DEFAULT, "INTERNAL_ERROR: Unable to find Android.Runtime.JNIEnv.BridgeProcessing"); - exit (FATAL_EXIT_CANNOT_FIND_JNIENV); + if (registerType == nullptr || Util::is_running_on_desktop) { + registerType = monodroid_get_required_method (image, runtime, managed_token_ids.android_runtime_jnienv_registerjninatives, 5, "Android.Runtime.JNIEnv", "RegisterJniNatives"); } + // Android.Runtime.JNIEnv.BridgeProcessing + MonoClassField *bridge_processing_field = utils.monodroid_get_class_field (runtime, managed_token_ids.android_runtime_jnienv_bridgeprocessing, "BridgeProcessing"); + abort_unless (bridge_processing_field != nullptr, "INTERNAL_ERROR: Unable to find Android.Runtime.JNIEnv.BridgeProcessing"); + jnienv_bridge_processing_field = bridge_processing_field; + jclass lrefLoaderClass = env->GetObjectClass (loader); init.Loader_loadClass = env->GetMethodID (lrefLoaderClass, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;"); env->DeleteLocalRef (lrefLoaderClass); @@ -1074,7 +1080,7 @@ MonodroidRuntime::get_android_runtime_class (MonoDomain *domain) { MonoAssembly *assm = utils.monodroid_load_assembly (domain, "Mono.Android"); MonoImage *image = mono_assembly_get_image (assm); - MonoClass *runtime = utils.monodroid_get_class_from_image (domain, image, "Android.Runtime", "JNIEnv"); + MonoClass *runtime = utils.monodroid_get_required_class (domain, image, managed_token_ids.android_runtime_jnienv, "Android.Runtime", "JNIEnv"); return runtime; } @@ -1686,7 +1692,7 @@ MonodroidRuntime::create_and_initialize_domain (JNIEnv* env, jclass runtimeClass MonoDomain* domain = create_domain (env, runtimeApks, is_root_domain); // When running on desktop, the root domain is only a dummy so don't initialize it - if constexpr (is_running_on_desktop) { + if constexpr (Util::is_running_on_desktop) { if (is_root_domain) { return domain; } @@ -1696,11 +1702,14 @@ MonodroidRuntime::create_and_initialize_domain (JNIEnv* env, jclass runtimeClass if (assembliesBytes != nullptr) designerAssemblies.add_or_update_from_java (domain, env, assemblies, assembliesBytes, assembliesPaths); #endif - bool preload = (androidSystem.is_assembly_preload_enabled () || (is_running_on_desktop && force_preload_assemblies)); + bool preload = (androidSystem.is_assembly_preload_enabled () || (Util::is_running_on_desktop && force_preload_assemblies)); load_assemblies (domain, preload, assemblies); - init_android_runtime (domain, env, runtimeClass, loader); - osBridge.add_monodroid_domain (domain); + MonoClass* jnienv = nullptr; + MonoClassField* jnienv_bridge_processing_field = nullptr; + init_android_runtime (domain, env, runtimeClass, loader, jnienv, jnienv_bridge_processing_field); + + osBridge.add_monodroid_domain (domain, jnienv, jnienv_bridge_processing_field); return domain; } @@ -1845,6 +1854,13 @@ MonodroidRuntime::install_logging_handlers () #endif // def ANDROID +void +MonodroidRuntime::lookup_java_timezone () +{ + std::lock_guard lock (jni_lookup_lock); + java_TimeZone = utils.get_class_from_runtime_field (osBridge.ensure_jnienv (), java_mono_Runtime, "java_util_TimeZone", true); +} + inline void MonodroidRuntime::Java_mono_android_Runtime_initInternal (JNIEnv *env, jclass klass, jstring lang, jobjectArray runtimeApksJava, jstring runtimeNativeLibDir, jobjectArray appDirs, jobject loader, @@ -1853,6 +1869,7 @@ MonodroidRuntime::Java_mono_android_Runtime_initInternal (JNIEnv *env, jclass kl char *mono_log_mask = nullptr; char *mono_log_level = nullptr; + java_mono_Runtime = klass; init_logging_categories (mono_log_mask, mono_log_level); timing_period total_time; @@ -1885,8 +1902,6 @@ MonodroidRuntime::Java_mono_android_Runtime_initInternal (JNIEnv *env, jclass kl androidSystem.detect_embedded_dso_mode (applicationDirs); androidSystem.set_running_in_emulator (isEmulator); - java_TimeZone = utils.get_class_from_runtime_field (env, klass, "java_util_TimeZone", true); - utils.monodroid_store_package_name (application_config.android_package_name); jstring_wrapper jstr (env, lang); @@ -2058,7 +2073,7 @@ MonodroidRuntime::Java_mono_android_Runtime_initInternal (JNIEnv *env, jclass kl #endif // def ANDROID && ndef NET6 // Install our dummy exception handler on Desktop - if constexpr (is_running_on_desktop) { + if constexpr (Util::is_running_on_desktop) { mono_add_internal_call ("System.Diagnostics.Debugger::Mono_UnhandledException_internal(System.Exception)", reinterpret_cast (monodroid_Mono_UnhandledException_internal)); } @@ -2196,8 +2211,8 @@ MonodroidRuntime::Java_mono_android_Runtime_register (JNIEnv *env, jstring manag domain = mono_domain_get (); MonoMethod *register_jni_natives = registerType; - if constexpr (is_running_on_desktop) { - MonoClass *runtime = utils.monodroid_get_class_from_name (domain, "Mono.Android", "Android.Runtime", "JNIEnv"); + if constexpr (Util::is_running_on_desktop) { + MonoClass *runtime = utils.monodroid_get_class (domain, "Mono.Android", "Android.Runtime", "JNIEnv"); register_jni_natives = mono_class_get_method_from_name (runtime, "RegisterJniNatives", 5); } utils.monodroid_runtime_invoke (domain, register_jni_natives, nullptr, args, nullptr); diff --git a/src/monodroid/jni/osbridge.cc b/src/monodroid/jni/osbridge.cc index 1b05810f2bb..d7a67466d1a 100644 --- a/src/monodroid/jni/osbridge.cc +++ b/src/monodroid/jni/osbridge.cc @@ -28,6 +28,7 @@ #include "globals.hh" #include "osbridge.hh" +#include "xamarin-app.hh" // These two must stay here until JavaInterop is converted to C++ FILE *gref_log; @@ -37,18 +38,57 @@ using namespace xamarin::android; using namespace xamarin::android::internal; const OSBridge::MonoJavaGCBridgeType OSBridge::mono_xa_gc_bridge_types[] = { - { "Java.Lang", "Object" }, - { "Java.Lang", "Throwable" }, + { + ._namespace = "Java.Lang", + ._typename = "Object", + .class_token_id = managed_token_ids.java_lang_object, + .handle_token_id = managed_token_ids.java_lang_object_handle, + .handle_type_token_id = managed_token_ids.java_lang_object_handle_type, + .refs_added_token_id = managed_token_ids.java_lang_object_refs_added, + .weak_handle_token_id = managed_token_ids.java_lang_object_weak_handle, + }, + + { + ._namespace = "Java.Lang", + ._typename = "Throwable", + .class_token_id = managed_token_ids.java_lang_throwable, + .handle_token_id = managed_token_ids.java_lang_throwable_handle, + .handle_type_token_id = managed_token_ids.java_lang_throwable_handle_type, + .refs_added_token_id = managed_token_ids.java_lang_throwable_refs_added, + .weak_handle_token_id = managed_token_ids.java_lang_throwable_weak_handle, + }, }; const OSBridge::MonoJavaGCBridgeType OSBridge::mono_ji_gc_bridge_types[] = { - { "Java.Interop", "JavaObject" }, - { "Java.Interop", "JavaException" }, + { + ._namespace = "Java.Interop", + ._typename = "JavaObject", + .class_token_id = managed_token_ids.java_interop_javaobject, + .handle_token_id = managed_token_ids.java_interop_javaobject_handle, + .handle_type_token_id = managed_token_ids.java_interop_javaobject_handle_type, + .refs_added_token_id = managed_token_ids.java_interop_javaobject_refs_added, + .weak_handle_token_id = managed_token_ids.java_interop_javaobject_weak_handle, + }, + + { + ._namespace = "Java.Interop", + ._typename = "JavaException", + .class_token_id = managed_token_ids.java_interop_javaexception, + .handle_token_id = managed_token_ids.java_interop_javaexception_handle, + .handle_type_token_id = managed_token_ids.java_interop_javaexception_handle_type, + .refs_added_token_id = managed_token_ids.java_interop_javaexception_refs_added, + .weak_handle_token_id = managed_token_ids.java_interop_javaexception_weak_handle, + }, }; const OSBridge::MonoJavaGCBridgeType OSBridge::empty_bridge_type = { - "", - "" + ._namespace = "", + ._typename = "", + .class_token_id = 0, + .handle_token_id = 0, + .handle_type_token_id = 0, + .refs_added_token_id = 0, + .weak_handle_token_id = 0, }; const uint32_t OSBridge::NUM_XA_GC_BRIDGE_TYPES = (sizeof (mono_xa_gc_bridge_types)/sizeof (mono_xa_gc_bridge_types [0])); @@ -1108,7 +1148,7 @@ OSBridge::initialize_on_runtime_init (JNIEnv *env, jclass runtimeClass) } void -OSBridge::add_monodroid_domain (MonoDomain *domain) +OSBridge::add_monodroid_domain (MonoDomain *domain, MonoClass* jnienv, MonoClassField* jnienv_bridge_processing_field) { MonodroidBridgeProcessingInfo *node = new MonodroidBridgeProcessingInfo (); //calloc (1, sizeof (MonodroidBridgeProcessingInfo)); @@ -1116,9 +1156,8 @@ OSBridge::add_monodroid_domain (MonoDomain *domain) * use GC API to allocate memory and thus can't be called from within the GC callback as it causes a deadlock * (the routine allocating the memory waits for the GC round to complete first) */ - MonoClass *jnienv = utils.monodroid_get_class_from_name (domain, "Mono.Android", "Android.Runtime", "JNIEnv");; node->domain = domain; - node->bridge_processing_field = mono_class_get_field_from_name (jnienv, const_cast ("BridgeProcessing")); + node->bridge_processing_field = jnienv_bridge_processing_field; node->jnienv_vtable = mono_class_vtable (domain, jnienv); node->next = domains_list; diff --git a/src/monodroid/jni/osbridge.hh b/src/monodroid/jni/osbridge.hh index c905801d1a6..87031de6edd 100644 --- a/src/monodroid/jni/osbridge.hh +++ b/src/monodroid/jni/osbridge.hh @@ -21,8 +21,13 @@ namespace xamarin::android::internal public: struct MonoJavaGCBridgeType { - const char *_namespace; - const char *_typename; + const char *_namespace; + const char *_typename; + const uint32_t class_token_id; + const uint32_t handle_token_id; + const uint32_t handle_type_token_id; + const uint32_t refs_added_token_id; + const uint32_t weak_handle_token_id; }; /* `mono_java_gc_bridge_info` stores shared global data about the last Monodroid assembly loaded. @@ -116,7 +121,7 @@ namespace xamarin::android::internal JNIEnv* ensure_jnienv (); void initialize_on_onload (JavaVM *vm, JNIEnv *env); void initialize_on_runtime_init (JNIEnv *env, jclass runtimeClass); - void add_monodroid_domain (MonoDomain *domain); + void add_monodroid_domain (MonoDomain *domain, MonoClass* jnienv, MonoClassField* jnienv_bridge_processing_field); void remove_monodroid_domain (MonoDomain *domain); void on_destroy_contexts (); diff --git a/src/monodroid/jni/util.cc b/src/monodroid/jni/util.cc index 5c2f879c210..63ccc4d5b59 100644 --- a/src/monodroid/jni/util.cc +++ b/src/monodroid/jni/util.cc @@ -231,8 +231,8 @@ Util::monodroid_property_set (MonoDomain *domain, MonoProperty *property, void * MonoDomain* Util::monodroid_create_appdomain (MonoDomain *parent_domain, const char *friendly_name, int shadow_copy, const char *shadow_directories) { - MonoClass *appdomain_setup_klass = monodroid_get_class_from_name (parent_domain, "mscorlib", "System", "AppDomainSetup"); - MonoClass *appdomain_klass = monodroid_get_class_from_name (parent_domain, "mscorlib", "System", "AppDomain"); + MonoClass *appdomain_setup_klass = monodroid_get_class (parent_domain, "mscorlib", "System", "AppDomainSetup"); + MonoClass *appdomain_klass = monodroid_get_class (parent_domain, "mscorlib", "System", "AppDomain"); MonoMethod *create_domain = mono_class_get_method_from_name (appdomain_klass, "CreateDomain", 3); MonoProperty *shadow_copy_prop = mono_class_get_property_from_name (appdomain_setup_klass, "ShadowCopyFiles"); MonoProperty *shadow_copy_dirs_prop = mono_class_get_property_from_name (appdomain_setup_klass, "ShadowCopyDirectories"); @@ -255,12 +255,14 @@ Util::monodroid_create_appdomain (MonoDomain *parent_domain, const char *friendl } MonoClass* -Util::monodroid_get_class_from_name (MonoDomain *domain, const char* assembly, const char *_namespace, const char *type) +Util::monodroid_get_class ([[maybe_unused]] MonoDomain *domain, const char* assembly, const char *_namespace, const char *type) { +#if !defined (NET6) MonoDomain *current = mono_domain_get (); if (domain != current) mono_domain_set (domain, FALSE); +#endif // ndef NET6 MonoClass *result; MonoAssemblyName *aname = mono_assembly_name_new (assembly); @@ -271,8 +273,10 @@ Util::monodroid_get_class_from_name (MonoDomain *domain, const char* assembly, c } else result = nullptr; +#if !defined (NET6) if (domain != current) mono_domain_set (current, FALSE); +#endif // ndef NET6 mono_assembly_name_free (aname); @@ -280,17 +284,51 @@ Util::monodroid_get_class_from_name (MonoDomain *domain, const char* assembly, c } MonoClass* -Util::monodroid_get_class_from_image (MonoDomain *domain, MonoImage *image, const char *_namespace, const char *type) +Util::monodroid_get_class ([[maybe_unused]] MonoDomain *domain, MonoImage *image, const char *_namespace, const char *type, bool required) { +#if !defined (NET6) MonoDomain *current = mono_domain_get (); if (domain != current) mono_domain_set (domain, FALSE); +#endif // ndef NET6 MonoClass *result = mono_class_from_name (image, _namespace, type); +#if !defined (NET6) if (domain != current) mono_domain_set (current, FALSE); +#endif // ndef NET6 + + abort_unless (!required || result != nullptr, "INTERNAL ERROR: Unable to find type `%s.%s, %s`", _namespace, type, mono_image_get_name (image)); + + return result; +} + +MonoClass* +Util::monodroid_get_class ([[maybe_unused]] MonoDomain *domain, MonoImage* image, uint32_t token_id, const char *_namespace, const char *type, bool required) +{ + if constexpr (is_running_on_desktop) { + return monodroid_get_class (domain, image, _namespace, type, required); + } + + log_debug (LOG_ASSEMBLY, "Getting class '%s.%s' from assembly '%s' with token id: %u", _namespace, type, mono_image_get_name (image), token_id); +#if !defined (NET6) + MonoDomain *current = mono_domain_get (); + + if (domain != current) + mono_domain_set (domain, FALSE); +#endif // ndef NET6 + + MonoClass *result = mono_class_get (image, token_id); + +#if !defined (NET6) + if (domain != current) + mono_domain_set (current, FALSE); +#endif // ndef NET6 + + abort_unless (!required || result != nullptr, "INTERNAL ERROR: Unable to find type `%s.%s, %s`", _namespace, type, mono_image_get_name (image)); + log_debug (LOG_ASSEMBLY, "Class found: %s.%s", mono_class_get_namespace (result), mono_class_get_name (result)); return result; } diff --git a/src/monodroid/jni/util.hh b/src/monodroid/jni/util.hh index 15b2fa02938..899af2de7b5 100644 --- a/src/monodroid/jni/util.hh +++ b/src/monodroid/jni/util.hh @@ -82,9 +82,29 @@ namespace xamarin::android void monodroid_store_package_name (const char *name); MonoAssembly *monodroid_load_assembly (MonoDomain *domain, const char *basename); MonoObject *monodroid_runtime_invoke (MonoDomain *domain, MonoMethod *method, void *obj, void **params, MonoObject **exc); - MonoClass *monodroid_get_class_from_name (MonoDomain *domain, const char* assembly, const char *_namespace, const char *type); + MonoClass *monodroid_get_class (MonoDomain *domain, const char* assembly, const char *_namespace, const char *type); + MonoClass *monodroid_get_class (MonoDomain *domain, MonoImage* image, const char *_namespace, const char *type, bool required); + MonoClass *monodroid_get_required_class (MonoDomain *domain, MonoImage* image, const char *_namespace, const char *type) + { + return monodroid_get_class (domain, image, _namespace, type, true); + } + + MonoClass *monodroid_get_class (MonoDomain *domain, MonoImage* image, uint32_t token_id, const char *_namespace, const char *type, bool required); + MonoClass *monodroid_get_required_class (MonoDomain *domain, MonoImage* image, uint32_t token_id, const char *_namespace, const char *type) + { + return monodroid_get_class (domain, image, token_id, _namespace, type, true); + } + + MonoClassField *monodroid_get_class_field (MonoClass *klass, uint32_t token_id, const char *name) + { + if constexpr (is_running_on_desktop) { + return mono_class_get_field_from_name (klass, const_cast (name)); + } else { + return mono_class_get_field (klass, token_id); + } + } + MonoDomain *monodroid_create_appdomain (MonoDomain *parent_domain, const char *friendly_name, int shadow_copy, const char *shadow_directories); - MonoClass *monodroid_get_class_from_image (MonoDomain *domain, MonoImage* image, const char *_namespace, const char *type); int send_uninterrupted (int fd, void *buf, size_t len); ssize_t recv_uninterrupted (int fd, void *buf, size_t len); jclass get_class_from_runtime_field (JNIEnv *env, jclass runtime, const char *name, bool make_gref = false); diff --git a/src/monodroid/jni/xa-internal-api.cc b/src/monodroid/jni/xa-internal-api.cc index e066737121a..efe4d0b3e7d 100644 --- a/src/monodroid/jni/xa-internal-api.cc +++ b/src/monodroid/jni/xa-internal-api.cc @@ -208,6 +208,8 @@ MonoAndroidInternalCalls_Impl::monodroid_get_identity_hash_code (JNIEnv *env, vo void* MonoAndroidInternalCalls_Impl::monodroid_timezone_get_default_id () { + log_warn (LOG_ASSEMBLY, "%s", __PRETTY_FUNCTION__); + JNIEnv *env = osBridge.ensure_jnienv (); jmethodID getDefault = env->GetStaticMethodID (monodroidRuntime.get_java_class_TimeZone (), "getDefault", "()Ljava/util/TimeZone;"); jmethodID getID = env->GetMethodID (monodroidRuntime.get_java_class_TimeZone (), "getID", "()Ljava/lang/String;"); diff --git a/src/monodroid/jni/xamarin-app.hh b/src/monodroid/jni/xamarin-app.hh index 6b5cb9ba41e..4e85b92726d 100644 --- a/src/monodroid/jni/xamarin-app.hh +++ b/src/monodroid/jni/xamarin-app.hh @@ -5,6 +5,7 @@ #include #include +#include #include "monodroid.h" @@ -113,6 +114,38 @@ struct ApplicationConfig const char *android_package_name; }; +struct ManagedTokenIds +{ + const uint32_t android_runtime_jnienv; + const uint32_t android_runtime_jnienv_initialize; + const uint32_t android_runtime_jnienv_registerjninatives; + const uint32_t android_runtime_jnienv_bridgeprocessing; + + const uint32_t java_lang_object; + const uint32_t java_lang_object_handle; + const uint32_t java_lang_object_handle_type; + const uint32_t java_lang_object_refs_added; + const uint32_t java_lang_object_weak_handle; + + const uint32_t java_lang_throwable; + const uint32_t java_lang_throwable_handle; + const uint32_t java_lang_throwable_handle_type; + const uint32_t java_lang_throwable_refs_added; + const uint32_t java_lang_throwable_weak_handle; + + const uint32_t java_interop_javaobject; + const uint32_t java_interop_javaobject_handle; + const uint32_t java_interop_javaobject_handle_type; + const uint32_t java_interop_javaobject_refs_added; + const uint32_t java_interop_javaobject_weak_handle; + + const uint32_t java_interop_javaexception; + const uint32_t java_interop_javaexception_handle; + const uint32_t java_interop_javaexception_handle_type; + const uint32_t java_interop_javaexception_refs_added; + const uint32_t java_interop_javaexception_weak_handle; +}; + MONO_API uint64_t format_tag; #if defined (DEBUG) || !defined (ANDROID) @@ -127,8 +160,10 @@ MONO_API const TypeMapJava map_java[]; MONO_API CompressedAssemblies compressed_assemblies; MONO_API ApplicationConfig application_config; +MONO_API ManagedTokenIds managed_token_ids; MONO_API const char* app_environment_variables[]; MONO_API const char* app_system_properties[]; MONO_API const char* mono_aot_mode_name; + #endif // __XAMARIN_ANDROID_TYPEMAP_H