diff --git a/Directory.Build.props b/Directory.Build.props
index 29990936e86..fef6d096b52 100644
--- a/Directory.Build.props
+++ b/Directory.Build.props
@@ -27,10 +27,6 @@
true
-
- <_EnableMarshalMethods>NoThanks
-
-
13.1.99
+- [Introduction](#introduction)
+- [Java <-> Managed interoperability overview](#java-managed-interoperability-overview)
+ - [Java Callable Wrappers (JCW)](#java-callable-wrappers-jcw)
+- [Registration](#registration)
+ - [Dynamic registration](#dynamic-registration)
+ - [Dynamic Java Callable Wrappers registration code](#dynamic-java-callable-wrappers-registration-code)
+ - [Dynamic Registration call sequence](#dynamic-registration-call-sequence)
+ - [Marshal methods](#marshal-methods)
+ - [Marshal Methods Java Callable Wrappers registration code](#marshal-methods-java-callable-wrappers-registration-code)
+ - [Marshal methods C# source code](#marshal-methods-c-source-code)
+ - [JNI requirements](#jni-requirements)
+ - [LLVM IR code generation](#llvm-ir-code-generation)
+ - [Assembly rewriting](#assembly-rewriting)
+ - [Wrappers for methods with non-blittable types](#wrappers-for-methods-with-non-blittable-types)
+ - [UnmanagedCallersOnly attribute](#unmanagedcallersonly-attribute)
+ - [Marshal Methods Registration call sequence](#marshal-methods-registration-call-sequence)
+
+
+# Introduction
+
+At the core of `.NET Android` is its ability to interoperate with
+the Java/Kotlin APIs implemented in the Android system. To make it
+work, it is necessary to "bridge" the two separate worlds of Java VM
+(`ART` in the Android OS) and the Managed VM (`MonoVM`). Application
+developers expect to be able to call native Android APIs and receive
+calls (or react to events) from the Android side using code written in
+one of the .NET managed languages. To make it work, `.NET Android`
+employs a number of techniques, both at build and at run time, which
+are described in the sections below.
+
+This guide is meant to explain the technical implementation in a way
+that is sufficient to understand the system without having to read the
+actual source code.
+
+# Java <-> Managed interoperability overview
+
+Java VM and Managed VM are two entirely separate entities which
+co-exist in the same process/application. Despite sharing the same
+process resources, they don't "naturally" communicate with each other.
+There is no direct way to call Java/Kotlin from .NET a'la the
+`p/invoke` mechanism which allows calling native code APIs. Nor there
+exists a way for Java/Kotlin code to invoke managed methods. To make
+it possible, `.NET Android` takes advantage of the Java's [JNI](https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/jniTOC.html)
+(`Java Native Interface`), a mechanism that allows native code
+(.NET managed code being "native" in this context) to register
+implementations of Java methods, written outside the Java VM and in
+languages other than Java/Kotlin (for instance in `C`, `C++` or
+`Rust`).
+
+Such methods need to be appropriately declared in the Java code, for
+instance:
+
+```java
+class MainActivity
+ extends androidx.appcompat.app.AppCompatActivity
+{
+ public void onCreate (android.os.Bundle p0)
+ {
+ n_onCreate (p0);
+ }
+
+ private native void n_onCreate (android.os.Bundle p0);
+}
+```
+
+Each native method is declared using the `native` keyword, and
+whenever it is invoked from other Java code, the Java VM will use the
+JNI to invoke the target method.
+
+Native methods can be registered either dynamically (by calling the
+[`RegisterNatives`](https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#RegisterNatives)
+JNI function) or "statically", by providing a native shared library
+which exports a symbol with appropriate name which points to the
+native function implementing the Java method.
+
+Both ways of registration are described in detail in the following
+sections.
+
+## Java Callable Wrappers (JCW)
+
+`.NET Android` wraps the entire Android API by generating
+appropriate C# code which mirrors the Java/Kotlin code (classes,
+interfaces, methods, properties etc). Each generated class that
+corresponds to a Java/Kotlin type, is derived from the
+`Java.Lang.Object` class (implemented in the `Mono.Android` assembly),
+which marks it as a "Java interoperable type", meaning that it can
+implement or override virtual Java methods. To make registration and
+invoking of such methods possible, it is necessary to generate a Java
+class which mirrors the Managed one and provides an entry point to
+the Java <-> Managed transition. The Java classes are generated
+during application (as well as `.NET Android`) build and we call
+them **Java Callable Wrappers** (or **JCW** for short). For instance,
+the following managed class:
+
+```csharp
+public class MainActivity : AppCompatActivity
+{
+ public override Android.Views.View? OnCreateView (Android.Views.View? parent, string name, Android.Content.Context context, Android.Util.IAttributeSet attrs)
+ {
+ return base.OnCreateView (parent, name, context, attrs);
+ }
+
+ protected override void OnCreate (Bundle savedInstanceState)
+ {
+ base.OnCreate(savedInstanceState);
+ DoSomething (savedInstanceState);
+ }
+
+ void DoSomething (Bundle bundle)
+ {
+ // do something with the bundle
+ }
+}
+```
+
+overrides two Java virtual methods found in the `AppCompatActivity`
+type: `OnCreateView` and `OnCreate`. The `DoSomething` method does
+not correspond to any method found in the base Java type, and thus it
+won't be included in the JCW.
+
+The Java Callable Wrapper generated for the above class would look as
+follows (a few generated methods not relevant to the discussion have
+been omitted for brevity):
+
+```java
+public class MainActivity
+ extends androidx.appcompat.app.AppCompatActivity
+{
+ public android.view.View onCreateView (android.view.View p0, java.lang.String p1, android.content.Context p2, android.util.AttributeSet p3)
+ {
+ return n_onCreateView (p0, p1, p2, p3);
+ }
+ private native android.view.View n_onCreateView (android.view.View p0, java.lang.String p1, android.content.Context p2, android.util.AttributeSet p3);
+
+ public void onCreate (android.os.Bundle p0)
+ {
+ n_onCreate (p0);
+ }
+ private native void n_onCreate (android.os.Bundle p0);
+}
+```
+
+Understanding the connection between Managed methods and their Java
+counterparts is required in order to understand the registration
+mechanisms described in sections found later in this document. The
+[Dynamic registration](#dynamic-registration) section will expand on
+this example in order to explain the details of how the Managed type
+and its methods are registered with the Java VM.
+
+# Registration
+
+Both mechanisms of method registration rely on generation of [Java
+Callable Wrappers](#java-callable-wrappers-jcw), with [Dynamic
+registration](#dynamic-registration) requiring more code to be
+generated so that the registration can be performed at the runtime.
+
+JCW are generated only for types that derive from
+the `Java.Lang.Object` type. Finding such types is the task of the
+Java.Interop's [`JavaTypeScanner`](../../external/Java.Interop/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaTypeScanner.cs),
+which uses `Mono.Cecil` to read all the assemblies referenced by the
+application and its libraries. The returned list of assemblies is
+then used by a variety of tasks, JCW being only one
+of them.
+
+After all types are found,
+[`JavaCallableWrapperGenerator`](../../external/Java.Interop/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.cs)
+is invoked in order to analyze each method in each type, looking for
+those which override a virtual Java method and, thus, need to be
+included in the wrapper class code. The generator optionally (if
+[marshal methods](#marshal-methods) are enabled) passes each method to
+an implementation of the
+[`Java.Interop.Tools.JavaCallableWrappers.JavaCallableMethodClassifier`](../../external/Java.Interop/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.cs)
+abstract class (which is
+[`MarshalMethodsClassifier`](../../src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsClassifier.cs)
+in our case), to check whether the given method can be registered
+statically.
+
+`JavaCallableWrapperGenerator` looks for methods decorated with the
+`[Register]` attribute, which most frequently is created by invoking
+its constructor with three parameters:
+
+ 1. Java method name
+ 2. JNI method signature
+ 3. "Connector" method name
+
+The "connector" is a static method which creates a delegate that
+subsequently allows calling of the native callback method:
+
+```csharp
+public class MainActivity : AppCompatActivity
+{
+ // Connector backing field
+ static Delegate? cb_onCreate_Landroid_os_Bundle_;
+
+ // Connector method
+ static Delegate GetOnCreate_Landroid_os_Bundle_Handler ()
+ {
+ if (cb_onCreate_Landroid_os_Bundle_ == null)
+ cb_onCreate_Landroid_os_Bundle_ = JNINativeWrapper.CreateDelegate ((_JniMarshal_PPL_V) n_OnCreate_Landroid_os_Bundle_);
+ return cb_onCreate_Landroid_os_Bundle_;
+ }
+
+ // Native callback
+ static void n_OnCreate_Landroid_os_Bundle_ (IntPtr jnienv, IntPtr native__this, IntPtr native_savedInstanceState)
+ {
+ var __this = global::Java.Lang.Object.GetObject (jnienv, native__this, JniHandleOwnership.DoNotTransfer)!;
+ var savedInstanceState = global::Java.Lang.Object.GetObject (native_savedInstanceState, JniHandleOwnership.DoNotTransfer);
+ __this.OnCreate (savedInstanceState);
+ }
+
+ // Target method
+ [Register ("onCreate", "(Landroid/os/Bundle;)V", "GetOnCreate_Landroid_os_Bundle_Handler")]
+ protected virtual unsafe void OnCreate (Android.OS.Bundle? savedInstanceState)
+ {
+ const string __id = "onCreate.(Landroid/os/Bundle;)V";
+ try {
+ JniArgumentValue* __args = stackalloc JniArgumentValue [1];
+ __args [0] = new JniArgumentValue ((savedInstanceState == null) ? IntPtr.Zero : ((global::Java.Lang.Object) savedInstanceState).Handle);
+ _members.InstanceMethods.InvokeVirtualVoidMethod (__id, this, __args);
+ } finally {
+ global::System.GC.KeepAlive (savedInstanceState);
+ }
+ }
+}
+```
+
+The above code is actually generated in the `Android.App.Activity`
+class while .NET Android is built, from which our example
+`MainActivity` eventually derives.
+
+What happens with the above code depends on the registration mechanism
+and is described in the sections below.
+
+## Dynamic registration
+
+This registration mechanism has been used by `.NET Android` since
+the beginning and it will remain in use for the foreseeable future
+when the application is built in the `Debug` configuration or when
+[Marshal Methods](#marshal-methods) are turned off.
+
+### Dynamic Java Callable Wrappers registration code
+
+Building on the C# example shown in the [Java Callable
+Wrappers](#java-callable-wrappers-jcw) section, the following Java
+code is generated (only the parts relevant to registration are shown):
+
+```java
+public class MainActivity
+ extends androidx.appcompat.app.AppCompatActivity
+{
+/** @hide */
+ public static final String __md_methods;
+ static {
+ __md_methods =
+ "n_onCreateView:(Landroid/view/View;Ljava/lang/String;Landroid/content/Context;Landroid/util/AttributeSet;)Landroid/view/View;:GetOnCreateView_Landroid_view_View_Ljava_lang_String_Landroid_content_Context_Landroid_util_AttributeSet_Handler\n" +
+ "n_onCreate:(Landroid/os/Bundle;)V:GetOnCreate_Landroid_os_Bundle_Handler\n" +
+ "";
+ mono.android.Runtime.register ("HelloAndroid.MainActivity, HelloAndroid", MainActivity.class, __md_methods);
+ }
+
+ public android.view.View onCreateView (android.view.View p0, java.lang.String p1, android.content.Context p2, android.util.AttributeSet p3)
+ {
+ return n_onCreateView (p0, p1, p2, p3);
+ }
+
+ private native android.view.View n_onCreateView (android.view.View p0, java.lang.String p1, android.content.Context p2, android.util.AttributeSet p3);
+
+ public void onCreate (android.os.Bundle p0)
+ {
+ n_onCreate (p0);
+ }
+
+ private native void n_onCreate (android.os.Bundle p0);
+}
+```
+
+Code fragment which takes part in registration is the class's static
+constructor. For each method registered for the type (that is,
+implemented or overridden in the managed code), the JCW generator
+outputs a single string which contains full information about the type
+and method to register. Each such registration string is terminated
+with the newline character and the entire sequence ends with an empty
+string. Together, all the lines are concatenated and placed in the
+`__md_methods` static variable. The `mono.android.Runtime.register`
+method (see below for more details) is then invoked to register all
+the methods.
+
+### Dynamic Registration call sequence
+
+All the "native" methods declared in the generated Java type are
+registered when the type is constructed or accessed for the first
+time. This is when the Java VM invokes the type's static constructor,
+kicking off a sequence of calls that eventually ends with all the type
+methods registered with JNI:
+
+ 1. `mono.android.Runtime.register` is itself a native method,
+ declared in the
+ [`Runtime`](../../src/java-runtime/java/mono/android/Runtime.java)
+ class of .NET Android's Java runtime code, and implemented in
+ the native .NET Android
+ [runtime](../../src/monodroid/jni/monodroid-glue.cc) (the
+ `MonodroidRuntime::Java_mono_android_Runtime_register` method).
+ Purpose of this method is to prepare a call into the
+ .NET Android managed runtime code, the
+ [`Android.Runtime.JNIEnv::RegisterJniNatives`](../../src/Mono.Android/Android.Runtime/JNIEnv.cs)
+ method.
+ 2. `Android.Runtime.JNIEnv::RegisterJniNatives` is passed name of
+ the managed type for which to register Java methods and uses .NET
+ reflection to load that type, followed by a call to cache the
+ type (via `RegisterType` method in the
+ [`TypeManager`](../../src/Mono.Android/Java.Interop/TypeManager.cs)
+ class) to end with a call to the
+ `Android.Runtime.AndroidTypeManager::RegisterNativeMembers`
+ method.
+ 3. `Android.Runtime.AndroidTypeManager::RegisterNativeMembers`
+ eventually calls the
+ `Java.Interop.JniEnvironment.Types::RegisterNatives` method which
+ first generates a delegate to the native callback method, using
+ `System.Reflection.Emit` (via the
+ [`Android.Runtime.JNINativeWrapper::CreateDelegate`](../../src/Mono.Android/Android.Runtime/JNINativeWrapper.cs)
+ method) and, eventually, invokes Java JNI's `RegisterNatives`
+ function, finally registering the native methods for a managed
+ type.
+
+The `System.Reflection.Emit` sequence mentioned in 3. above is among
+the most costly operations, repeated for each registered method.
+
+Some more information about Java type registration can be found
+[here](https://github.com/xamarin/xamarin-android/wiki/Blueprint#java-type-registration).
+
+## Marshal methods
+
+The goal of marshal methods is to completely bypass the [dynamic
+registration sequence](#dynamic-registration-call-sequence), replacing
+it with native code generated and compiled during application build,
+thus saving on the startup time of the application.
+
+Marshal methods registration mechanism takes advantage of the JNI
+ability to look up implementations of `native` Java methods in actual
+native (shared) libraries. Such symbols must have names that follow a
+set of rules, so that JNI is able to properly locate them (details are
+explained in the [JNI Requirements](#jni-requirements) section below).
+
+To achieve that, the marshal methods mechanism uses a number of
+classes which [generate native](#llvm-ir-code-generation) code and
+[modify assemblies](#assembly-rewriting) that contain the registered
+methods.
+
+Current implementation of the marshal methods classifier recognizes
+the "standard" method registration pattern, using the example of the
+`OnCreate` method shown in [Registration](#registration) above.
+
+The standard pattern consists of:
+
+ * the "connector" method, `GetOnCreate_Landroid_os_Bundle_Handler`
+ above
+ * the delegate backing field, `cb_onCreate_Landroid_os_Bundle_`
+ above
+ * the native callback method, `n_OnCreate_Landroid_os_Bundle_` above
+ * and the virtual target method which dispatches the call to the
+ actual object, `OnCreate` above.
+
+Whenever the classifier's `ShouldBeDynamicallyRegistered` method is
+called, it is passed not only the method's declaring type, but also
+the `Register` attribute instance which it then uses to check whether
+the method being registered conforms to the "standard" registration
+pattern shown above. The connector, native callback methods as well
+as the backing field must be private and static in order for the
+registered method to be considered as a candidate for static
+registration.
+
+Registered methods which don't follow the "standard" pattern will be
+registered dynamically.
+
+### Marshal Methods Java Callable Wrappers registration code
+
+Building on the C# example show in the [Java Callable
+Wrappers](#java-callable-wrappers-jcw) section, the following Java
+code is generated (only the parts relevant to registration are shown):
+
+```java
+public class MainActivity
+ extends androidx.appcompat.app.AppCompatActivity
+{
+ public android.view.View onCreateView (android.view.View p0, java.lang.String p1, android.content.Context p2, android.util.AttributeSet p3)
+ {
+ return n_onCreateView (p0, p1, p2, p3);
+ }
+
+ private native android.view.View n_onCreateView (android.view.View p0, java.lang.String p1, android.content.Context p2, android.util.AttributeSet p3);
+
+ public void onCreate (android.os.Bundle p0)
+ {
+ n_onCreate (p0);
+ }
+
+ private native void n_onCreate (android.os.Bundle p0);
+}
+```
+
+Note that, compared to the code generated for the [dynamic
+registration](#dynamic-java-callable-wrappers-registration-code)
+mechanism, there is no static constructor while the rest
+of the code remains exactly the same.
+
+### Marshal methods C# source code
+
+The marshal methods sections below will all refer to this code
+fragment:
+
+
+```csharp
+public class MainActivity : AppCompatActivity
+{
+ // Connector backing field
+ static Delegate? cb_onCreate_Landroid_os_Bundle_;
+
+ // Connector method
+ static Delegate GetOnCreate_Landroid_os_Bundle_Handler ()
+ {
+ if (cb_onCreate_Landroid_os_Bundle_ == null)
+ cb_onCreate_Landroid_os_Bundle_ = JNINativeWrapper.CreateDelegate ((_JniMarshal_PPL_V) n_OnCreate_Landroid_os_Bundle_);
+ return cb_onCreate_Landroid_os_Bundle_;
+ }
+
+ // Native callback
+ static void n_OnCreate_Landroid_os_Bundle_ (IntPtr jnienv, IntPtr native__this, IntPtr native_savedInstanceState)
+ {
+ var __this = global::Java.Lang.Object.GetObject (jnienv, native__this, JniHandleOwnership.DoNotTransfer)!;
+ var savedInstanceState = global::Java.Lang.Object.GetObject (native_savedInstanceState, JniHandleOwnership.DoNotTransfer);
+ __this.OnCreate (savedInstanceState);
+ }
+
+ // Target method
+ [Register ("onCreate", "(Landroid/os/Bundle;)V", "GetOnCreate_Landroid_os_Bundle_Handler")]
+ protected virtual unsafe void OnCreate (Android.OS.Bundle? savedInstanceState)
+ {
+ const string __id = "onCreate.(Landroid/os/Bundle;)V";
+ try {
+ JniArgumentValue* __args = stackalloc JniArgumentValue [1];
+ __args [0] = new JniArgumentValue ((savedInstanceState == null) ? IntPtr.Zero : ((global::Java.Lang.Object) savedInstanceState).Handle);
+ _members.InstanceMethods.InvokeVirtualVoidMethod (__id, this, __args);
+ } finally {
+ global::System.GC.KeepAlive (savedInstanceState);
+ }
+ }
+}
+```
+
+### JNI requirements
+
+JNI
+[specifies](https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/design.html#resolving_native_method_names)
+a number of rules which govern how the native symbol name is
+constructed, so that a mapping of object-oriented Java code (with its
+package names/namespaces, class names and overloadable methods) into the essentially
+"flat" procedural "namespace" of the lowest common denominator C code.
+
+The precise rules are outlined in the URL above, and their short
+version is as follows:
+
+ * Each symbol starts with the `Java_` prefix
+ * Next follows the mangled (see below) **fully qualified class
+ name**
+ * Next the `_` character serves as a separator before
+ * A mangled **method** name, which is optionally followed by
+ * Double underscore `__` and the mangled method argument signature
+
+"Mangling" is a way of encoding certain characters that are not
+directly representable both in the source code and in the native
+symbol name. The JNI specification allows for direct use of ASCII
+letters (capital and lowercase) and digits, while all the other
+characters are either represented by placeholders or encoded as 16-bit
+hexadecimal Unicode character code (table copied from the JNI
+specification for easier reference):
+
+| Escape sequence | Denotes |
+|-----------------|------------------------------------------|
+| _0XXXX | a Unicode character XXXX, all lower case |
+| _1 | The `_` character |
+| _2 | The `;` character in signatures |
+| _3 | The `[` character in signatures |
+| _ | The `.` or `/` characters |
+
+Generation of JNI symbol names is performed by the
+[`MarshalMethodsNativeAssemblyGenerator`](../../src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsNativeAssemblyGenerator.cs)
+class while generating the native function source code.
+
+JNI supports two forms of the native symbol name, as signalled in the
+bullet list above - a short and a long one. The former is looked up
+first by the Java VM, followed by the latter. The latter needs to be
+used only for overloaded methods, which is what our generator does.
+
+### LLVM IR code generation
+
+[`MarshalMethodsNativeAssemblyGenerator`](../../src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsNativeAssemblyGenerator.cs)
+uses the LLVM IR generator infrastructure to output both data and
+executable code for all the marshal methods wrappers. It is not
+necessary to understand the generated code unless one needs to modify
+it, so this document only shows the equivalent C++ code which can
+serve as a guide to understanding how the marshal method runtime
+invocation works:
+
+```C++
+using get_function_pointer_fn = void(*)(uint32_t mono_image_index, uint32_t class_index, uint32_t method_token, void*& target_ptr);
+
+static get_function_pointer_fn get_function_pointer;
+
+void xamarin_app_init (get_function_pointer_fn fn) noexcept
+{
+ get_function_pointer = fn;
+}
+
+using android_app_activity_on_create_bundle_fn = void (*) (JNIEnv *env, jclass klass, jobject savedInstanceState);
+static android_app_activity_on_create_bundle_fn android_app_activity_on_create_bundle = nullptr;
+
+extern "C" JNIEXPORT void
+JNICALL Java_helloandroid_MainActivity_n_1onCreate__Landroid_os_Bundle_2 (JNIEnv *env, jclass klass, jobject savedInstanceState) noexcept
+{
+ if (android_app_activity_on_create_bundle == nullptr) {
+ get_function_pointer (
+ 16, // mono image index
+ 0, // class index
+ 0x0600055B, // method token
+ reinterpret_cast(android_app_activity_on_create_bundle) // target pointer
+ );
+ }
+
+ android_app_activity_on_create_bundle (env, klass, savedInstanceState);
+}
+```
+
+The `xamarin_app_init` function is output only once and is called by
+the `.NET Android` runtime twice during application startup - once
+to pass `get_function_pointer_fn` which does **not** use any locks (as
+we know that until a certain point during startup we are in a single
+lock, so no data access races can happen) and the other time just
+before handing control over to the MonoVM, to pass pointer to
+`get_function_pointer_fn` which **does** employ locking (since during
+runtime it may very well happen that our generated Java native
+functions will be called from different threads simultaneously).
+
+The `Java_helloandroid_MainActivity_n_1onCreate__Landroid_os_Bundle_2`
+function is a template which is repeated for each Java native
+function, with each function having its own set of arguments and its
+own callback backing field (`android_app_activity_on_create_bundle`
+here).
+
+The `get_function_pointer` function takes as parameters indexes into a
+couple of tables, one for `MonoImage*` pointers and the other for
+`MonoClass*` pointers - both of which are generated by the
+`MarshalMethodsNativeAssemblyGenerator` class at application build
+time and allow for very fast lookup during run time. Target methods
+are retrieved by their token value, within the specified `MonoImage*`
+(essentially a pointer to managed assembly image in memory) and class.
+
+The method identified in such manner, **must** be decorated in the
+managed code with the
+[`[UnmanagedCallersOnly]`](https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.unmanagedcallersonlyattribute?view=net-6.0)
+attribute (see [below](#unmanagedcallersonly-attribute) for more
+details) so that it can be invoked directly, as if it was a native
+method itself, with minimal managed marshaling overhead.
+
+### Assembly rewriting
+
+Please refer to the [C# code fragment](#marshal-methods-c-source-code)
+above in order to understand this section.
+
+Managed assemblies (including `Mono.Android.dll`) which contain Java
+types need to be usable in two contexts: with the "traditional"
+dynamic registration and with marshal methods. Both of these
+mechanisms, however, have different requirements. We cannot assume
+that any assembly (either from .NET Android or a third party nuget)
+will have "marshal methods friendly" code and thus we need to make
+sure that the code meets our requirements.
+
+We do it by reading each relevant assembly and modifying it by
+altering the definition of the native callbacks and removing the
+code that's no longer used by marshal methods. This task is performed
+by the
+[`MarshalMethodsAssemblyRewriter`](../../src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsAssemblyRewriter.cs)
+invoked during application build after all the assemblies are linked
+but **before** type maps are generated (as rewriting **will** alter
+the method and potentially type tokens)
+
+The exact modifications we apply are:
+
+ * Removal of the **connector backing field**
+ * Removal of the **connector method**
+ * Generation of a **native callback wrapper** method, which catches
+ and propagates unhandled exceptions thrown by the native callback
+ or the target method. This method is decorated with the
+ `[UnmanagedCallersOnly]` attribute and called directly from the
+ native code.
+ * Optionally, generate code in the **native callback wrapper** to handle
+ [non-blittable types](#wrappers-for-methods-with-non-blittable-types).
+
+All the modifications are performed with `Mono.Cecil`.
+
+After modifications, the assembly contains equivalent of the following
+C# code for each marshal method:
+
+```csharp
+public class MainActivity : AppCompatActivity
+{
+ // Native callback
+ static void n_OnCreate_Landroid_os_Bundle_ (IntPtr jnienv, IntPtr native__this, IntPtr native_savedInstanceState)
+ {
+ var __this = global::Java.Lang.Object.GetObject (jnienv, native__this, JniHandleOwnership.DoNotTransfer)!;
+ var savedInstanceState = global::Java.Lang.Object.GetObject (native_savedInstanceState, JniHandleOwnership.DoNotTransfer);
+ __this.OnCreate (savedInstanceState);
+ }
+
+ // Native callback exception wrapper
+ [UnmanagedCallersOnly]
+ static void n_OnCreate_Landroid_os_Bundle__mm_wrapper (IntPtr jnienv, IntPtr native__this, IntPtr native_savedInstanceState)
+ {
+ try {
+ n_OnCreate_Landroid_os_Bundle_ (jnienv, native__this, native_savedInstanceState)
+ } catch (Exception ex) {
+ Android.Runtime.AndroidEnvironmentInternal.UnhandledException (ex);
+ }
+ }
+
+ // Target method
+ [Register ("onCreate", "(Landroid/os/Bundle;)V", "GetOnCreate_Landroid_os_Bundle_Handler")]
+ protected virtual unsafe void OnCreate (Android.OS.Bundle? savedInstanceState)
+ {
+ const string __id = "onCreate.(Landroid/os/Bundle;)V";
+ try {
+ JniArgumentValue* __args = stackalloc JniArgumentValue [1];
+ __args [0] = new JniArgumentValue ((savedInstanceState == null) ? IntPtr.Zero : ((global::Java.Lang.Object) savedInstanceState).Handle);
+ _members.InstanceMethods.InvokeVirtualVoidMethod (__id, this, __args);
+ } finally {
+ global::System.GC.KeepAlive (savedInstanceState);
+ }
+ }
+}
+```
+
+#### Wrappers for methods with non-blittable types
+
+The
+[`[UnmanagedCallersOnly]`](https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.unmanagedcallersonlyattribute?view=net-6.0)
+attribute requires that all the argument types as well as the method
+return type are
+[blittable](https://docs.microsoft.com/en-us/dotnet/framework/interop/blittable-and-non-blittable-types).
+
+Among these types is one that's commonly used by the managed classes
+implementing Java methods: `bool`. Currently this is the **only**
+non-blittable type we've encountered in bindings, so at this point it
+is the only one supported by the assembly rewriter.
+
+Whenever we encounter a method with a non-blittable type, we must
+generate a wrapper for it, so that we can decorate it with the
+`[UnmanagedCallersOnly]` attribute. This is easier and less error
+prone than modifying the native callback method's IL stream to
+implement the necessary conversion.
+
+An example of such method is
+`Android.Views.View.IOnTouchListener::OnTouch`:
+
+```csharp
+static bool n_OnTouch_Landroid_view_View_Landroid_view_MotionEvent_ (IntPtr jnienv, IntPtr native__this, IntPtr native_v, IntPtr native_e)
+{
+ var __this = global::Java.Lang.Object.GetObject (jnienv, native__this, JniHandleOwnership.DoNotTransfer)!;
+ var v = global::Java.Lang.Object.GetObject (native_v, JniHandleOwnership.DoNotTransfer);
+ var e = global::Java.Lang.Object.GetObject (native_e, JniHandleOwnership.DoNotTransfer);
+ bool __ret = __this.OnTouch (v, e);
+ return __ret;
+}
+```
+
+As it returns a `bool` value, it needs a wrapper to cast the return
+value properly. Each wrapper method retains the native callback method
+name, but appends the `_mm_wrapper` suffix to it:
+
+```csharp
+static bool n_OnTouch_Landroid_view_View_Landroid_view_MotionEvent_ (IntPtr jnienv, IntPtr native__this, IntPtr native_v, IntPtr native_e)
+{
+ var __this = global::Java.Lang.Object.GetObject (jnienv, native__this, JniHandleOwnership.DoNotTransfer)!;
+ var v = global::Java.Lang.Object.GetObject (native_v, JniHandleOwnership.DoNotTransfer);
+ var e = global::Java.Lang.Object.GetObject (native_e, JniHandleOwnership.DoNotTransfer);
+ bool __ret = __this.OnTouch (v, e);
+ return __ret;
+}
+
+[UnmanagedCallersOnly]
+static byte n_OnTouch_Landroid_view_View_Landroid_view_MotionEvent__mm_wrapper (IntPtr jnienv, IntPtr native__this, IntPtr native_v, IntPtr native_e)
+{
+ try {
+ return n_OnTouch_Landroid_view_View_Landroid_view_MotionEvent_(jnienv, native__this, native_v, native_e) ? 1 : 0;
+ } catch (Exception ex) {
+ Android.Runtime.AndroidEnvironmentInternal.UnhandledException (ex);
+ return default;
+ }
+}
+```
+
+The wrapper's return statement uses the ternary operator to "cast" the
+boolean value to `1` (for `true`) or `0` (for `false`) because the
+value of `bool` across the managed runtime can take a range of values:
+
+ * `0` for `false`
+ * `-1` or `1` for `true`
+ * `!= 0` for true
+
+Since the `bool` type in C# can be 1, 2 or 4 bytes long, we need to
+cast it to some type of a known and static size. The managed type
+`byte` was chosen as it corresponds to the Java/JNI `jboolean` type,
+defined as an unsigned 8-bit type.
+
+Whenever an **argument** value needs to be converted between `byte` and
+`bool`, we generate code that is equivalent of the `argument != 0`
+comparison, for instance for the
+`Android.Views.View.IOnFocusChangeListener::OnFocusChange` method:
+
+```csharp
+[UnmanagedCallersOnly]
+static void n_OnFocusChange_Landroid_view_View_Z (IntPtr jnienv, IntPtr native__this, IntPtr native_v, byte hasFocus)
+{
+ n_OnFocusChange_Landroid_view_View_Z (jnienv, native__this, native_v, hasFocus != 0);
+}
+
+static void n_OnFocusChange_Landroid_view_View_Z (IntPtr jnienv, IntPtr native__this, IntPtr native_v, bool hasFocus)
+{
+ var __this = global::Java.Lang.Object.GetObject (jnienv, native__this, JniHandleOwnership.DoNotTransfer)!;
+ var v = global::Java.Lang.Object.GetObject (native_v, JniHandleOwnership.DoNotTransfer);
+ __this.OnFocusChange (v, hasFocus);
+}
+```
+
+#### UnmanagedCallersOnly attribute
+
+Each marshal methods native callback method is decorated with the
+[`[UnmanagedCallersOnly]`](https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.unmanagedcallersonlyattribute?view=net-6.0)
+attribute, in order for us to be able to invoke the callback directly
+from native code with minimal overhead compared to traditional
+managed-from-native method calls (`mono_runtime_invoke`)
+
+### Marshal Methods Registration call sequence
+
+The sequence described in the [dynamic
+registration sequence](#dynamic-registration-call-sequence) section
+above is completely removed for the marshal methods. What remains
+common for both dynamic and marshal methods registration, is the
+resolution of the native function target done by the Java VM runtime.
+In both cases the method declared in a Java class as `native` is
+looked up by the Java VM when first JIT-ing the code. The difference
+lies in the way this lookup is performed.
+
+Dynamic registration uses the
+[`RegisterNatives`](https://docs.oracle.com/javase/8/docs/technotes/guides/jni/spec/functions.html#RegisterNatives)
+JNI function at the runtime, which stores a pointer to the registered
+method inside the structure which describes a Java class in the Java
+VM.
+
+Marshal methods, however, don't register anything with the JNI,
+instead they rely on the symbol lookup mechanism of the Java VM.
+Whenever a call to `native` Java method is JIT-ed and it is not
+registered previously using the `RegisterNatives` JNI function, Java
+VM will proceed to look for symbols in the process runtime image (e.g.
+using `dlopen` + `dlsym` calls on Unix) and, having found a matching
+symbol, use pointer to it as the target of the `native` Java method
+call.
diff --git a/Xamarin.Android.sln b/Xamarin.Android.sln
index d0f508ac92e..baba28b1734 100644
--- a/Xamarin.Android.sln
+++ b/Xamarin.Android.sln
@@ -92,6 +92,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mono.Android", "src\Mono.An
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mono.Android.Export", "src\Mono.Android.Export\Mono.Android.Export.csproj", "{B8105878-D423-4159-A3E7-028298281EC6}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Mono.Android.Runtime", "src\Mono.Android.Runtime\Mono.Android.Runtime.csproj", "{43564FB3-0F79-4FF4-A2B0-B1637072FF01}"
+EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Android.Build.BaseTasks", "external\xamarin-android-tools\src\Microsoft.Android.Build.BaseTasks\Microsoft.Android.Build.BaseTasks.csproj", "{3DE17662-DCD6-4F49-AF06-D39AACC8649A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Xamarin.Android.Tools.AndroidSdk", "external\xamarin-android-tools\src\Xamarin.Android.Tools.AndroidSdk\Xamarin.Android.Tools.AndroidSdk.csproj", "{E34BCFA0-CAA4-412C-AA1C-75DB8D67D157}"
@@ -296,6 +298,12 @@ Global
{B8105878-D423-4159-A3E7-028298281EC6}.Debug|AnyCPU.Build.0 = Debug|Any CPU
{B8105878-D423-4159-A3E7-028298281EC6}.Release|AnyCPU.ActiveCfg = Release|Any CPU
{B8105878-D423-4159-A3E7-028298281EC6}.Release|AnyCPU.Build.0 = Release|Any CPU
+
+ {43564FB3-0F79-4FF4-A2B0-B1637072FF01}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU
+ {43564FB3-0F79-4FF4-A2B0-B1637072FF01}.Debug|AnyCPU.Build.0 = Debug|Any CPU
+ {43564FB3-0F79-4FF4-A2B0-B1637072FF01}.Release|AnyCPU.ActiveCfg = Release|Any CPU
+ {43564FB3-0F79-4FF4-A2B0-B1637072FF01}.Release|AnyCPU.Build.0 = Release|Any CPU
+
{3DE17662-DCD6-4F49-AF06-D39AACC8649A}.Debug|AnyCPU.ActiveCfg = Debug|Any CPU
{3DE17662-DCD6-4F49-AF06-D39AACC8649A}.Debug|AnyCPU.Build.0 = Debug|Any CPU
{3DE17662-DCD6-4F49-AF06-D39AACC8649A}.Release|AnyCPU.ActiveCfg = Release|Any CPU
@@ -456,6 +464,7 @@ Global
{73DF9E10-E933-4222-B8E1-F4536FFF9FAD} = {864062D3-A415-4A6F-9324-5820237BA058}
{66CF299A-CE95-4131-BCD8-DB66E30C4BF7} = {04E3E11E-B47D-4599-8AFC-50515A95E715}
{B8105878-D423-4159-A3E7-028298281EC6} = {04E3E11E-B47D-4599-8AFC-50515A95E715}
+ {43564FB3-0F79-4FF4-A2B0-B1637072FF01} = {04E3E11E-B47D-4599-8AFC-50515A95E715}
{3DE17662-DCD6-4F49-AF06-D39AACC8649A} = {04E3E11E-B47D-4599-8AFC-50515A95E715}
{E34BCFA0-CAA4-412C-AA1C-75DB8D67D157} = {04E3E11E-B47D-4599-8AFC-50515A95E715}
{1E5501E8-49C1-4659-838D-CC9720C5208F} = {CAB438D8-B0F5-4AF0-BEBD-9E2ADBD7B483}
diff --git a/build-tools/automation/yaml-templates/run-designer-tests.yml b/build-tools/automation/yaml-templates/run-designer-tests.yml
index 796c181577f..f6a63f5adc6 100644
--- a/build-tools/automation/yaml-templates/run-designer-tests.yml
+++ b/build-tools/automation/yaml-templates/run-designer-tests.yml
@@ -62,7 +62,10 @@ steps:
displayName: 'Copy binlogs'
inputs:
sourceFolder: ${{ parameters.designerSourcePath }}/Xamarin.Designer.Android
- contents: '**/*.binlog'
+ contents: |
+ **/*.binlog
+ **/hs*.log
+ **/hs*.mdmp
targetFolder: $(Build.ArtifactStagingDirectory)/designer-binlogs
overWrite: true
flattenFolders: true
diff --git a/build-tools/create-packs/Microsoft.Android.Ref.proj b/build-tools/create-packs/Microsoft.Android.Ref.proj
index a4d71c2decb..39d0b2e6c11 100644
--- a/build-tools/create-packs/Microsoft.Android.Ref.proj
+++ b/build-tools/create-packs/Microsoft.Android.Ref.proj
@@ -33,6 +33,7 @@ by projects that use the Microsoft.Android framework in .NET 6+.
<_AndroidRefPackAssemblies Include="$(JavaInteropSourceDirectory)\bin\$(Configuration)-net7.0\ref\Java.Interop.dll" />
<_AndroidRefPackAssemblies Include="$(_MonoAndroidNETDefaultOutDir)ref\Mono.Android.dll" />
+ <_AndroidRefPackAssemblies Include="$(_MonoAndroidNETDefaultOutDir)ref\Mono.Android.Runtime.dll" />
<_AndroidRefPackAssemblies Include="$(_MonoAndroidNETOutputRoot)$(AndroidLatestStableApiLevel)\ref\Mono.Android.Export.dll" />
diff --git a/build-tools/create-packs/Microsoft.Android.Runtime.proj b/build-tools/create-packs/Microsoft.Android.Runtime.proj
index aef96f9dfc8..8982eb6f911 100644
--- a/build-tools/create-packs/Microsoft.Android.Runtime.proj
+++ b/build-tools/create-packs/Microsoft.Android.Runtime.proj
@@ -35,6 +35,7 @@ projects that use the Microsoft.Android framework in .NET 6+.
<_AndroidRuntimePackAssemblies Include="$(_MonoAndroidNETDefaultOutDir)Java.Interop.dll" />
<_AndroidRuntimePackAssemblies Include="$(_MonoAndroidNETDefaultOutDir)Mono.Android.dll" />
+ <_AndroidRuntimePackAssemblies Include="$(_MonoAndroidNETDefaultOutDir)Mono.Android.Runtime.dll" />
<_AndroidRuntimePackAssemblies Include="$(_MonoAndroidNETOutputRoot)$(AndroidLatestStableApiLevel)\Mono.Android.Export.dll" />
<_AndroidRuntimePackAssets Include="$(MicrosoftAndroidSdkOutDir)lib\$(AndroidRID)\libmono-android.debug.so" />
diff --git a/build-tools/scripts/JavaCallableWrappers.targets b/build-tools/scripts/JavaCallableWrappers.targets
index 21d9bcd231c..bd0e0bbf117 100644
--- a/build-tools/scripts/JavaCallableWrappers.targets
+++ b/build-tools/scripts/JavaCallableWrappers.targets
@@ -23,7 +23,6 @@
/>
<_JavaSources Include="$(IntermediateOutputPath)jcw\src\**\*.java" />
- <_JavaSources Include="$(JavaInteropSourceDirectory)\src\Java.Interop\java\com\xamarin\**\*.java" />
+
+
+
diff --git a/src/Microsoft.Android.Sdk.ILLink/PreserveLists/Mono.Android.xml b/src/Microsoft.Android.Sdk.ILLink/PreserveLists/Mono.Android.xml
index 368771253da..1f27591ef8f 100644
--- a/src/Microsoft.Android.Sdk.ILLink/PreserveLists/Mono.Android.xml
+++ b/src/Microsoft.Android.Sdk.ILLink/PreserveLists/Mono.Android.xml
@@ -7,8 +7,10 @@
-
+
+
+
@@ -16,8 +18,6 @@
-
-
diff --git a/src/Mono.Android.Runtime/Android.Runtime/AndroidEnvironmentInternal.cs b/src/Mono.Android.Runtime/Android.Runtime/AndroidEnvironmentInternal.cs
new file mode 100644
index 00000000000..198e6c92423
--- /dev/null
+++ b/src/Mono.Android.Runtime/Android.Runtime/AndroidEnvironmentInternal.cs
@@ -0,0 +1,18 @@
+using System;
+
+namespace Android.Runtime
+{
+ internal static class AndroidEnvironmentInternal
+ {
+ internal static Action? UnhandledExceptionHandler;
+
+ internal static void UnhandledException (Exception e)
+ {
+ if (UnhandledExceptionHandler == null) {
+ return;
+ }
+
+ UnhandledExceptionHandler (e);
+ }
+ }
+}
diff --git a/src/Mono.Android.Runtime/Mono.Android.Runtime.csproj b/src/Mono.Android.Runtime/Mono.Android.Runtime.csproj
new file mode 100644
index 00000000000..62e90145971
--- /dev/null
+++ b/src/Mono.Android.Runtime/Mono.Android.Runtime.csproj
@@ -0,0 +1,62 @@
+
+
+
+
+
+
+
+ $(DotNetTargetFramework)
+ true
+ false
+ ..\..\product.snk
+ false
+ true
+ false
+ false
+ 10
+ true
+ enable
+ true
+ true
+
+
+ $(NoWarn);CS0169;CS0414;CS0649
+
+ $(DefineConstants);INSIDE_MONO_ANDROID_RUNTIME;JAVA_INTEROP
+
+
+
+ $(_MonoAndroidNETDefaultOutDir)
+
+
+
+
+
+
+
+
+
+
+
+
+ $(BuildDependsOn);
+ _CopyToPackDirs;
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Mono.Android.Runtime/Mono.Android.Runtime.targets b/src/Mono.Android.Runtime/Mono.Android.Runtime.targets
new file mode 100644
index 00000000000..0b00eed9b47
--- /dev/null
+++ b/src/Mono.Android.Runtime/Mono.Android.Runtime.targets
@@ -0,0 +1,52 @@
+
+
+
+
+
+ <_PackageVersion>$(ProductVersion)
+ <_PackageVersionBuild>$(XAVersionCommitCount)
+
+
+ <_PackageVersion>$(AndroidPackVersion)
+ <_PackageVersionBuild>$(PackVersionCommitCount)
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/Mono.Android.Runtime/Properties/AssemblyInfo.cs.in b/src/Mono.Android.Runtime/Properties/AssemblyInfo.cs.in
new file mode 100644
index 00000000000..6c392e8130f
--- /dev/null
+++ b/src/Mono.Android.Runtime/Properties/AssemblyInfo.cs.in
@@ -0,0 +1,21 @@
+//
+// AssemblyInfo.cs.in
+//
+// Authors:
+// Jonathan Pryor (jonp@xamarin.com)
+//
+// Copyright 2014 Xamarin, Inc.
+// Copyright 2016 Microsoft Corporation.
+
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.Versioning;
+
+[assembly: AssemblyInformationalVersion ("@PACKAGE_VERSION@.@PACKAGE_VERSION_BUILD@; git-rev-head:@PACKAGE_HEAD_REV@; git-branch:@PACKAGE_HEAD_BRANCH@")]
+[assembly: AssemblyTitle ("Mono.Android.Runtime.dll")]
+[assembly: AssemblyProduct ("Xamarin.Android")]
+[assembly: AssemblyCompany ("Microsoft Corporation")]
+[assembly: TargetPlatform("Android@API_LEVEL@.0")]
+[assembly: SupportedOSPlatform("Android@MIN_API_LEVEL@.0")]
+
+[assembly: InternalsVisibleTo("Mono.Android, PublicKey=0024000004800000940000000602000000240000525341310004000011000000438ac2a5acfbf16cbd2b2b47a62762f273df9cb2795ceccdf77d10bf508e69e7a362ea7a45455bbf3ac955e1f2e2814f144e5d817efc4c6502cc012df310783348304e3ae38573c6d658c234025821fda87a0be8a0d504df564e2c93b2b878925f42503e9d54dfef9f9586d9e6f38a305769587b1de01f6c0410328b2c9733db")]
diff --git a/src/Mono.Android/Android.App/SyncContext.cs b/src/Mono.Android/Android.App/SyncContext.cs
index ac0fe30093a..6c3daa83c7d 100644
--- a/src/Mono.Android/Android.App/SyncContext.cs
+++ b/src/Mono.Android/Android.App/SyncContext.cs
@@ -18,7 +18,7 @@ static bool EnsureLooper ([NotNullWhen (true)]Looper? looper, SendOrPostCallback
{
if (looper == null) {
var message = $"No Android message loop is available. Skipping invocation of `{d.Method.DeclaringType?.FullName}.{d.Method.Name}`!";
- if (JNIEnv.IsRunningOnDesktop)
+ if (JNIEnvInit.IsRunningOnDesktop)
message += " Using `await` when running on the Android Designer is not currently supported. Please use the `View.IsInEditMode` property.";
Logger.Log (LogLevel.Error, "monodroid-synccontext", message);
return false;
diff --git a/src/Mono.Android/Android.Runtime/AndroidEnvironment.cs b/src/Mono.Android/Android.Runtime/AndroidEnvironment.cs
index ea5368a2f51..dda2dea521e 100644
--- a/src/Mono.Android/Android.Runtime/AndroidEnvironment.cs
+++ b/src/Mono.Android/Android.Runtime/AndroidEnvironment.cs
@@ -264,17 +264,14 @@ static void GetDisplayDPI (out float x_dpi, out float y_dpi)
//
static string GetDefaultTimeZone ()
{
- IntPtr id = _monodroid_timezone_get_default_id ();
+ IntPtr id = RuntimeNativeMethods._monodroid_timezone_get_default_id ();
try {
return Marshal.PtrToStringAnsi (id)!;
} finally {
- JNIEnv.monodroid_free (id);
+ RuntimeNativeMethods.monodroid_free (id);
}
}
- [DllImport (AndroidRuntime.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr _monodroid_timezone_get_default_id ();
-
// This is invoked by
// mscorlib.dll!System.AndroidPlatform.GetDefaultSyncContext()
// DO NOT REMOVE
@@ -293,35 +290,26 @@ static string GetDefaultTimeZone ()
// These are invoked by
// System.dll!System.AndroidPlatform.getifaddrs
// DO NOT REMOVE
- [DllImport (AndroidRuntime.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
- static extern int _monodroid_getifaddrs (out IntPtr ifap);
-
static int GetInterfaceAddresses (out IntPtr ifap)
{
- return _monodroid_getifaddrs (out ifap);
+ return RuntimeNativeMethods._monodroid_getifaddrs (out ifap);
}
// These are invoked by
// System.dll!System.AndroidPlatform.freeifaddrs
// DO NOT REMOVE
- [DllImport (AndroidRuntime.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
- static extern void _monodroid_freeifaddrs (IntPtr ifap);
-
static void FreeInterfaceAddresses (IntPtr ifap)
{
- _monodroid_freeifaddrs (ifap);
+ RuntimeNativeMethods._monodroid_freeifaddrs (ifap);
}
- [DllImport (AndroidRuntime.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
- static extern void _monodroid_detect_cpu_and_architecture (ref ushort built_for_cpu, ref ushort running_on_cpu, ref byte is64bit);
-
static void DetectCPUAndArchitecture (out ushort builtForCPU, out ushort runningOnCPU, out bool is64bit)
{
ushort built_for_cpu = 0;
ushort running_on_cpu = 0;
byte _is64bit = 0;
- _monodroid_detect_cpu_and_architecture (ref built_for_cpu, ref running_on_cpu, ref _is64bit);
+ RuntimeNativeMethods._monodroid_detect_cpu_and_architecture (ref built_for_cpu, ref running_on_cpu, ref _is64bit);
builtForCPU = built_for_cpu;
runningOnCPU = running_on_cpu;
is64bit = _is64bit != 0;
diff --git a/src/Mono.Android/Android.Runtime/AndroidRuntime.cs b/src/Mono.Android/Android.Runtime/AndroidRuntime.cs
index 3bfffb9001b..fa98adf6337 100644
--- a/src/Mono.Android/Android.Runtime/AndroidRuntime.cs
+++ b/src/Mono.Android/Android.Runtime/AndroidRuntime.cs
@@ -17,7 +17,7 @@ namespace Android.Runtime {
class AndroidRuntime : JniRuntime {
- public const string InternalDllName = "xa-internal-api";
+ public const string InternalDllName = RuntimeConstants.InternalDllName;
internal AndroidRuntime (IntPtr jnienv,
IntPtr vm,
@@ -32,6 +32,11 @@ internal AndroidRuntime (IntPtr jnienv,
classLoader_loadClass,
jniAddNativeMethodRegistrationAttributePresent))
{
+#if NETCOREAPP
+ // This is not ideal, but we need to set this while the runtime is initializing but we can't do it directly from the `JNIEnvInit.Initialize` method, since
+ // it lives in an assembly that does not reference Mono.Android. So we do it here, because this class is instantiated by JNIEnvInit.Initialize.
+ AndroidEnvironmentInternal.UnhandledExceptionHandler = AndroidEnvironment.UnhandledException;
+#endif
}
public override void FailFast (string? message)
@@ -54,7 +59,7 @@ public override string GetCurrentManagedThreadStackTrace (int skipFrames, bool f
{
if (!reference.IsValid)
return null;
- var peeked = JNIEnv.AndroidValueManager?.PeekPeer (reference);
+ var peeked = JNIEnvInit.AndroidValueManager?.PeekPeer (reference);
var peekedExc = peeked as Exception;
if (peekedExc == null) {
var throwable = Java.Lang.Object.GetObject (reference.Handle, JniHandleOwnership.DoNotTransfer);
@@ -62,7 +67,7 @@ public override string GetCurrentManagedThreadStackTrace (int skipFrames, bool f
return throwable;
}
JniObjectReference.Dispose (ref reference, options);
- var unwrapped = JNIEnv.AndroidValueManager?.UnboxException (peeked!);
+ var unwrapped = JNIEnvInit.AndroidValueManager?.UnboxException (peeked!);
if (unwrapped != null) {
return unwrapped;
}
@@ -102,20 +107,12 @@ public AndroidRuntimeOptions (IntPtr jnienv,
}
class AndroidObjectReferenceManager : JniRuntime.JniObjectReferenceManager {
-
- [DllImport (AndroidRuntime.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
- static extern int _monodroid_gref_get ();
-
public override int GlobalReferenceCount {
- get {return _monodroid_gref_get ();}
+ get {return RuntimeNativeMethods._monodroid_gref_get ();}
}
-
- [DllImport (AndroidRuntime.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
- static extern int _monodroid_weak_gref_get ();
-
public override int WeakGlobalReferenceCount {
- get {return _monodroid_weak_gref_get ();}
+ get {return RuntimeNativeMethods._monodroid_weak_gref_get ();}
}
public override JniObjectReference CreateLocalReference (JniObjectReference value, ref int localReferenceCount)
@@ -126,7 +123,7 @@ public override JniObjectReference CreateLocalReference (JniObjectReference valu
var tname = Thread.CurrentThread.Name;
var tid = Thread.CurrentThread.ManagedThreadId;;
var from = new StringBuilder (new StackTrace (true).ToString ());
- JNIEnv._monodroid_lref_log_new (localReferenceCount, r.Handle, (byte) 'L', tname, tid, from, 1);
+ RuntimeNativeMethods._monodroid_lref_log_new (localReferenceCount, r.Handle, (byte) 'L', tname, tid, from, 1);
}
return r;
@@ -138,7 +135,7 @@ public override void DeleteLocalReference (ref JniObjectReference value, ref int
var tname = Thread.CurrentThread.Name;
var tid = Thread.CurrentThread.ManagedThreadId;;
var from = new StringBuilder (new StackTrace (true).ToString ());
- JNIEnv._monodroid_lref_log_delete (localReferenceCount-1, value.Handle, (byte) 'L', tname, tid, from, 1);
+ RuntimeNativeMethods._monodroid_lref_log_delete (localReferenceCount-1, value.Handle, (byte) 'L', tname, tid, from, 1);
}
base.DeleteLocalReference (ref value, ref localReferenceCount);
}
@@ -150,7 +147,7 @@ public override void CreatedLocalReference (JniObjectReference value, ref int lo
var tname = Thread.CurrentThread.Name;
var tid = Thread.CurrentThread.ManagedThreadId;;
var from = new StringBuilder (new StackTrace (true).ToString ());
- JNIEnv._monodroid_lref_log_new (localReferenceCount, value.Handle, (byte) 'L', tname, tid, from, 1);
+ RuntimeNativeMethods._monodroid_lref_log_new (localReferenceCount, value.Handle, (byte) 'L', tname, tid, from, 1);
}
}
@@ -161,14 +158,14 @@ public override IntPtr ReleaseLocalReference (ref JniObjectReference value, ref
var tname = Thread.CurrentThread.Name;
var tid = Thread.CurrentThread.ManagedThreadId;;
var from = new StringBuilder (new StackTrace (true).ToString ());
- JNIEnv._monodroid_lref_log_delete (localReferenceCount-1, value.Handle, (byte) 'L', tname, tid, from, 1);
+ RuntimeNativeMethods._monodroid_lref_log_delete (localReferenceCount-1, value.Handle, (byte) 'L', tname, tid, from, 1);
}
return r;
}
public override void WriteGlobalReferenceLine (string format, params object?[] args)
{
- JNIEnv._monodroid_gref_log (string.Format (format, args));
+ RuntimeNativeMethods._monodroid_gref_log (string.Format (format, args));
}
public override JniObjectReference CreateGlobalReference (JniObjectReference value)
@@ -181,8 +178,8 @@ public override JniObjectReference CreateGlobalReference (JniObjectReference val
var tname = log ? Thread.CurrentThread.Name : null;
var tid = log ? Thread.CurrentThread.ManagedThreadId : 0;
var from = log ? new StringBuilder (new StackTrace (true).ToString ()) : null;
- int gc = JNIEnv._monodroid_gref_log_new (value.Handle, ctype, r.Handle, ntype, tname, tid, from, 1);
- if (gc >= JNIEnv.gref_gc_threshold) {
+ int gc = RuntimeNativeMethods._monodroid_gref_log_new (value.Handle, ctype, r.Handle, ntype, tname, tid, from, 1);
+ if (gc >= JNIEnvInit.gref_gc_threshold) {
Logger.Log (LogLevel.Info, "monodroid-gc", gc + " outstanding GREFs. Performing a full GC!");
System.GC.Collect ();
}
@@ -208,7 +205,7 @@ public override void DeleteGlobalReference (ref JniObjectReference value)
var tname = log ? Thread.CurrentThread.Name : null;
var tid = log ? Thread.CurrentThread.ManagedThreadId : 0;
var from = log ? new StringBuilder (new StackTrace (true).ToString ()) : null;
- JNIEnv._monodroid_gref_log_delete (value.Handle, ctype, tname, tid, from, 1);
+ RuntimeNativeMethods._monodroid_gref_log_delete (value.Handle, ctype, tname, tid, from, 1);
base.DeleteGlobalReference (ref value);
}
@@ -223,7 +220,7 @@ public override JniObjectReference CreateWeakGlobalReference (JniObjectReference
var tname = log ? Thread.CurrentThread.Name : null;
var tid = log ? Thread.CurrentThread.ManagedThreadId : 0;
var from = log ? new StringBuilder (new StackTrace (true).ToString ()) : null;
- JNIEnv._monodroid_weak_gref_new (value.Handle, ctype, r.Handle, ntype, tname, tid, from, 1);
+ RuntimeNativeMethods._monodroid_weak_gref_new (value.Handle, ctype, r.Handle, ntype, tname, tid, from, 1);
return r;
}
@@ -235,7 +232,7 @@ public override void DeleteWeakGlobalReference (ref JniObjectReference value)
var tname = log ? Thread.CurrentThread.Name : null;
var tid = log ? Thread.CurrentThread.ManagedThreadId : 0;
var from = log ? new StringBuilder (new StackTrace (true).ToString ()) : null;
- JNIEnv._monodroid_weak_gref_delete (value.Handle, ctype, tname, tid, from, 1);
+ RuntimeNativeMethods._monodroid_weak_gref_delete (value.Handle, ctype, tname, tid, from, 1);
base.DeleteWeakGlobalReference (ref value);
}
@@ -276,7 +273,7 @@ protected override IEnumerable GetTypesForSimpleReference (string jniSimpl
#endif // NET
j;
}
- if (JNIEnv.IsRunningOnDesktop) {
+ if (JNIEnvInit.IsRunningOnDesktop) {
return JavaNativeTypeManager.ToJniName (type);
}
return null;
@@ -288,7 +285,7 @@ protected override IEnumerable GetSimpleReferences (Type type)
#if NET
j = GetReplacementTypeCore (j) ?? j;
#endif // NET
- if (JNIEnv.IsRunningOnDesktop) {
+ if (JNIEnvInit.IsRunningOnDesktop) {
string? d = JavaNativeTypeManager.ToJniName (type);
if (j != null && d != null) {
return new[]{j, d};
@@ -323,16 +320,13 @@ protected override IEnumerable GetSimpleReferences (Type type)
};
}
- [DllImport (AndroidRuntime.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr _monodroid_lookup_replacement_type (string jniSimpleReference);
-
protected override string? GetReplacementTypeCore (string jniSimpleReference)
{
- if (!JNIEnv.jniRemappingInUse) {
+ if (!JNIEnvInit.jniRemappingInUse) {
return null;
}
- IntPtr ret = _monodroid_lookup_replacement_type (jniSimpleReference);
+ IntPtr ret = RuntimeNativeMethods._monodroid_lookup_replacement_type (jniSimpleReference);
if (ret == IntPtr.Zero) {
return null;
}
@@ -340,16 +334,13 @@ protected override IEnumerable GetSimpleReferences (Type type)
return Marshal.PtrToStringAnsi (ret);
}
- [DllImport (AndroidRuntime.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
- static extern IntPtr _monodroid_lookup_replacement_method_info (string jniSourceType, string jniMethodName, string jniMethodSignature);
-
protected override JniRuntime.ReplacementMethodInfo? GetReplacementMethodInfoCore (string jniSourceType, string jniMethodName, string jniMethodSignature)
{
- if (!JNIEnv.jniRemappingInUse) {
+ if (!JNIEnvInit.jniRemappingInUse) {
return null;
}
- IntPtr retInfo = _monodroid_lookup_replacement_method_info (jniSourceType, jniMethodName, jniMethodSignature);
+ IntPtr retInfo = RuntimeNativeMethods._monodroid_lookup_replacement_method_info (jniSourceType, jniMethodName, jniMethodSignature);
if (retInfo == IntPtr.Zero) {
return null;
}
@@ -473,42 +464,22 @@ static bool CallRegisterMethodByIndex (JniNativeMethodRegistrationArguments argu
public override void RegisterNativeMembers (JniType nativeClass, Type type, string? methods) =>
RegisterNativeMembers (nativeClass, type, methods.AsSpan ());
-#if ENABLE_MARSHAL_METHODS
- // Temporary hack, see comments in RegisterNativeMembers below
- static readonly Dictionary dynamicRegistrationMethods = new Dictionary (StringComparer.Ordinal) {
- {"Android.Views.View+IOnLayoutChangeListenerImplementor", new string[] { "GetOnLayoutChange_Landroid_view_View_IIIIIIIIHandler" }},
- {"Android.Views.View+IOnLayoutChangeListenerInvoker", new string[] { "GetOnLayoutChange_Landroid_view_View_IIIIIIIIHandler" }},
- {"Java.Interop.TypeManager+JavaTypeManager", new string[] { "GetActivateHandler" }},
- };
-#endif
-
public void RegisterNativeMembers (JniType nativeClass, Type type, ReadOnlySpan methods)
{
-#if ENABLE_MARSHAL_METHODS
- Logger.Log (LogLevel.Info, "monodroid-mm", $"RegisterNativeMembers ('{nativeClass?.Name}', '{type?.FullName}', '{methods.ToString ()}')");
- Logger.Log (LogLevel.Info, "monodroid-mm", "RegisterNativeMembers called from:");
- var st = new StackTrace (true);
- Logger.Log (LogLevel.Info, "monodroid-mm", st.ToString ());
-
- if (methods.IsEmpty) {
- Logger.Log (LogLevel.Info, "monodroid-mm", "No methods to register, returning");
- return;
- }
-#endif
try {
- if (FastRegisterNativeMembers (nativeClass, type, methods))
- return;
-
if (methods.IsEmpty) {
if (jniAddNativeMethodRegistrationAttributePresent)
base.RegisterNativeMembers (nativeClass, type, methods.ToString ());
return;
+ } else if (FastRegisterNativeMembers (nativeClass, type, methods)) {
+ return;
}
int methodCount = CountMethods (methods);
if (methodCount < 1) {
- if (jniAddNativeMethodRegistrationAttributePresent)
+ if (jniAddNativeMethodRegistrationAttributePresent) {
base.RegisterNativeMembers (nativeClass, type, methods.ToString ());
+ }
return;
}
@@ -517,9 +488,8 @@ public void RegisterNativeMembers (JniType nativeClass, Type type, ReadOnlySpan<
MethodInfo []? typeMethods = null;
ReadOnlySpan methodsSpan = methods;
-#if ENABLE_MARSHAL_METHODS
bool needToRegisterNatives = false;
-#endif
+
while (!methodsSpan.IsEmpty) {
int newLineIndex = methodsSpan.IndexOf ('\n');
@@ -545,9 +515,7 @@ public void RegisterNativeMembers (JniType nativeClass, Type type, ReadOnlySpan<
if (minfo == null)
throw new InvalidOperationException (String.Format ("Specified managed method '{0}' was not found. Signature: {1}", mname.ToString (), signature.ToString ()));
callback = CreateDynamicCallback (minfo);
-#if ENABLE_MARSHAL_METHODS
needToRegisterNatives = true;
-#endif
} else {
Type callbackDeclaringType = type;
if (!callbackDeclaringTypeString.IsEmpty) {
@@ -556,45 +524,14 @@ public void RegisterNativeMembers (JniType nativeClass, Type type, ReadOnlySpan<
while (callbackDeclaringType.ContainsGenericParameters) {
callbackDeclaringType = callbackDeclaringType.BaseType!;
}
-#if ENABLE_MARSHAL_METHODS
- // TODO: this is temporary hack, it needs a full fledged registration mechanism for methods like these (that is, ones which
- // aren't registered with [Register] but are baked into Mono.Android's managed and Java code)
- bool createCallback = false;
- string declaringTypeName = callbackDeclaringType.FullName;
- string callbackName = callbackString.ToString ();
-
- foreach (var kvp in dynamicRegistrationMethods) {
- string dynamicTypeName = kvp.Key;
-
- foreach (string dynamicCallbackMethodName in kvp.Value) {
- if (ShouldRegisterDynamically (declaringTypeName, callbackName, dynamicTypeName, dynamicCallbackMethodName)) {
- createCallback = true;
- break;
- }
- }
- if (createCallback) {
- break;
- }
- }
-
- if (createCallback) {
- Logger.Log (LogLevel.Info, "monodroid-mm", $" creating delegate for: '{callbackString.ToString()}' in type {callbackDeclaringType.FullName}");
-#endif
- GetCallbackHandler connector = (GetCallbackHandler) Delegate.CreateDelegate (typeof (GetCallbackHandler),
- callbackDeclaringType, callbackString.ToString ());
- callback = connector ();
-#if ENABLE_MARSHAL_METHODS
- } else {
- Logger.Log (LogLevel.Warn, "monodroid-mm", $" would try to create delegate for: '{callbackString.ToString()}' in type {callbackDeclaringType.FullName}");
- }
-#endif
+ GetCallbackHandler connector = (GetCallbackHandler) Delegate.CreateDelegate (typeof (GetCallbackHandler),
+ callbackDeclaringType, callbackString.ToString ());
+ callback = connector ();
}
if (callback != null) {
-#if ENABLE_MARSHAL_METHODS
needToRegisterNatives = true;
-#endif
natives [nativesIndex++] = new JniNativeMethodRegistration (name.ToString (), signature.ToString (), callback);
}
}
@@ -602,18 +539,13 @@ public void RegisterNativeMembers (JniType nativeClass, Type type, ReadOnlySpan<
methodsSpan = newLineIndex != -1 ? methodsSpan.Slice (newLineIndex + 1) : default;
}
-#if ENABLE_MARSHAL_METHODS
if (needToRegisterNatives) {
-#endif
JniEnvironment.Types.RegisterNatives (nativeClass.PeerReference, natives, nativesIndex);
-#if ENABLE_MARSHAL_METHODS
}
-#endif
} catch (Exception e) {
JniEnvironment.Runtime.RaisePendingException (e);
}
-#if ENABLE_MARSHAL_METHODS
bool ShouldRegisterDynamically (string callbackTypeName, string callbackString, string typeName, string callbackName)
{
if (String.Compare (typeName, callbackTypeName, StringComparison.Ordinal) != 0) {
@@ -622,7 +554,6 @@ bool ShouldRegisterDynamically (string callbackTypeName, string callbackString,
return String.Compare (callbackName, callbackString, StringComparison.Ordinal) == 0;
}
-#endif
}
static int CountMethods (ReadOnlySpan methodsSpan)
@@ -665,7 +596,7 @@ class AndroidValueManager : JniRuntime.JniValueManager {
public override void WaitForGCBridgeProcessing ()
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
}
public override IJavaPeerable? CreatePeer (ref JniObjectReference reference, JniObjectReferenceOptions options, Type? targetType)
@@ -686,7 +617,7 @@ public override void AddPeer (IJavaPeerable value)
throw new ArgumentException ("Must have a valid JNI object reference!", nameof (value));
var reference = value.PeerReference;
- var hash = JNIEnv.IdentityHash! (reference.Handle);
+ var hash = JNIEnv.IdentityHash (reference.Handle);
AddPeer (value, reference, hash);
}
@@ -749,14 +680,14 @@ internal void AddPeer (IJavaPeerable value, IntPtr handle, JniHandleOwnership tr
if (handleField == IntPtr.Zero)
throw new InvalidOperationException ("Unable to allocate Global Reference for object '" + value.ToString () + "'!");
- IntPtr hash = JNIEnv.IdentityHash! (handleField);
+ IntPtr hash = JNIEnv.IdentityHash (handleField);
value.SetJniIdentityHashCode ((int) hash);
if ((transfer & JniHandleOwnership.DoNotRegister) == 0) {
AddPeer (value, new JniObjectReference (handleField, JniObjectReferenceType.Global), hash);
}
if (Logger.LogGlobalRef) {
- JNIEnv._monodroid_gref_log ("handle 0x" + handleField.ToString ("x") +
+ RuntimeNativeMethods._monodroid_gref_log ("handle 0x" + handleField.ToString ("x") +
"; key_handle 0x" + hash.ToString ("x") +
": Java Type: `" + JNIEnv.GetClassNameFromInstance (handleField) + "`; " +
"MCW type: `" + value.GetType ().FullName + "`\n");
@@ -807,7 +738,7 @@ public override void RemovePeer (IJavaPeerable value)
// Likely an idempotent DIspose(); ignore.
return;
}
- var hash = JNIEnv.IdentityHash! (reference.Handle);
+ var hash = JNIEnv.IdentityHash (reference.Handle);
RemovePeer (value, hash);
}
@@ -841,7 +772,7 @@ internal void RemovePeer (IJavaPeerable value, IntPtr hash)
if (!reference.IsValid)
return null;
- var hash = JNIEnv.IdentityHash! (reference.Handle);
+ var hash = JNIEnv.IdentityHash (reference.Handle);
lock (instances) {
if (instances.TryGetValue (hash, out var targets)) {
for (int i = targets.Count - 1; i >= 0; i--) {
@@ -894,7 +825,7 @@ public override void FinalizePeer (IJavaPeerable value)
throw new ArgumentNullException (nameof (value));
if (Logger.LogGlobalRef) {
- JNIEnv._monodroid_gref_log ($"Finalizing handle {value.PeerReference}\n");
+ RuntimeNativeMethods._monodroid_gref_log ($"Finalizing handle {value.PeerReference}\n");
}
// FIXME: need hash cleanup mechanism.
diff --git a/src/Mono.Android/Android.Runtime/AndroidRuntimeInternal.cs b/src/Mono.Android/Android.Runtime/AndroidRuntimeInternal.cs
new file mode 100644
index 00000000000..37df6ef353a
--- /dev/null
+++ b/src/Mono.Android/Android.Runtime/AndroidRuntimeInternal.cs
@@ -0,0 +1,41 @@
+#if !NETCOREAPP || INSIDE_MONO_ANDROID_RUNTIME
+using System;
+using System.Reflection;
+
+namespace Android.Runtime
+{
+ public static class AndroidRuntimeInternal
+ {
+ internal static MethodInfo? mono_unhandled_exception_method = null;
+#if NETCOREAPP
+ internal static Action mono_unhandled_exception = RuntimeNativeMethods.monodroid_debugger_unhandled_exception;
+#else
+ internal static Action? mono_unhandled_exception = null;
+#endif
+
+#pragma warning disable CS0649 // Field is never assigned to. This field is assigned from monodroid-glue.cc.
+ internal static volatile bool BridgeProcessing; // = false
+#pragma warning restore CS0649 // Field is never assigned to.
+
+ internal static void InitializeUnhandledExceptionMethod ()
+ {
+ if (mono_unhandled_exception == null) {
+ mono_unhandled_exception_method = typeof (System.Diagnostics.Debugger)
+ .GetMethod ("Mono_UnhandledException", BindingFlags.NonPublic | BindingFlags.Static);
+ if (mono_unhandled_exception_method != null)
+ mono_unhandled_exception = (Action) Delegate.CreateDelegate (typeof(Action), mono_unhandled_exception_method);
+ }
+ if (mono_unhandled_exception_method == null && mono_unhandled_exception != null) {
+ mono_unhandled_exception_method = mono_unhandled_exception.Method;
+ }
+ }
+
+ public static void WaitForBridgeProcessing ()
+ {
+ if (!BridgeProcessing)
+ return;
+ RuntimeNativeMethods._monodroid_gc_wait_for_bridge_processing ();
+ }
+ }
+}
+#endif // !NETCOREAPP || INSIDE_MONO_ANDROID_RUNTIME
diff --git a/src/Mono.Android/Android.Runtime/BoundExceptionType.cs b/src/Mono.Android/Android.Runtime/BoundExceptionType.cs
index 442f05ea7a1..f760ef89530 100644
--- a/src/Mono.Android/Android.Runtime/BoundExceptionType.cs
+++ b/src/Mono.Android/Android.Runtime/BoundExceptionType.cs
@@ -1,7 +1,7 @@
namespace Android.Runtime
{
// Keep the enum values in sync with those in src/monodroid/jni/monodroid-glue-internal.hh
- enum BoundExceptionType : byte
+ internal enum BoundExceptionType : byte
{
System = 0x00,
Java = 0x01,
diff --git a/src/Mono.Android/Android.Runtime/JNIEnv.cs b/src/Mono.Android/Android.Runtime/JNIEnv.cs
index d9ef52d1255..46c8d75313c 100644
--- a/src/Mono.Android/Android.Runtime/JNIEnv.cs
+++ b/src/Mono.Android/Android.Runtime/JNIEnv.cs
@@ -14,74 +14,15 @@
using System.Diagnostics.CodeAnalysis;
namespace Android.Runtime {
-#pragma warning disable 0649
- struct JnienvInitializeArgs {
- public IntPtr javaVm;
- public IntPtr env;
- public IntPtr grefLoader;
- public IntPtr Loader_loadClass;
- public IntPtr grefClass;
- public IntPtr Class_forName;
- public uint logCategories;
- public int version;
- public int androidSdkVersion;
- public int localRefsAreIndirect;
- public int grefGcThreshold;
- public IntPtr grefIGCUserPeer;
- public int isRunningOnDesktop;
- public byte brokenExceptionTransitions;
- public int packageNamingPolicy;
- public byte ioExceptionType;
- public int jniAddNativeMethodRegistrationAttributePresent;
- public bool jniRemappingInUse;
- }
-#pragma warning restore 0649
-
public static partial class JNIEnv {
- static IntPtr java_class_loader;
- static IntPtr java_vm;
- static IntPtr load_class_id;
- static IntPtr gref_class;
- static JniMethodInfo? mid_Class_forName;
- static int version;
- static int androidSdkVersion;
-
- static bool AllocObjectSupported;
- internal static bool jniRemappingInUse;
-
- static IntPtr grefIGCUserPeer_class;
-
- internal static int gref_gc_threshold;
-
- internal static bool PropagateExceptions;
-
- internal static bool IsRunningOnDesktop;
- internal static bool LogAssemblyCategory;
-
- static AndroidRuntime? androidRuntime;
- static BoundExceptionType BoundExceptionType;
-
[ThreadStatic]
static byte[]? mvid_bytes;
- internal static AndroidValueManager? AndroidValueManager;
+ public static IntPtr Handle => JniEnvironment.EnvironmentPointer;
- [DllImport (AndroidRuntime.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
- internal extern static void monodroid_log (LogLevel level, LogCategories category, string message);
-
- [DllImport (AndroidRuntime.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
- internal extern static IntPtr monodroid_timing_start (string? message);
-
- [DllImport (AndroidRuntime.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
- internal extern static void monodroid_timing_stop (IntPtr sequence, string? message);
-
- [DllImport (AndroidRuntime.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
- internal extern static void monodroid_free (IntPtr ptr);
-
- public static IntPtr Handle {
- get {
- return JniEnvironment.EnvironmentPointer;
- }
+ internal static IntPtr IdentityHash (IntPtr v)
+ {
+ return JNIEnvInit.LocalRefsAreIndirect ? RuntimeNativeMethods._monodroid_get_identity_hash_code (Handle, v) : v;
}
public static void CheckHandle (IntPtr jnienv)
@@ -94,23 +35,25 @@ internal static bool IsGCUserPeer (IntPtr value)
if (value == IntPtr.Zero)
return false;
- return IsInstanceOf (value, grefIGCUserPeer_class);
+ return IsInstanceOf (value, JNIEnvInit.grefIGCUserPeer_class);
}
internal static bool ShouldWrapJavaException (Java.Lang.Throwable? t, [CallerMemberName] string? caller = null)
{
if (t == null) {
- monodroid_log (LogLevel.Warn,
+ RuntimeNativeMethods.monodroid_log (LogLevel.Warn,
LogCategories.Default,
$"ShouldWrapJavaException was not passed a valid `Java.Lang.Throwable` instance. Called from method `{caller}`");
return false;
}
- bool wrap = BoundExceptionType == BoundExceptionType.System;
+ bool wrap = JNIEnvInit.BoundExceptionType == BoundExceptionType.System;
if (!wrap) {
- monodroid_log (LogLevel.Warn,
- LogCategories.Default,
- $"Not wrapping exception of type {t.GetType().FullName} from method `{caller}`. This will change in a future release.");
+ RuntimeNativeMethods.monodroid_log (
+ LogLevel.Warn,
+ LogCategories.Default,
+ $"Not wrapping exception of type {t.GetType().FullName} from method `{caller}`. This will change in a future release."
+ );
}
return wrap;
@@ -119,94 +62,6 @@ internal static bool ShouldWrapJavaException (Java.Lang.Throwable? t, [CallerMem
[DllImport ("libc")]
static extern int gettid ();
-#if NETCOREAPP
- [UnmanagedCallersOnly]
-#endif
- static unsafe void RegisterJniNatives (IntPtr typeName_ptr, int typeName_len, IntPtr jniClass, IntPtr methods_ptr, int methods_len)
- {
- string typeName = new string ((char*) typeName_ptr, 0, typeName_len);
- var type = Type.GetType (typeName);
- if (type == null) {
- monodroid_log (LogLevel.Error,
- LogCategories.Default,
- $"Could not load type '{typeName}'. Skipping JNI registration of type '{Java.Interop.TypeManager.GetClassName (jniClass)}'.");
- return;
- }
-
- var className = Java.Interop.TypeManager.GetClassName (jniClass);
- Java.Interop.TypeManager.RegisterType (className, type);
-
- JniType? jniType = null;
- JniType.GetCachedJniType (ref jniType, className);
-
- ReadOnlySpan methods = new ReadOnlySpan ((void*) methods_ptr, methods_len);
- ((AndroidTypeManager)androidRuntime!.TypeManager).RegisterNativeMembers (jniType, type, methods);
- }
-
-#if NETCOREAPP
- [UnmanagedCallersOnly]
-#endif
- internal static unsafe void Initialize (JnienvInitializeArgs* args)
- {
- IntPtr total_timing_sequence = IntPtr.Zero;
- IntPtr partial_timing_sequence = IntPtr.Zero;
-
- LogAssemblyCategory = (args->logCategories & (uint)LogCategories.Assembly) != 0;
-
- gref_gc_threshold = args->grefGcThreshold;
-
- jniRemappingInUse = args->jniRemappingInUse;
- java_vm = args->javaVm;
-
- version = args->version;
-
- androidSdkVersion = args->androidSdkVersion;
-
- java_class_loader = args->grefLoader;
- load_class_id = args->Loader_loadClass;
- gref_class = args->grefClass;
- mid_Class_forName = new JniMethodInfo (args->Class_forName, isStatic: true);
-
- if (args->localRefsAreIndirect == 1)
- IdentityHash = v => _monodroid_get_identity_hash_code (Handle, v);
- else
- IdentityHash = v => v;
-
-#if MONOANDROID1_0
- Mono.SystemDependencyProvider.Initialize ();
-#endif
-
- BoundExceptionType = (BoundExceptionType)args->ioExceptionType;
- androidRuntime = new AndroidRuntime (args->env, args->javaVm, androidSdkVersion > 10, args->grefLoader, args->Loader_loadClass, args->jniAddNativeMethodRegistrationAttributePresent != 0);
- AndroidValueManager = (AndroidValueManager) androidRuntime.ValueManager;
-
- AllocObjectSupported = androidSdkVersion > 10;
- IsRunningOnDesktop = args->isRunningOnDesktop == 1;
-
- grefIGCUserPeer_class = args->grefIGCUserPeer;
-
- PropagateExceptions = args->brokenExceptionTransitions == 0;
-
- JavaNativeTypeManager.PackageNamingPolicy = (PackageNamingPolicy)args->packageNamingPolicy;
- if (IsRunningOnDesktop) {
- var packageNamingPolicy = Environment.GetEnvironmentVariable ("__XA_PACKAGE_NAMING_POLICY__");
- if (Enum.TryParse (packageNamingPolicy, out PackageNamingPolicy pnp)) {
- JavaNativeTypeManager.PackageNamingPolicy = pnp;
- }
- }
-
-#if !MONOANDROID1_0
- SetSynchronizationContext ();
-#endif
- }
-
-#if !MONOANDROID1_0
- // NOTE: prevents Android.App.Application static ctor from running
- [MethodImpl (MethodImplOptions.NoInlining)]
- static void SetSynchronizationContext () =>
- SynchronizationContext.SetSynchronizationContext (Android.App.Application.SynchronizationContext);
-#endif
-
internal static void Exit ()
{
/* Manually dispose surfaced objects and close the current JniEnvironment to
@@ -219,7 +74,7 @@ internal static void Exit ()
obj.Dispose ();
continue;
} catch (Exception e) {
- monodroid_log (LogLevel.Warn, LogCategories.Default, $"Couldn't dispose object: {e}");
+ RuntimeNativeMethods.monodroid_log (LogLevel.Warn, LogCategories.Default, $"Couldn't dispose object: {e}");
}
/* If calling Dispose failed, the assumption is that user-code in
* the Dispose(bool) overload is to blame for it. In that case we
@@ -247,29 +102,13 @@ static void ManualJavaObjectDispose (Java.Lang.Object obj)
GC.SuppressFinalize (obj);
}
-#if NETCOREAPP
- internal static Action mono_unhandled_exception = monodroid_debugger_unhandled_exception;
-#else // NETCOREAPP
- internal static Action mono_unhandled_exception = null!;
-#endif // NETCOREAPP
- internal static MethodInfo? mono_unhandled_exception_method = null;
-
#if !NETCOREAPP
static Action AppDomain_DoUnhandledException = null!;
#endif // ndef NETCOREAPP
static void Initialize ()
{
- if (mono_unhandled_exception == null) {
- mono_unhandled_exception_method = typeof (System.Diagnostics.Debugger)
- .GetMethod ("Mono_UnhandledException", BindingFlags.NonPublic | BindingFlags.Static);
- if (mono_unhandled_exception_method != null)
- mono_unhandled_exception = (Action) Delegate.CreateDelegate (typeof(Action), mono_unhandled_exception_method);
- }
- if (mono_unhandled_exception_method == null && mono_unhandled_exception != null) {
- mono_unhandled_exception_method = mono_unhandled_exception.Method;
- }
-
+ AndroidRuntimeInternal.InitializeUnhandledExceptionMethod ();
#if !NETCOREAPP
if (AppDomain_DoUnhandledException == null) {
var ad_due = typeof (AppDomain)
@@ -286,14 +125,9 @@ static void Initialize ()
#endif // ndef NETCOREAPP
}
-#if NETCOREAPP
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- extern static void monodroid_unhandled_exception (Exception javaException);
-#endif // def NETCOREAPP
-
internal static void PropagateUncaughtException (IntPtr env, IntPtr javaThreadPtr, IntPtr javaExceptionPtr)
{
- if (!PropagateExceptions)
+ if (!JNIEnvInit.PropagateExceptions)
return;
try {
@@ -305,7 +139,7 @@ internal static void PropagateUncaughtException (IntPtr env, IntPtr javaThreadPt
var javaException = JavaObject.GetObject (env, javaExceptionPtr, JniHandleOwnership.DoNotTransfer)!;
if (Debugger.IsAttached) {
- mono_unhandled_exception?.Invoke (javaException);
+ AndroidRuntimeInternal.mono_unhandled_exception?.Invoke (javaException);
}
try {
@@ -321,32 +155,18 @@ internal static void PropagateUncaughtException (IntPtr env, IntPtr javaThreadPt
//AppDomain.CurrentDomain.DoUnhandledException (args);
AppDomain_DoUnhandledException?.Invoke (AppDomain.CurrentDomain, args);
#else // ndef NETCOREAPP
- monodroid_unhandled_exception (innerException ?? javaException);
+ RuntimeNativeMethods.monodroid_unhandled_exception (innerException ?? javaException);
#endif // def NETCOREAPP
} catch (Exception e) {
Logger.Log (LogLevel.Error, "monodroid", "Exception thrown while raising AppDomain.UnhandledException event: " + e.ToString ());
}
}
- [DllImport (AndroidRuntime.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
- extern static void _monodroid_gc_wait_for_bridge_processing ();
-
-#pragma warning disable CS0649 // Field is never assigned to. This field is assigned from monodroid-glue.cc.
- static volatile bool BridgeProcessing; // = false
-#pragma warning restore CS0649 // Field is never assigned to.
-
public static void WaitForBridgeProcessing ()
{
- if (!BridgeProcessing)
- return;
- _monodroid_gc_wait_for_bridge_processing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
}
- [DllImport (AndroidRuntime.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
- extern static IntPtr _monodroid_get_identity_hash_code (IntPtr env, IntPtr value);
-
- internal static Func? IdentityHash;
-
public static IntPtr AllocObject (string jniClassName)
{
IntPtr jniClass = JNIEnv.FindClass (jniClassName);
@@ -371,7 +191,7 @@ public static IntPtr AllocObject (Type type)
public static unsafe IntPtr StartCreateInstance (IntPtr jclass, IntPtr constructorId, JValue* constructorParameters)
{
- if (AllocObjectSupported) {
+ if (JNIEnvInit.AllocObjectSupported) {
return AllocObject (jclass);
}
return NewObject (jclass, constructorId, constructorParameters);
@@ -385,7 +205,7 @@ public static unsafe IntPtr StartCreateInstance (IntPtr jclass, IntPtr construct
public static unsafe void FinishCreateInstance (IntPtr instance, IntPtr jclass, IntPtr constructorId, JValue* constructorParameters)
{
- if (!AllocObjectSupported)
+ if (!JNIEnvInit.AllocObjectSupported)
return;
CallNonvirtualVoidMethod (instance, jclass, constructorId, constructorParameters);
}
@@ -398,7 +218,7 @@ public static unsafe void FinishCreateInstance (IntPtr instance, IntPtr jclass,
public static unsafe IntPtr StartCreateInstance (Type type, string jniCtorSignature, JValue* constructorParameters)
{
- if (AllocObjectSupported) {
+ if (JNIEnvInit.AllocObjectSupported) {
return AllocObject (type);
}
return CreateInstance (type, jniCtorSignature, constructorParameters);
@@ -412,7 +232,7 @@ public static unsafe IntPtr StartCreateInstance (Type type, string jniCtorSignat
public static unsafe IntPtr StartCreateInstance (string jniClassName, string jniCtorSignature, JValue* constructorParameters)
{
- if (AllocObjectSupported)
+ if (JNIEnvInit.AllocObjectSupported)
return AllocObject (jniClassName);
return CreateInstance (jniClassName, jniCtorSignature, constructorParameters);
}
@@ -425,7 +245,7 @@ public static unsafe IntPtr StartCreateInstance (string jniClassName, string jni
public static unsafe void FinishCreateInstance (IntPtr instance, string jniCtorSignature, JValue* constructorParameters)
{
- if (!AllocObjectSupported)
+ if (!JNIEnvInit.AllocObjectSupported)
return;
InvokeConstructor (instance, jniCtorSignature, constructorParameters);
}
@@ -513,7 +333,7 @@ public static IntPtr FindClass (System.Type type)
} catch (Java.Lang.Throwable e) {
if (!((e is Java.Lang.NoClassDefFoundError) || (e is Java.Lang.ClassNotFoundException)))
throw;
- monodroid_log (LogLevel.Warn, LogCategories.Default, $"JNIEnv.FindClass(Type) caught unexpected exception: {e}");
+ RuntimeNativeMethods.monodroid_log (LogLevel.Warn, LogCategories.Default, $"JNIEnv.FindClass(Type) caught unexpected exception: {e}");
var jni = Java.Interop.TypeManager.GetJniTypeName (type);
if (jni != null) {
e.Dispose ();
@@ -533,7 +353,7 @@ public static IntPtr FindClass (System.Type type)
}
}
- static readonly int nameBufferLength = 1024;
+ const int nameBufferLength = 1024;
[ThreadStatic] static char[]? nameBuffer;
static unsafe IntPtr BinaryName (string classname)
@@ -572,9 +392,9 @@ public unsafe static IntPtr FindClass (string classname)
JniArgumentValue* parameters = stackalloc JniArgumentValue [3] {
new JniArgumentValue (native_str),
new JniArgumentValue (true),
- new JniArgumentValue (java_class_loader),
+ new JniArgumentValue (JNIEnvInit.java_class_loader),
};
- local_ref = JniEnvironment.StaticMethods.CallStaticObjectMethod (Java.Lang.Class.Members.JniPeerType.PeerReference, mid_Class_forName!, parameters);
+ local_ref = JniEnvironment.StaticMethods.CallStaticObjectMethod (Java.Lang.Class.Members.JniPeerType.PeerReference, JNIEnvInit.mid_Class_forName!, parameters);
} finally {
DeleteLocalRef (native_str);
}
@@ -634,27 +454,6 @@ internal static void DeleteRef (IntPtr handle, JniHandleOwnership transfer)
}
}
- [DllImport (AndroidRuntime.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
- internal static extern int _monodroid_gref_log (string message);
-
- [DllImport (AndroidRuntime.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
- internal static extern int _monodroid_gref_log_new (IntPtr curHandle, byte curType, IntPtr newHandle, byte newType, string? threadName, int threadId, [In] StringBuilder? from, int from_writable);
-
- [DllImport (AndroidRuntime.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
- internal static extern void _monodroid_gref_log_delete (IntPtr handle, byte type, string? threadName, int threadId, [In] StringBuilder? from, int from_writable);
-
- [DllImport (AndroidRuntime.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
- internal static extern void _monodroid_weak_gref_new (IntPtr curHandle, byte curType, IntPtr newHandle, byte newType, string? threadName, int threadId, [In] StringBuilder? from, int from_writable);
-
- [DllImport (AndroidRuntime.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
- internal static extern void _monodroid_weak_gref_delete (IntPtr handle, byte type, string? threadName, int threadId, [In] StringBuilder? from, int from_writable);
-
- [DllImport (AndroidRuntime.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
- internal static extern int _monodroid_lref_log_new (int lrefc, IntPtr handle, byte type, string? threadName, int threadId, [In] StringBuilder from, int from_writable);
-
- [DllImport (AndroidRuntime.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
- internal static extern void _monodroid_lref_log_delete (int lrefc, IntPtr handle, byte type, string? threadName, int threadId, [In] StringBuilder from, int from_writable);
-
public static IntPtr NewGlobalRef (IntPtr jobject)
{
var r = new JniObjectReference (jobject);
@@ -715,20 +514,15 @@ public static string GetClassNameFromInstance (IntPtr jobject)
[MethodImplAttribute(MethodImplOptions.InternalCall)]
static extern unsafe IntPtr monodroid_typemap_managed_to_java (Type type, byte* mvid);
-#if NETCOREAPP
- [MethodImplAttribute(MethodImplOptions.InternalCall)]
- static extern unsafe void monodroid_debugger_unhandled_exception (Exception e);
-#endif // NETCOREAPP
-
internal static void LogTypemapTrace (StackTrace st)
{
string? trace = st.ToString ()?.Trim ();
if (String.IsNullOrEmpty (trace))
return;
- monodroid_log (LogLevel.Warn, LogCategories.Assembly, "typemap: called from");
+ RuntimeNativeMethods.monodroid_log (LogLevel.Warn, LogCategories.Assembly, "typemap: called from");
foreach (string line in trace!.Split ('\n')) {
- monodroid_log (LogLevel.Warn, LogCategories.Assembly, line);
+ RuntimeNativeMethods.monodroid_log (LogLevel.Warn, LogCategories.Assembly, line);
}
}
@@ -740,7 +534,7 @@ internal static void LogTypemapTrace (StackTrace st)
var mvid = new Span(mvid_bytes);
byte[]? mvid_data = null;
if (!type.Module.ModuleVersionId.TryWriteBytes (mvid)) {
- monodroid_log (LogLevel.Warn, LogCategories.Default, $"Failed to obtain module MVID using the fast method, falling back to the slow one");
+ RuntimeNativeMethods.monodroid_log (LogLevel.Warn, LogCategories.Default, $"Failed to obtain module MVID using the fast method, falling back to the slow one");
mvid_data = type.Module.ModuleVersionId.ToByteArray ();
} else {
mvid_data = mvid_bytes;
@@ -752,8 +546,8 @@ internal static void LogTypemapTrace (StackTrace st)
}
if (ret == IntPtr.Zero) {
- if (LogAssemblyCategory) {
- monodroid_log (LogLevel.Warn, LogCategories.Default, $"typemap: failed to map managed type to Java type: {type.AssemblyQualifiedName} (Module ID: {type.Module.ModuleVersionId}; Type token: {type.MetadataToken})");
+ if (JNIEnvInit.LogAssemblyCategory) {
+ RuntimeNativeMethods.monodroid_log (LogLevel.Warn, LogCategories.Default, $"typemap: failed to map managed type to Java type: {type.AssemblyQualifiedName} (Module ID: {type.Module.ModuleVersionId}; Type token: {type.MetadataToken})");
LogTypemapTrace (new StackTrace (true));
}
diff --git a/src/Mono.Android/Android.Runtime/JNIEnvInit.cs b/src/Mono.Android/Android.Runtime/JNIEnvInit.cs
new file mode 100644
index 00000000000..9311757806e
--- /dev/null
+++ b/src/Mono.Android/Android.Runtime/JNIEnvInit.cs
@@ -0,0 +1,136 @@
+using System;
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Threading;
+
+using Java.Interop;
+using Java.Interop.Tools.TypeNameMappings;
+
+namespace Android.Runtime
+{
+ static internal class JNIEnvInit
+ {
+#pragma warning disable 0649
+ internal struct JnienvInitializeArgs {
+ public IntPtr javaVm;
+ public IntPtr env;
+ public IntPtr grefLoader;
+ public IntPtr Loader_loadClass;
+ public IntPtr grefClass; // TODO: remove, not needed anymore
+ public IntPtr Class_forName;
+ public uint logCategories;
+ public int version; // TODO: remove, not needed anymore
+ public int androidSdkVersion;
+ public int localRefsAreIndirect;
+ public int grefGcThreshold;
+ public IntPtr grefIGCUserPeer;
+ public int isRunningOnDesktop;
+ public byte brokenExceptionTransitions;
+ public int packageNamingPolicy;
+ public byte ioExceptionType;
+ public int jniAddNativeMethodRegistrationAttributePresent;
+ public bool jniRemappingInUse;
+ public bool marshalMethodsEnabled;
+ }
+#pragma warning restore 0649
+
+ internal static AndroidValueManager? AndroidValueManager;
+ internal static bool AllocObjectSupported;
+ internal static bool IsRunningOnDesktop;
+ internal static bool jniRemappingInUse;
+ internal static bool LocalRefsAreIndirect;
+ internal static bool LogAssemblyCategory;
+ internal static bool MarshalMethodsEnabled;
+ internal static bool PropagateExceptions;
+ internal static BoundExceptionType BoundExceptionType;
+ internal static int gref_gc_threshold;
+ internal static IntPtr grefIGCUserPeer_class;
+ internal static IntPtr java_class_loader;
+ internal static JniMethodInfo? mid_Class_forName;
+
+ static AndroidRuntime? androidRuntime;
+
+#if NETCOREAPP
+ [UnmanagedCallersOnly]
+#endif
+ static unsafe void RegisterJniNatives (IntPtr typeName_ptr, int typeName_len, IntPtr jniClass, IntPtr methods_ptr, int methods_len)
+ {
+ string typeName = new string ((char*) typeName_ptr, 0, typeName_len);
+ var type = Type.GetType (typeName);
+ if (type == null) {
+ RuntimeNativeMethods.monodroid_log (LogLevel.Error,
+ LogCategories.Default,
+ $"Could not load type '{typeName}'. Skipping JNI registration of type '{Java.Interop.TypeManager.GetClassName (jniClass)}'.");
+ return;
+ }
+
+ var className = Java.Interop.TypeManager.GetClassName (jniClass);
+ Java.Interop.TypeManager.RegisterType (className, type);
+
+ JniType? jniType = null;
+ JniType.GetCachedJniType (ref jniType, className);
+
+ ReadOnlySpan methods = new ReadOnlySpan ((void*) methods_ptr, methods_len);
+ ((AndroidTypeManager)androidRuntime!.TypeManager).RegisterNativeMembers (jniType, type, methods);
+ }
+
+#if NETCOREAPP
+ [UnmanagedCallersOnly]
+#endif
+ internal static unsafe void Initialize (JnienvInitializeArgs* args)
+ {
+ IntPtr total_timing_sequence = IntPtr.Zero;
+ IntPtr partial_timing_sequence = IntPtr.Zero;
+
+ LogAssemblyCategory = (args->logCategories & (uint)LogCategories.Assembly) != 0;
+
+ gref_gc_threshold = args->grefGcThreshold;
+
+ jniRemappingInUse = args->jniRemappingInUse;
+#if NETCOREAPP
+ MarshalMethodsEnabled = args->marshalMethodsEnabled;
+#endif
+ java_class_loader = args->grefLoader;
+
+ mid_Class_forName = new JniMethodInfo (args->Class_forName, isStatic: true);
+
+ LocalRefsAreIndirect = args->localRefsAreIndirect == 1;
+
+#if MONOANDROID1_0
+ Mono.SystemDependencyProvider.Initialize ();
+#endif
+
+ bool androidNewerThan10 = args->androidSdkVersion > 10;
+ BoundExceptionType = (BoundExceptionType)args->ioExceptionType;
+ androidRuntime = new AndroidRuntime (args->env, args->javaVm, androidNewerThan10, args->grefLoader, args->Loader_loadClass, args->jniAddNativeMethodRegistrationAttributePresent != 0);
+ AndroidValueManager = (AndroidValueManager) androidRuntime.ValueManager;
+
+ AllocObjectSupported = androidNewerThan10;
+ IsRunningOnDesktop = args->isRunningOnDesktop == 1;
+
+ grefIGCUserPeer_class = args->grefIGCUserPeer;
+
+ PropagateExceptions = args->brokenExceptionTransitions == 0;
+
+ JavaNativeTypeManager.PackageNamingPolicy = (PackageNamingPolicy)args->packageNamingPolicy;
+ if (IsRunningOnDesktop) {
+ var packageNamingPolicy = Environment.GetEnvironmentVariable ("__XA_PACKAGE_NAMING_POLICY__");
+ if (Enum.TryParse (packageNamingPolicy, out PackageNamingPolicy pnp)) {
+ JavaNativeTypeManager.PackageNamingPolicy = pnp;
+ }
+ }
+
+#if !MONOANDROID1_0
+ SetSynchronizationContext ();
+#endif
+ }
+
+#if !MONOANDROID1_0
+ // NOTE: prevents Android.App.Application static ctor from running
+ [MethodImpl (MethodImplOptions.NoInlining)]
+ static void SetSynchronizationContext () =>
+ SynchronizationContext.SetSynchronizationContext (Android.App.Application.SynchronizationContext);
+#endif
+ }
+}
diff --git a/src/Mono.Android/Android.Runtime/JNINativeWrapper.cs b/src/Mono.Android/Android.Runtime/JNINativeWrapper.cs
index 1d09ccf9772..d7b27e855d8 100644
--- a/src/Mono.Android/Android.Runtime/JNINativeWrapper.cs
+++ b/src/Mono.Android/Android.Runtime/JNINativeWrapper.cs
@@ -20,9 +20,9 @@ static void get_runtime_types ()
if (exception_handler_method == null)
AndroidEnvironment.FailFast ("Cannot find AndroidEnvironment.UnhandledException");
- wait_for_bridge_processing_method = typeof (JNIEnv).GetMethod ("WaitForBridgeProcessing", BindingFlags.Public | BindingFlags.Static);
+ wait_for_bridge_processing_method = typeof (AndroidRuntimeInternal).GetMethod ("WaitForBridgeProcessing", BindingFlags.Public | BindingFlags.Static);
if (wait_for_bridge_processing_method == null)
- AndroidEnvironment.FailFast ("Cannot find JNIEnv.WaitForBridgeProcessing");
+ AndroidEnvironment.FailFast ("Cannot find AndroidRuntimeInternal.WaitForBridgeProcessing");
}
public static Delegate CreateDelegate (Delegate dlg)
@@ -41,8 +41,8 @@ public static Delegate CreateDelegate (Delegate dlg)
if (result != null)
return result;
- if (JNIEnv.LogAssemblyCategory) {
- JNIEnv.monodroid_log (LogLevel.Debug, LogCategories.Assembly, $"Falling back to System.Reflection.Emit for delegate type '{delegateType}': {dlg.Method}");
+ if (JNIEnvInit.LogAssemblyCategory) {
+ RuntimeNativeMethods.monodroid_log (LogLevel.Debug, LogCategories.Assembly, $"Falling back to System.Reflection.Emit for delegate type '{delegateType}': {dlg.Method}");
}
var ret_type = dlg.Method.ReturnType;
@@ -72,11 +72,11 @@ public static Delegate CreateDelegate (Delegate dlg)
ig.Emit (OpCodes.Leave, label);
- bool filter = Debugger.IsAttached || !JNIEnv.PropagateExceptions;
- if (filter && JNIEnv.mono_unhandled_exception_method != null) {
+ bool filter = Debugger.IsAttached || !JNIEnvInit.PropagateExceptions;
+ if (filter && AndroidRuntimeInternal.mono_unhandled_exception_method != null) {
ig.BeginExceptFilterBlock ();
- ig.Emit (OpCodes.Call, JNIEnv.mono_unhandled_exception_method);
+ ig.Emit (OpCodes.Call, AndroidRuntimeInternal.mono_unhandled_exception_method);
ig.Emit (OpCodes.Ldc_I4_1);
ig.BeginCatchBlock (null!);
} else {
@@ -101,4 +101,3 @@ public static Delegate CreateDelegate (Delegate dlg)
}
}
-
diff --git a/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.cs b/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.cs
index 301a488008a..738b2e07bc1 100644
--- a/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.cs
+++ b/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.cs
@@ -8,8 +8,8 @@ public static partial class JNINativeWrapper
{
static bool _unhandled_exception (Exception e)
{
- if (Debugger.IsAttached || !JNIEnv.PropagateExceptions) {
- JNIEnv.mono_unhandled_exception?.Invoke (e);
+ if (Debugger.IsAttached || !JNIEnvInit.PropagateExceptions) {
+ AndroidRuntimeInternal.mono_unhandled_exception?.Invoke (e);
return false;
}
return true;
@@ -17,18 +17,18 @@ static bool _unhandled_exception (Exception e)
internal static void Wrap_JniMarshal_PP_V (this _JniMarshal_PP_V callback, IntPtr jnienv, IntPtr klazz)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
callback (jnienv, klazz);
} catch (Exception e) when (_unhandled_exception (e)) {
AndroidEnvironment.UnhandledException (e);
-
+
}
}
internal static int Wrap_JniMarshal_PP_I (this _JniMarshal_PP_I callback, IntPtr jnienv, IntPtr klazz)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
return callback (jnienv, klazz);
} catch (Exception e) when (_unhandled_exception (e)) {
@@ -39,7 +39,7 @@ internal static int Wrap_JniMarshal_PP_I (this _JniMarshal_PP_I callback, IntPtr
internal static bool Wrap_JniMarshal_PP_Z (this _JniMarshal_PP_Z callback, IntPtr jnienv, IntPtr klazz)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
return callback (jnienv, klazz);
} catch (Exception e) when (_unhandled_exception (e)) {
@@ -50,18 +50,18 @@ internal static bool Wrap_JniMarshal_PP_Z (this _JniMarshal_PP_Z callback, IntPt
internal static void Wrap_JniMarshal_PPI_V (this _JniMarshal_PPI_V callback, IntPtr jnienv, IntPtr klazz, int p0)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
callback (jnienv, klazz, p0);
} catch (Exception e) when (_unhandled_exception (e)) {
AndroidEnvironment.UnhandledException (e);
-
+
}
}
internal static IntPtr Wrap_JniMarshal_PPI_L (this _JniMarshal_PPI_L callback, IntPtr jnienv, IntPtr klazz, int p0)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
return callback (jnienv, klazz, p0);
} catch (Exception e) when (_unhandled_exception (e)) {
@@ -72,7 +72,7 @@ internal static IntPtr Wrap_JniMarshal_PPI_L (this _JniMarshal_PPI_L callback, I
internal static int Wrap_JniMarshal_PPI_I (this _JniMarshal_PPI_I callback, IntPtr jnienv, IntPtr klazz, int p0)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
return callback (jnienv, klazz, p0);
} catch (Exception e) when (_unhandled_exception (e)) {
@@ -83,7 +83,7 @@ internal static int Wrap_JniMarshal_PPI_I (this _JniMarshal_PPI_I callback, IntP
internal static long Wrap_JniMarshal_PPI_J (this _JniMarshal_PPI_J callback, IntPtr jnienv, IntPtr klazz, int p0)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
return callback (jnienv, klazz, p0);
} catch (Exception e) when (_unhandled_exception (e)) {
@@ -94,7 +94,7 @@ internal static long Wrap_JniMarshal_PPI_J (this _JniMarshal_PPI_J callback, Int
internal static int Wrap_JniMarshal_PPL_I (this _JniMarshal_PPL_I callback, IntPtr jnienv, IntPtr klazz, IntPtr p0)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
return callback (jnienv, klazz, p0);
} catch (Exception e) when (_unhandled_exception (e)) {
@@ -105,7 +105,7 @@ internal static int Wrap_JniMarshal_PPL_I (this _JniMarshal_PPL_I callback, IntP
internal static IntPtr Wrap_JniMarshal_PPL_L (this _JniMarshal_PPL_L callback, IntPtr jnienv, IntPtr klazz, IntPtr p0)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
return callback (jnienv, klazz, p0);
} catch (Exception e) when (_unhandled_exception (e)) {
@@ -116,18 +116,18 @@ internal static IntPtr Wrap_JniMarshal_PPL_L (this _JniMarshal_PPL_L callback, I
internal static void Wrap_JniMarshal_PPL_V (this _JniMarshal_PPL_V callback, IntPtr jnienv, IntPtr klazz, IntPtr p0)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
callback (jnienv, klazz, p0);
} catch (Exception e) when (_unhandled_exception (e)) {
AndroidEnvironment.UnhandledException (e);
-
+
}
}
internal static bool Wrap_JniMarshal_PPL_Z (this _JniMarshal_PPL_Z callback, IntPtr jnienv, IntPtr klazz, IntPtr p0)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
return callback (jnienv, klazz, p0);
} catch (Exception e) when (_unhandled_exception (e)) {
@@ -138,7 +138,7 @@ internal static bool Wrap_JniMarshal_PPL_Z (this _JniMarshal_PPL_Z callback, Int
internal static bool Wrap_JniMarshal_PPJ_Z (this _JniMarshal_PPJ_Z callback, IntPtr jnienv, IntPtr klazz, long p0)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
return callback (jnienv, klazz, p0);
} catch (Exception e) when (_unhandled_exception (e)) {
@@ -149,62 +149,62 @@ internal static bool Wrap_JniMarshal_PPJ_Z (this _JniMarshal_PPJ_Z callback, Int
internal static void Wrap_JniMarshal_PPII_V (this _JniMarshal_PPII_V callback, IntPtr jnienv, IntPtr klazz, int p0, int p1)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
callback (jnienv, klazz, p0, p1);
} catch (Exception e) when (_unhandled_exception (e)) {
AndroidEnvironment.UnhandledException (e);
-
+
}
}
internal static void Wrap_JniMarshal_PPLI_V (this _JniMarshal_PPLI_V callback, IntPtr jnienv, IntPtr klazz, IntPtr p0, int p1)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
callback (jnienv, klazz, p0, p1);
} catch (Exception e) when (_unhandled_exception (e)) {
AndroidEnvironment.UnhandledException (e);
-
+
}
}
internal static void Wrap_JniMarshal_PPLZ_V (this _JniMarshal_PPLZ_V callback, IntPtr jnienv, IntPtr klazz, IntPtr p0, bool p1)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
callback (jnienv, klazz, p0, p1);
} catch (Exception e) when (_unhandled_exception (e)) {
AndroidEnvironment.UnhandledException (e);
-
+
}
}
internal static void Wrap_JniMarshal_PPLL_V (this _JniMarshal_PPLL_V callback, IntPtr jnienv, IntPtr klazz, IntPtr p0, IntPtr p1)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
callback (jnienv, klazz, p0, p1);
} catch (Exception e) when (_unhandled_exception (e)) {
AndroidEnvironment.UnhandledException (e);
-
+
}
}
internal static void Wrap_JniMarshal_PPLF_V (this _JniMarshal_PPLF_V callback, IntPtr jnienv, IntPtr klazz, IntPtr p0, float p1)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
callback (jnienv, klazz, p0, p1);
} catch (Exception e) when (_unhandled_exception (e)) {
AndroidEnvironment.UnhandledException (e);
-
+
}
}
internal static IntPtr Wrap_JniMarshal_PPLI_L (this _JniMarshal_PPLI_L callback, IntPtr jnienv, IntPtr klazz, IntPtr p0, int p1)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
return callback (jnienv, klazz, p0, p1);
} catch (Exception e) when (_unhandled_exception (e)) {
@@ -215,7 +215,7 @@ internal static IntPtr Wrap_JniMarshal_PPLI_L (this _JniMarshal_PPLI_L callback,
internal static IntPtr Wrap_JniMarshal_PPLL_L (this _JniMarshal_PPLL_L callback, IntPtr jnienv, IntPtr klazz, IntPtr p0, IntPtr p1)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
return callback (jnienv, klazz, p0, p1);
} catch (Exception e) when (_unhandled_exception (e)) {
@@ -226,7 +226,7 @@ internal static IntPtr Wrap_JniMarshal_PPLL_L (this _JniMarshal_PPLL_L callback,
internal static bool Wrap_JniMarshal_PPLL_Z (this _JniMarshal_PPLL_Z callback, IntPtr jnienv, IntPtr klazz, IntPtr p0, IntPtr p1)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
return callback (jnienv, klazz, p0, p1);
} catch (Exception e) when (_unhandled_exception (e)) {
@@ -237,7 +237,7 @@ internal static bool Wrap_JniMarshal_PPLL_Z (this _JniMarshal_PPLL_Z callback, I
internal static bool Wrap_JniMarshal_PPIL_Z (this _JniMarshal_PPIL_Z callback, IntPtr jnienv, IntPtr klazz, int p0, IntPtr p1)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
return callback (jnienv, klazz, p0, p1);
} catch (Exception e) when (_unhandled_exception (e)) {
@@ -248,18 +248,18 @@ internal static bool Wrap_JniMarshal_PPIL_Z (this _JniMarshal_PPIL_Z callback, I
internal static void Wrap_JniMarshal_PPIIL_V (this _JniMarshal_PPIIL_V callback, IntPtr jnienv, IntPtr klazz, int p0, int p1, IntPtr p2)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
callback (jnienv, klazz, p0, p1, p2);
} catch (Exception e) when (_unhandled_exception (e)) {
AndroidEnvironment.UnhandledException (e);
-
+
}
}
internal static int Wrap_JniMarshal_PPLII_I (this _JniMarshal_PPLII_I callback, IntPtr jnienv, IntPtr klazz, IntPtr p0, int p1, int p2)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
return callback (jnienv, klazz, p0, p1, p2);
} catch (Exception e) when (_unhandled_exception (e)) {
@@ -270,7 +270,7 @@ internal static int Wrap_JniMarshal_PPLII_I (this _JniMarshal_PPLII_I callback,
internal static bool Wrap_JniMarshal_PPLII_Z (this _JniMarshal_PPLII_Z callback, IntPtr jnienv, IntPtr klazz, IntPtr p0, int p1, int p2)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
return callback (jnienv, klazz, p0, p1, p2);
} catch (Exception e) when (_unhandled_exception (e)) {
@@ -281,29 +281,29 @@ internal static bool Wrap_JniMarshal_PPLII_Z (this _JniMarshal_PPLII_Z callback,
internal static void Wrap_JniMarshal_PPLII_V (this _JniMarshal_PPLII_V callback, IntPtr jnienv, IntPtr klazz, IntPtr p0, int p1, int p2)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
callback (jnienv, klazz, p0, p1, p2);
} catch (Exception e) when (_unhandled_exception (e)) {
AndroidEnvironment.UnhandledException (e);
-
+
}
}
internal static void Wrap_JniMarshal_PPIII_V (this _JniMarshal_PPIII_V callback, IntPtr jnienv, IntPtr klazz, int p0, int p1, int p2)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
callback (jnienv, klazz, p0, p1, p2);
} catch (Exception e) when (_unhandled_exception (e)) {
AndroidEnvironment.UnhandledException (e);
-
+
}
}
internal static bool Wrap_JniMarshal_PPLLJ_Z (this _JniMarshal_PPLLJ_Z callback, IntPtr jnienv, IntPtr klazz, IntPtr p0, IntPtr p1, long p2)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
return callback (jnienv, klazz, p0, p1, p2);
} catch (Exception e) when (_unhandled_exception (e)) {
@@ -314,18 +314,18 @@ internal static bool Wrap_JniMarshal_PPLLJ_Z (this _JniMarshal_PPLLJ_Z callback,
internal static void Wrap_JniMarshal_PPILL_V (this _JniMarshal_PPILL_V callback, IntPtr jnienv, IntPtr klazz, int p0, IntPtr p1, IntPtr p2)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
callback (jnienv, klazz, p0, p1, p2);
} catch (Exception e) when (_unhandled_exception (e)) {
AndroidEnvironment.UnhandledException (e);
-
+
}
}
internal static bool Wrap_JniMarshal_PPLIL_Z (this _JniMarshal_PPLIL_Z callback, IntPtr jnienv, IntPtr klazz, IntPtr p0, int p1, IntPtr p2)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
return callback (jnienv, klazz, p0, p1, p2);
} catch (Exception e) when (_unhandled_exception (e)) {
@@ -336,18 +336,18 @@ internal static bool Wrap_JniMarshal_PPLIL_Z (this _JniMarshal_PPLIL_Z callback,
internal static void Wrap_JniMarshal_PPLLL_V (this _JniMarshal_PPLLL_V callback, IntPtr jnienv, IntPtr klazz, IntPtr p0, IntPtr p1, IntPtr p2)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
callback (jnienv, klazz, p0, p1, p2);
} catch (Exception e) when (_unhandled_exception (e)) {
AndroidEnvironment.UnhandledException (e);
-
+
}
}
internal static IntPtr Wrap_JniMarshal_PPLLL_L (this _JniMarshal_PPLLL_L callback, IntPtr jnienv, IntPtr klazz, IntPtr p0, IntPtr p1, IntPtr p2)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
return callback (jnienv, klazz, p0, p1, p2);
} catch (Exception e) when (_unhandled_exception (e)) {
@@ -358,7 +358,7 @@ internal static IntPtr Wrap_JniMarshal_PPLLL_L (this _JniMarshal_PPLLL_L callbac
internal static bool Wrap_JniMarshal_PPLLL_Z (this _JniMarshal_PPLLL_Z callback, IntPtr jnienv, IntPtr klazz, IntPtr p0, IntPtr p1, IntPtr p2)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
return callback (jnienv, klazz, p0, p1, p2);
} catch (Exception e) when (_unhandled_exception (e)) {
@@ -369,7 +369,7 @@ internal static bool Wrap_JniMarshal_PPLLL_Z (this _JniMarshal_PPLLL_Z callback,
internal static IntPtr Wrap_JniMarshal_PPIZI_L (this _JniMarshal_PPIZI_L callback, IntPtr jnienv, IntPtr klazz, int p0, bool p1, int p2)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
return callback (jnienv, klazz, p0, p1, p2);
} catch (Exception e) when (_unhandled_exception (e)) {
@@ -380,29 +380,29 @@ internal static IntPtr Wrap_JniMarshal_PPIZI_L (this _JniMarshal_PPIZI_L callbac
internal static void Wrap_JniMarshal_PPIIII_V (this _JniMarshal_PPIIII_V callback, IntPtr jnienv, IntPtr klazz, int p0, int p1, int p2, int p3)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
callback (jnienv, klazz, p0, p1, p2, p3);
} catch (Exception e) when (_unhandled_exception (e)) {
AndroidEnvironment.UnhandledException (e);
-
+
}
}
internal static void Wrap_JniMarshal_PPLLLL_V (this _JniMarshal_PPLLLL_V callback, IntPtr jnienv, IntPtr klazz, IntPtr p0, IntPtr p1, IntPtr p2, IntPtr p3)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
callback (jnienv, klazz, p0, p1, p2, p3);
} catch (Exception e) when (_unhandled_exception (e)) {
AndroidEnvironment.UnhandledException (e);
-
+
}
}
internal static bool Wrap_JniMarshal_PPLZZL_Z (this _JniMarshal_PPLZZL_Z callback, IntPtr jnienv, IntPtr klazz, IntPtr p0, bool p1, bool p2, IntPtr p3)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
return callback (jnienv, klazz, p0, p1, p2, p3);
} catch (Exception e) when (_unhandled_exception (e)) {
@@ -413,34 +413,34 @@ internal static bool Wrap_JniMarshal_PPLZZL_Z (this _JniMarshal_PPLZZL_Z callbac
internal static void Wrap_JniMarshal_PPLIIII_V (this _JniMarshal_PPLIIII_V callback, IntPtr jnienv, IntPtr klazz, IntPtr p0, int p1, int p2, int p3, int p4)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
callback (jnienv, klazz, p0, p1, p2, p3, p4);
} catch (Exception e) when (_unhandled_exception (e)) {
AndroidEnvironment.UnhandledException (e);
-
+
}
}
internal static void Wrap_JniMarshal_PPZIIII_V (this _JniMarshal_PPZIIII_V callback, IntPtr jnienv, IntPtr klazz, bool p0, int p1, int p2, int p3, int p4)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
callback (jnienv, klazz, p0, p1, p2, p3, p4);
} catch (Exception e) when (_unhandled_exception (e)) {
AndroidEnvironment.UnhandledException (e);
-
+
}
}
internal static void Wrap_JniMarshal_PPLIIIIIIII_V (this _JniMarshal_PPLIIIIIIII_V callback, IntPtr jnienv, IntPtr klazz, IntPtr p0, int p1, int p2, int p3, int p4, int p5, int p6, int p7, int p8)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
callback (jnienv, klazz, p0, p1, p2, p3, p4, p5, p6, p7, p8);
} catch (Exception e) when (_unhandled_exception (e)) {
AndroidEnvironment.UnhandledException (e);
-
+
}
}
diff --git a/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.tt b/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.tt
index 94a5da0272e..b3bb339b6e7 100644
--- a/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.tt
+++ b/src/Mono.Android/Android.Runtime/JNINativeWrapper.g.tt
@@ -250,8 +250,12 @@ namespace Android.Runtime
{
static bool _unhandled_exception (Exception e)
{
- if (Debugger.IsAttached || !JNIEnv.PropagateExceptions) {
- JNIEnv.mono_unhandled_exception?.Invoke (e);
+ if (Debugger.IsAttached || !JNIEnvInit.PropagateExceptions) {
+#if NETCOREAPP
+ AndroidRuntimeInternal.mono_unhandled_exception?.Invoke (e);
+#else
+ JNIEnvInit.mono_unhandled_exception?.Invoke (e);
+#endif
return false;
}
return true;
@@ -262,7 +266,7 @@ foreach (var info in delegateTypes) {
#>
internal static <#= info.Return #> Wrap<#= info.Type #> (this <#= info.Type #> callback, <#= info.Signature #>)
{
- JNIEnv.WaitForBridgeProcessing ();
+ AndroidRuntimeInternal.WaitForBridgeProcessing ();
try {
<#= info.Return != "void" ? "return " : "" #>callback (<#= info.Invoke #>);
} catch (Exception e) when (_unhandled_exception (e)) {
diff --git a/src/Mono.Android/Android.Runtime/LogCategories.cs b/src/Mono.Android/Android.Runtime/LogCategories.cs
new file mode 100644
index 00000000000..5eb6020cfad
--- /dev/null
+++ b/src/Mono.Android/Android.Runtime/LogCategories.cs
@@ -0,0 +1,23 @@
+#if !NETCOREAPP || INSIDE_MONO_ANDROID_RUNTIME
+using System;
+
+namespace Android.Runtime
+{
+ // Keep in sync with the LogCategories enum in
+ // monodroid/libmonodroid/logger.{c,h}
+ [Flags]
+ internal enum LogCategories {
+ None = 0,
+ Default = 1 << 0,
+ Assembly = 1 << 1,
+ Debugger = 1 << 2,
+ GC = 1 << 3,
+ GlobalRef = 1 << 4,
+ LocalRef = 1 << 5,
+ Timing = 1 << 6,
+ Bundle = 1 << 7,
+ Net = 1 << 8,
+ Netlink = 1 << 9,
+ }
+}
+#endif // !NETCOREAPP || INSIDE_MONO_ANDROID_RUNTIME
diff --git a/src/Mono.Android/Android.Runtime/LogLevel.cs b/src/Mono.Android/Android.Runtime/LogLevel.cs
new file mode 100644
index 00000000000..347f2037523
--- /dev/null
+++ b/src/Mono.Android/Android.Runtime/LogLevel.cs
@@ -0,0 +1,18 @@
+#if !NETCOREAPP || INSIDE_MONO_ANDROID_RUNTIME
+namespace Android.Runtime
+{
+ // Keep in sync with the LogLevel enum in
+ // monodroid/libmonodroid/logger.{c,h}
+ internal enum LogLevel {
+ Unknown = 0x00,
+ Default = 0x01,
+ Verbose = 0x02,
+ Debug = 0x03,
+ Info = 0x04,
+ Warn = 0x05,
+ Error = 0x06,
+ Fatal = 0x07,
+ Silent = 0x08
+ }
+}
+#endif // !NETCOREAPP || INSIDE_MONO_ANDROID_RUNTIME
diff --git a/src/Mono.Android/Android.Runtime/Logger.cs b/src/Mono.Android/Android.Runtime/Logger.cs
index 35e0651a835..f4665ab3a63 100644
--- a/src/Mono.Android/Android.Runtime/Logger.cs
+++ b/src/Mono.Android/Android.Runtime/Logger.cs
@@ -60,7 +60,7 @@ public static void Log (LogLevel level, string appname, string? log) {
}
}
- [DllImport (AndroidRuntime.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
+ [DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
extern static uint monodroid_get_log_categories ();
static Logger ()
@@ -68,35 +68,4 @@ static Logger ()
Categories = (LogCategories) monodroid_get_log_categories ();
}
}
-
- // Keep in sync with the LogLevel enum in
- // monodroid/libmonodroid/logger.{c,h}
- internal enum LogLevel {
- Unknown = 0x00,
- Default = 0x01,
- Verbose = 0x02,
- Debug = 0x03,
- Info = 0x04,
- Warn = 0x05,
- Error = 0x06,
- Fatal = 0x07,
- Silent = 0x08
- }
-
- // Keep in sync with the LogCategories enum in
- // monodroid/libmonodroid/logger.{c,h}
- [Flags]
- internal enum LogCategories {
- None = 0,
- Default = 1 << 0,
- Assembly = 1 << 1,
- Debugger = 1 << 2,
- GC = 1 << 3,
- GlobalRef = 1 << 4,
- LocalRef = 1 << 5,
- Timing = 1 << 6,
- Bundle = 1 << 7,
- Net = 1 << 8,
- Netlink = 1 << 9,
- }
}
diff --git a/src/Mono.Android/Android.Runtime/RuntimeConstants.cs b/src/Mono.Android/Android.Runtime/RuntimeConstants.cs
new file mode 100644
index 00000000000..254fb8c2170
--- /dev/null
+++ b/src/Mono.Android/Android.Runtime/RuntimeConstants.cs
@@ -0,0 +1,9 @@
+#if !NETCOREAPP || INSIDE_MONO_ANDROID_RUNTIME
+namespace Android.Runtime
+{
+ internal static class RuntimeConstants
+ {
+ public const string InternalDllName = "xa-internal-api";
+ }
+}
+#endif // !NETCOREAPP || INSIDE_MONO_ANDROID_RUNTIME
diff --git a/src/Mono.Android/Android.Runtime/RuntimeNativeMethods.cs b/src/Mono.Android/Android.Runtime/RuntimeNativeMethods.cs
new file mode 100644
index 00000000000..3487514a6a7
--- /dev/null
+++ b/src/Mono.Android/Android.Runtime/RuntimeNativeMethods.cs
@@ -0,0 +1,88 @@
+#if !NETCOREAPP || INSIDE_MONO_ANDROID_RUNTIME
+using System;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+using System.Text;
+
+namespace Android.Runtime
+{
+ internal static class RuntimeNativeMethods
+ {
+ [DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
+ internal extern static void monodroid_log (LogLevel level, LogCategories category, string message);
+
+ [DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
+ internal extern static IntPtr monodroid_timing_start (string? message);
+
+ [DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
+ internal extern static void monodroid_timing_stop (IntPtr sequence, string? message);
+
+ [DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
+ internal extern static void monodroid_free (IntPtr ptr);
+
+ [DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
+ internal extern static IntPtr _monodroid_get_identity_hash_code (IntPtr env, IntPtr value);
+
+ [DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int _monodroid_gref_get ();
+
+ [DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int _monodroid_weak_gref_get ();
+
+ [DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr _monodroid_lookup_replacement_type (string jniSimpleReference);
+
+ [DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr _monodroid_lookup_replacement_method_info (string jniSourceType, string jniMethodName, string jniMethodSignature);
+
+ [DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr _monodroid_timezone_get_default_id ();
+
+ [DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int _monodroid_getifaddrs (out IntPtr ifap);
+
+ [DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern void _monodroid_freeifaddrs (IntPtr ifap);
+
+ [DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern void _monodroid_detect_cpu_and_architecture (ref ushort built_for_cpu, ref ushort running_on_cpu, ref byte is64bit);
+
+ [DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
+ internal extern static void _monodroid_gc_wait_for_bridge_processing ();
+
+ [DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int _monodroid_gref_log (string message);
+
+ [DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int _monodroid_gref_log_new (IntPtr curHandle, byte curType, IntPtr newHandle, byte newType, string? threadName, int threadId, [In] StringBuilder? from, int from_writable);
+
+ [DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern void _monodroid_gref_log_delete (IntPtr handle, byte type, string? threadName, int threadId, [In] StringBuilder? from, int from_writable);
+
+ [DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern void _monodroid_weak_gref_new (IntPtr curHandle, byte curType, IntPtr newHandle, byte newType, string? threadName, int threadId, [In] StringBuilder? from, int from_writable);
+
+ [DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern void _monodroid_weak_gref_delete (IntPtr handle, byte type, string? threadName, int threadId, [In] StringBuilder? from, int from_writable);
+
+ [DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int _monodroid_lref_log_new (int lrefc, IntPtr handle, byte type, string? threadName, int threadId, [In] StringBuilder from, int from_writable);
+
+ [DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern void _monodroid_lref_log_delete (int lrefc, IntPtr handle, byte type, string? threadName, int threadId, [In] StringBuilder from, int from_writable);
+
+ [DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern IntPtr monodroid_TypeManager_get_java_class_name (IntPtr klass);
+
+ [DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
+ internal static extern int _monodroid_max_gref_get ();
+#if NETCOREAPP
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern void monodroid_unhandled_exception (Exception javaException);
+
+ [MethodImplAttribute(MethodImplOptions.InternalCall)]
+ internal static extern unsafe void monodroid_debugger_unhandled_exception (Exception e);
+#endif
+ }
+}
+#endif // !NETCOREAPP || INSIDE_MONO_ANDROID_RUNTIME
diff --git a/src/Mono.Android/Android.Runtime/TimingLogger.cs b/src/Mono.Android/Android.Runtime/TimingLogger.cs
index 9bbbf538c0a..e34ec0d701e 100644
--- a/src/Mono.Android/Android.Runtime/TimingLogger.cs
+++ b/src/Mono.Android/Android.Runtime/TimingLogger.cs
@@ -45,7 +45,7 @@ public void Start (string? startMessage = null)
if (sequence != IntPtr.Zero)
return;
- sequence = JNIEnv.monodroid_timing_start (startMessage ?? initStartMessage);
+ sequence = RuntimeNativeMethods.monodroid_timing_start (startMessage ?? initStartMessage);
}
///
@@ -65,7 +65,7 @@ public void Stop (string stopMessage)
if (sequence == IntPtr.Zero)
return;
- JNIEnv.monodroid_timing_stop (sequence, stopMessage);
+ RuntimeNativeMethods.monodroid_timing_stop (sequence, stopMessage);
sequence = IntPtr.Zero;
}
@@ -86,7 +86,7 @@ protected virtual void Dispose (bool disposing)
{
if (!disposed) {
if (sequence != IntPtr.Zero) {
- JNIEnv.monodroid_timing_stop (sequence, null);
+ RuntimeNativeMethods.monodroid_timing_stop (sequence, null);
sequence = IntPtr.Zero;
}
diff --git a/src/Mono.Android/Java.Interop/Runtime.cs b/src/Mono.Android/Java.Interop/Runtime.cs
index 4ebf234200e..ceb14ca9d87 100644
--- a/src/Mono.Android/Java.Interop/Runtime.cs
+++ b/src/Mono.Android/Java.Interop/Runtime.cs
@@ -11,7 +11,7 @@ public static class Runtime {
[Obsolete ("Please use Java.Interop.JniEnvironment.Runtime.ValueManager.GetSurfacedPeers()")]
public static List GetSurfacedObjects ()
{
- var peers = JNIEnv.AndroidValueManager!.GetSurfacedPeers ();
+ var peers = JNIEnvInit.AndroidValueManager!.GetSurfacedPeers ();
var r = new List (peers.Count);
foreach (var p in peers) {
if (p.SurfacedPeer.TryGetTarget (out var target))
@@ -20,18 +20,12 @@ public static List GetSurfacedObjects ()
return r;
}
- [DllImport (AndroidRuntime.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
- static extern int _monodroid_max_gref_get ();
-
public static int MaxGlobalReferenceCount {
- get {return _monodroid_max_gref_get ();}
+ get {return RuntimeNativeMethods._monodroid_max_gref_get ();}
}
- [DllImport (AndroidRuntime.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
- static extern int _monodroid_gref_get ();
-
public static int GlobalReferenceCount {
- get {return _monodroid_gref_get ();}
+ get {return RuntimeNativeMethods._monodroid_gref_get ();}
}
public static int LocalReferenceCount {
diff --git a/src/Mono.Android/Java.Interop/TypeManager.cs b/src/Mono.Android/Java.Interop/TypeManager.cs
index 0c32fb53b84..e70d00bc424 100644
--- a/src/Mono.Android/Java.Interop/TypeManager.cs
+++ b/src/Mono.Android/Java.Interop/TypeManager.cs
@@ -38,14 +38,11 @@ public static Dictionary ManagedToJni {
}
public static partial class TypeManager {
- [DllImport (AndroidRuntime.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
- extern static IntPtr monodroid_TypeManager_get_java_class_name (IntPtr klass);
-
internal static string GetClassName (IntPtr class_ptr)
{
- IntPtr ptr = monodroid_TypeManager_get_java_class_name (class_ptr);
+ IntPtr ptr = RuntimeNativeMethods.monodroid_TypeManager_get_java_class_name (class_ptr);
string ret = Marshal.PtrToStringAnsi (ptr)!;
- JNIEnv.monodroid_free (ptr);
+ RuntimeNativeMethods.monodroid_free (ptr);
return ret;
}
@@ -144,7 +141,7 @@ static void n_Activate (IntPtr jnienv, IntPtr jclass, IntPtr typename_ptr, IntPt
string.Format ("warning: Skipping managed constructor invocation for handle 0x{0} (key_handle 0x{1}). " +
"Please use JNIEnv.StartCreateInstance() + JNIEnv.FinishCreateInstance() instead of " +
"JNIEnv.NewObject() and/or JNIEnv.CreateInstance().",
- jobject.ToString ("x"), JNIEnv.IdentityHash! (jobject).ToString ("x")));
+ jobject.ToString ("x"), JNIEnv.IdentityHash (jobject).ToString ("x")));
}
return;
}
@@ -183,7 +180,7 @@ internal static void Activate (IntPtr jobject, ConstructorInfo cinfo, object? []
cinfo.Invoke (newobj, parms);
} catch (Exception e) {
var m = string.Format ("Could not activate JNI Handle 0x{0} (key_handle 0x{1}) of Java type '{2}' as managed type '{3}'.",
- jobject.ToString ("x"), JNIEnv.IdentityHash! (jobject).ToString ("x"), JNIEnv.GetClassNameFromInstance (jobject), cinfo.DeclaringType.FullName);
+ jobject.ToString ("x"), JNIEnv.IdentityHash (jobject).ToString ("x"), JNIEnv.GetClassNameFromInstance (jobject), cinfo.DeclaringType.FullName);
Logger.Log (LogLevel.Warn, "monodroid", m);
Logger.Log (LogLevel.Warn, "monodroid", CreateJavaLocationException ().ToString ());
@@ -224,9 +221,9 @@ static Exception CreateJavaLocationException ()
if (type != null)
return type;
- if (!JNIEnv.IsRunningOnDesktop) {
+ if (!JNIEnvInit.IsRunningOnDesktop) {
// Miss message is logged in the native runtime
- if (JNIEnv.LogAssemblyCategory)
+ if (JNIEnvInit.LogAssemblyCategory)
JNIEnv.LogTypemapTrace (new System.Diagnostics.StackTrace (true));
return null;
}
@@ -314,7 +311,7 @@ internal static IJavaPeerable CreateInstance (IntPtr handle, JniHandleOwnership
result.SetJniManagedPeerState (JniManagedPeerStates.Replaceable);
}
} catch (MissingMethodException e) {
- var key_handle = JNIEnv.IdentityHash! (handle);
+ var key_handle = JNIEnv.IdentityHash (handle);
JNIEnv.DeleteRef (handle, transfer);
throw new NotSupportedException (
string.Format ("Unable to activate instance of type {0} from native handle 0x{1} (key_handle 0x{2}).",
@@ -359,7 +356,7 @@ public static void RegisterType (string java_class, Type t)
if (String.Compare (jniFromType, java_class, StringComparison.OrdinalIgnoreCase) != 0) {
TypeManagerMapDictionaries.ManagedToJni.Add (t, java_class);
}
- } else if (!JNIEnv.IsRunningOnDesktop || t != typeof (Java.Interop.TypeManager)) {
+ } else if (!JNIEnvInit.IsRunningOnDesktop || t != typeof (Java.Interop.TypeManager)) {
// skip the registration and output a warning
Logger.Log (LogLevel.Warn, "monodroid", string.Format ("Type Registration Skipped for {0} to {1} ", java_class, t.ToString()));
}
@@ -379,7 +376,7 @@ public static void RegisterPackage (string package, Converter look
{
LazyInitPackageLookup ();
- lock (packageLookup) {
+ lock (packageLookup!) {
if (!packageLookup.TryGetValue (package, out var lookups))
packageLookup.Add (package, lookups = new List> ());
lookups.Add (lookup);
@@ -397,7 +394,7 @@ public static void RegisterPackages (string[] packages, Converter
if (packages.Length != lookups.Length)
throw new ArgumentException ("`packages` and `lookups` arrays must have same number of elements.");
- lock (packageLookup) {
+ lock (packageLookup!) {
for (int i = 0; i < packages.Length; ++i) {
string package = packages [i];
var lookup = lookups [i];
@@ -418,6 +415,18 @@ static void n_Activate (IntPtr jnienv, IntPtr jclass, IntPtr typename_ptr, IntPt
TypeManager.n_Activate (jnienv, jclass, typename_ptr, signature_ptr, jobject, parameters_ptr);
}
+#if NETCOREAPP
+ [UnmanagedCallersOnly]
+ static void n_Activate_mm (IntPtr jnienv, IntPtr jclass, IntPtr typename_ptr, IntPtr signature_ptr, IntPtr jobject, IntPtr parameters_ptr)
+ {
+ // TODO: need a full wrapper code here, a'la JNINativeWrapper.CreateDelegate
+ try {
+ TypeManager.n_Activate (jnienv, jclass, typename_ptr, signature_ptr, jobject, parameters_ptr);
+ } catch (Exception ex) {
+ AndroidEnvironment.UnhandledException (ex);
+ }
+ }
+#endif
internal static Delegate GetActivateHandler ()
{
return TypeManager.GetActivateHandler ();
diff --git a/src/Mono.Android/Java.Lang/Object.cs b/src/Mono.Android/Java.Lang/Object.cs
index 76618d7026e..e968f0369ff 100644
--- a/src/Mono.Android/Java.Lang/Object.cs
+++ b/src/Mono.Android/Java.Lang/Object.cs
@@ -170,7 +170,7 @@ void IJavaPeerable.DisposeUnlessReferenced ()
[EditorBrowsable (EditorBrowsableState.Never)]
public void UnregisterFromRuntime ()
{
- JNIEnv.AndroidValueManager?.RemovePeer (this, key_handle);
+ JNIEnvInit.AndroidValueManager?.RemovePeer (this, key_handle);
}
void IJavaPeerable.Disposed ()
@@ -207,7 +207,7 @@ void IJavaPeerable.SetPeerReference (JniObjectReference reference)
public void Dispose ()
{
- JNIEnv.AndroidValueManager?.DisposePeer (this);
+ JNIEnvInit.AndroidValueManager?.DisposePeer (this);
}
protected virtual void Dispose (bool disposing)
@@ -220,11 +220,11 @@ internal static void Dispose (IJavaPeerable instance, ref IntPtr handle, IntPtr
return;
if (Logger.LogGlobalRef) {
- JNIEnv._monodroid_gref_log (
+ RuntimeNativeMethods._monodroid_gref_log (
string.Format ("Disposing handle 0x{0}\n", handle.ToString ("x")));
}
- JNIEnv.AndroidValueManager?.RemovePeer (instance, key_handle);
+ JNIEnvInit.AndroidValueManager?.RemovePeer (instance, key_handle);
switch (handle_type) {
case JObjectRefType.Global:
@@ -248,13 +248,13 @@ internal static void Dispose (IJavaPeerable instance, ref IntPtr handle, IntPtr
[EditorBrowsable (EditorBrowsableState.Never)]
protected void SetHandle (IntPtr value, JniHandleOwnership transfer)
{
- JNIEnv.AndroidValueManager?.AddPeer (this, value, transfer, out handle);
+ JNIEnvInit.AndroidValueManager?.AddPeer (this, value, transfer, out handle);
handle_type = JObjectRefType.Global;
}
internal static IJavaPeerable? PeekObject (IntPtr handle, Type? requiredType = null)
{
- var peeked = JNIEnv.AndroidValueManager?.PeekPeer (new JniObjectReference (handle));
+ var peeked = JNIEnvInit.AndroidValueManager?.PeekPeer (new JniObjectReference (handle));
if (peeked == null)
return null;
if (requiredType != null && !requiredType.IsAssignableFrom (peeked.GetType ()))
diff --git a/src/Mono.Android/Java.Lang/Throwable.cs b/src/Mono.Android/Java.Lang/Throwable.cs
index b483c557c76..5dc63ef7952 100644
--- a/src/Mono.Android/Java.Lang/Throwable.cs
+++ b/src/Mono.Android/Java.Lang/Throwable.cs
@@ -241,7 +241,7 @@ public unsafe Java.Lang.Class? Class {
[EditorBrowsable (EditorBrowsableState.Never)]
protected void SetHandle (IntPtr value, JniHandleOwnership transfer)
{
- JNIEnv.AndroidValueManager?.AddPeer (this, value, transfer, out handle);
+ JNIEnvInit.AndroidValueManager?.AddPeer (this, value, transfer, out handle);
handle_type = JObjectRefType.Global;
}
@@ -296,7 +296,7 @@ void IJavaPeerable.DisposeUnlessReferenced ()
public void UnregisterFromRuntime ()
{
- JNIEnv.AndroidValueManager?.RemovePeer (this, key_handle);
+ JNIEnvInit.AndroidValueManager?.RemovePeer (this, key_handle);
}
void IJavaPeerable.Disposed ()
@@ -332,7 +332,7 @@ void IJavaPeerable.SetPeerReference (JniObjectReference reference)
public void Dispose ()
{
- JNIEnv.AndroidValueManager?.DisposePeer (this);
+ JNIEnvInit.AndroidValueManager?.DisposePeer (this);
}
protected virtual void Dispose (bool disposing)
@@ -340,4 +340,3 @@ protected virtual void Dispose (bool disposing)
}
}
}
-
diff --git a/src/Mono.Android/Mono.Android.csproj b/src/Mono.Android/Mono.Android.csproj
index 97e25263da6..1fdbe977281 100644
--- a/src/Mono.Android/Mono.Android.csproj
+++ b/src/Mono.Android/Mono.Android.csproj
@@ -229,6 +229,7 @@
+
@@ -249,6 +250,7 @@
+
@@ -259,6 +261,8 @@
+
+
@@ -266,7 +270,9 @@
+
+
@@ -387,6 +393,10 @@
+
+
+
+
diff --git a/src/Mono.Android/java/mono/android/TypeManager.java b/src/Mono.Android/java/mono/android/TypeManager.java
index 6915cb96034..bf64780bf9e 100644
--- a/src/Mono.Android/java/mono/android/TypeManager.java
+++ b/src/Mono.Android/java/mono/android/TypeManager.java
@@ -9,10 +9,12 @@ public static void Activate (String typeName, String sig, Object instance, Objec
private static native void n_activate (String typeName, String sig, Object instance, Object[] parameterList);
+//#MARSHAL_METHODS:START - do not remove or modify this line, it is required during application build
static {
String methods =
"n_activate:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/Object;)V:GetActivateHandler\n" +
"";
mono.android.Runtime.register ("Java.Interop.TypeManager+JavaTypeManager, Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null", TypeManager.class, methods);
}
+//#MARSHAL_METHODS:END - do not remove or modify this line, it is required during application build
}
diff --git a/src/Xamarin.Android.Build.Tasks/Linker/PreserveLists/Mono.Android.xml b/src/Xamarin.Android.Build.Tasks/Linker/PreserveLists/Mono.Android.xml
index 3c4d22fbfe0..dbcd130d347 100644
--- a/src/Xamarin.Android.Build.Tasks/Linker/PreserveLists/Mono.Android.xml
+++ b/src/Xamarin.Android.Build.Tasks/Linker/PreserveLists/Mono.Android.xml
@@ -13,6 +13,7 @@
+
diff --git a/src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Aapt2.targets b/src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Aapt2.targets
index 009618373d4..ce005fa8959 100644
--- a/src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Aapt2.targets
+++ b/src/Xamarin.Android.Build.Tasks/MSBuild/Xamarin/Android/Xamarin.Android.Aapt2.targets
@@ -208,7 +208,9 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
<_ProtobufFormat Condition=" '$(AndroidPackageFormat)' == 'aab' ">True
<_ProtobufFormat Condition=" '$(_ProtobufFormat)' == '' ">False
<_Aapt2ProguardRules Condition=" '$(AndroidLinkTool)' != '' ">$(IntermediateOutputPath)aapt_rules.txt
+ <_OutputFileDir>$([System.IO.Path]::GetDirectoryName ('$(_PackagedResources)'))
+
+ <_AotResolvedFileToPublish
+ Include="@(_AotCompiledAssemblies)"
+ Condition=" '$(_AndroidUseMarshalMethods)' == 'True' "
+ ArchiveFileName="libaot-$([System.IO.Path]::GetFileNameWithoutExtension('%(_AotCompiledAssemblies.Identity)')).so"
+ />
+
+
+
+
+
+ <_AotResolvedFileToPublish Remove="@(_SourceItemsToCopyToPublishDirectory)" />
+ <_AotResolvedFileToPublish
+ Condition=" '%(_AotResolvedFileToPublish.RuntimeIdentifier)' == '' "
+ Update="@(_AotResolvedFileToPublish)"
+ RuntimeIdentifier="$(RuntimeIdentifier)"
+ />
diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets
index 188ddf12e42..3ccabedc09d 100644
--- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets
+++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.AssemblyResolution.targets
@@ -38,8 +38,13 @@ _ResolveAssemblies MSBuild target.
+
+
+ <_RunAotMaybe Condition=" '$(_AndroidUseMarshalMethods)' != 'True' ">_AndroidAot
+
+
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/CreateTypeManagerJava.cs b/src/Xamarin.Android.Build.Tasks/Tasks/CreateTypeManagerJava.cs
new file mode 100644
index 00000000000..37872f31927
--- /dev/null
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/CreateTypeManagerJava.cs
@@ -0,0 +1,79 @@
+using System.IO;
+using System.Reflection;
+using System.Text;
+using System;
+
+using Microsoft.Android.Build.Tasks;
+using Microsoft.Build.Framework;
+
+namespace Xamarin.Android.Tasks
+{
+ public class CreateTypeManagerJava : AndroidTask
+ {
+ public override string TaskPrefix => "CTMJ";
+
+ [Required]
+ public string ResourceName { get; set; }
+
+ [Required]
+ public string OutputFilePath { get; set; }
+
+ static readonly Assembly ExecutingAssembly = Assembly.GetExecutingAssembly ();
+
+ public override bool RunTask ()
+ {
+ string? content = ReadResource (ResourceName);
+
+ if (String.IsNullOrEmpty (content)) {
+ return false;
+ }
+
+ var result = new StringBuilder ();
+ bool ignoring = false;
+ foreach (string line in content.Split ('\n')) {
+ if (!ignoring) {
+ if (ignoring = line.StartsWith ("//#MARSHAL_METHODS:START", StringComparison.Ordinal)) {
+ continue;
+ }
+ result.AppendLine (line);
+ } else if (line.StartsWith ("//#MARSHAL_METHODS:END", StringComparison.Ordinal)) {
+ ignoring = false;
+ }
+ }
+
+ if (result.Length == 0) {
+ Log.LogDebugMessage ("TypeManager.java not generated, empty resource data");
+ return false;
+ }
+
+ using (var ms = new MemoryStream ()) {
+ using (var sw = new StreamWriter (ms)) {
+ sw.Write (result.ToString ());
+ sw.Flush ();
+
+ if (Files.CopyIfStreamChanged (ms, OutputFilePath)) {
+ Log.LogDebugMessage ($"Wrote resource {OutputFilePath}.");
+ } else {
+ Log.LogDebugMessage ($"Resource {OutputFilePath} is unchanged. Skipping.");
+ }
+ }
+ }
+
+ return !Log.HasLoggedErrors;
+ }
+
+ string? ReadResource (string resourceName)
+ {
+ using (var from = ExecutingAssembly.GetManifestResourceStream (resourceName)) {
+ if (from == null) {
+ Log.LogCodedError ("XA0116", Properties.Resources.XA0116, resourceName);
+ return null;
+ }
+
+ using (var sr = new StreamReader (from)) {
+ return sr.ReadToEnd ();
+ }
+ }
+ }
+ }
+}
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs
index 6ff7428ad7e..4053a0f9b1e 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs
@@ -51,6 +51,10 @@ public class GenerateJavaStubs : AndroidTask
[Required]
public bool GenerateNativeAssembly { get; set; }
+ public string IntermediateOutputDirectory { get; set; }
+ public bool LinkingEnabled { get; set; }
+ public bool HaveMultipleRIDs { get; set; }
+ public bool EnableMarshalMethods { get; set; }
public string ManifestTemplate { get; set; }
public string[] MergedManifestDocuments { get; set; }
@@ -86,6 +90,8 @@ public class GenerateJavaStubs : AndroidTask
public string SupportedOSPlatformVersion { get; set; }
+ public ITaskItem[] Environments { get; set; }
+
[Output]
public string [] GeneratedBinaryTypeMaps { get; set; }
@@ -96,8 +102,8 @@ public override bool RunTask ()
try {
// We're going to do 3 steps here instead of separate tasks so
// we can share the list of JLO TypeDefinitions between them
- using (var res = new DirectoryAssemblyResolver (this.CreateTaskLogger (), loadDebugSymbols: true)) {
- Run (res);
+ using (DirectoryAssemblyResolver res = MakeResolver ()) {
+ Run (res, useMarshalMethods: !Debug && EnableMarshalMethods);
}
} catch (XamarinAndroidException e) {
Log.LogCodedError (string.Format ("XA{0:0000}", e.Code), e.MessageWithoutCode);
@@ -115,21 +121,33 @@ public override bool RunTask ()
return !Log.HasLoggedErrors;
}
- void Run (DirectoryAssemblyResolver res)
+ DirectoryAssemblyResolver MakeResolver ()
{
- PackageNamingPolicy pnp;
- JavaNativeTypeManager.PackageNamingPolicy = Enum.TryParse (PackageNamingPolicy, out pnp) ? pnp : PackageNamingPolicyEnum.LowercaseCrc64;
+ var readerParams = new ReaderParameters {
+ ReadWrite = true,
+ InMemory = true,
+ };
+ var res = new DirectoryAssemblyResolver (this.CreateTaskLogger (), loadDebugSymbols: true, loadReaderParameters: readerParams);
foreach (var dir in FrameworkDirectories) {
- if (Directory.Exists (dir.ItemSpec))
+ if (Directory.Exists (dir.ItemSpec)) {
res.SearchDirectories.Add (dir.ItemSpec);
+ }
}
-#if ENABLE_MARSHAL_METHODS
+
+ return res;
+ }
+
+ void Run (DirectoryAssemblyResolver res, bool useMarshalMethods)
+ {
+ PackageNamingPolicy pnp;
+ JavaNativeTypeManager.PackageNamingPolicy = Enum.TryParse (PackageNamingPolicy, out pnp) ? pnp : PackageNamingPolicyEnum.LowercaseCrc64;
+
Dictionary> marshalMethodsAssemblyPaths = null;
- if (!Debug) {
+ if (useMarshalMethods) {
marshalMethodsAssemblyPaths = new Dictionary> (StringComparer.Ordinal);
}
-#endif
+
// Put every assembly we'll need in the resolver
bool hasExportReference = false;
bool haveMonoAndroid = false;
@@ -164,11 +182,9 @@ void Run (DirectoryAssemblyResolver res)
}
res.Load (assembly.ItemSpec);
-#if ENABLE_MARSHAL_METHODS
- if (!Debug) {
+ if (useMarshalMethods) {
StoreMarshalAssemblyPath (Path.GetFileNameWithoutExtension (assembly.ItemSpec), assembly);
}
-#endif
}
// However we only want to look for JLO types in user code for Java stub code generation
@@ -182,9 +198,7 @@ void Run (DirectoryAssemblyResolver res)
string name = Path.GetFileNameWithoutExtension (asm.ItemSpec);
if (!userAssemblies.ContainsKey (name))
userAssemblies.Add (name, asm.ItemSpec);
-#if ENABLE_MARSHAL_METHODS
StoreMarshalAssemblyPath (name, asm);
-#endif
}
// Step 1 - Find all the JLO types
@@ -197,7 +211,10 @@ void Run (DirectoryAssemblyResolver res)
var javaTypes = new List ();
foreach (TypeDefinition td in allJavaTypes) {
- if (!userAssemblies.ContainsKey (td.Module.Assembly.Name.Name) || JavaTypeScanner.ShouldSkipJavaCallableWrapperGeneration (td, cache)) {
+ // Whem marshal methods are in use we do not want to skip non-user assemblies (such as Mono.Android) - we need to generate JCWs for them during
+ // application build, unlike in Debug configuration or when marshal methods are disabled, in which case we use JCWs generated during Xamarin.Android
+ // build and stored in a jar file.
+ if ((!useMarshalMethods && !userAssemblies.ContainsKey (td.Module.Assembly.Name.Name)) || JavaTypeScanner.ShouldSkipJavaCallableWrapperGeneration (td, cache)) {
continue;
}
@@ -205,25 +222,43 @@ void Run (DirectoryAssemblyResolver res)
}
MarshalMethodsClassifier classifier = null;
-#if ENABLE_MARSHAL_METHODS
- if (!Debug) {
+ if (useMarshalMethods) {
classifier = new MarshalMethodsClassifier (cache, res, Log);
}
-#endif
+
// Step 2 - Generate Java stub code
- var success = CreateJavaSources (javaTypes, cache, classifier);
+ var success = CreateJavaSources (javaTypes, cache, classifier, useMarshalMethods);
if (!success)
return;
-#if ENABLE_MARSHAL_METHODS
- if (!Debug) {
- // TODO: we must rewrite assemblies for all SupportedAbis. Alternatively, we need to copy the ones that are identical
- // Cecil does **not** guarantee that the same assembly modified twice in the same will yield the same result - tokens may differ, so can
- // MVID.
+ if (useMarshalMethods) {
+ // We need to parse the environment files supplied by the user to see if they want to use broken exception transitions. This information is needed
+ // in order to properly generate wrapper methods in the marshal methods assembly rewriter.
+ // We don't care about those generated by us, since they won't contain the `XA_BROKEN_EXCEPTION_TRANSITIONS` variable we look for.
+ var environmentParser = new EnvironmentFilesParser ();
+ var targetPaths = new List ();
+
+ if (!LinkingEnabled) {
+ targetPaths.Add (Path.GetDirectoryName (ResolvedAssemblies[0].ItemSpec));
+ } else {
+ if (String.IsNullOrEmpty (IntermediateOutputDirectory)) {
+ throw new InvalidOperationException ($"Internal error: marshal methods require the `IntermediateOutputDirectory` property of the `GenerateJavaStubs` task to have a value");
+ }
+
+ // If the property is set then, even if we have just one RID, the linked assemblies path will include the RID
+ if (!HaveMultipleRIDs && SupportedAbis.Length == 1) {
+ targetPaths.Add (Path.Combine (IntermediateOutputDirectory, "linked"));
+ } else {
+ foreach (string abi in SupportedAbis) {
+ targetPaths.Add (Path.Combine (IntermediateOutputDirectory, AbiToRid (abi), "linked"));
+ }
+ }
+ }
+
var rewriter = new MarshalMethodsAssemblyRewriter (classifier.MarshalMethods, classifier.Assemblies, marshalMethodsAssemblyPaths, Log);
- rewriter.Rewrite (res);
+ rewriter.Rewrite (res, targetPaths, environmentParser.AreBrokenExceptionTransitionsEnabled (Environments));
}
-#endif
+
// Step 3 - Generate type maps
// Type mappings need to use all the assemblies, always.
WriteTypeMappings (allJavaTypes, cache);
@@ -240,9 +275,6 @@ void Run (DirectoryAssemblyResolver res)
string managedKey = type.FullName.Replace ('/', '.');
string javaKey = JavaNativeTypeManager.ToJniName (type, cache).Replace ('/', '.');
-#if ENABLE_MARSHAL_METHODS
- Console.WriteLine ($"##G2: {type.FullName} -> {javaKey}");
-#endif
acw_map.Write (type.GetPartialAssemblyQualifiedName (cache));
acw_map.Write (';');
acw_map.Write (javaKey);
@@ -352,11 +384,10 @@ void Run (DirectoryAssemblyResolver res)
regCallsWriter.WriteLine ("\t\t// Application and Instrumentation ACWs must be registered first.");
foreach (var type in javaTypes) {
if (JavaNativeTypeManager.IsApplication (type, cache) || JavaNativeTypeManager.IsInstrumentation (type, cache)) {
-#if ENABLE_MARSHAL_METHODS
- if (!classifier.FoundDynamicallyRegisteredMethods (type)) {
+ if (classifier != null && !classifier.FoundDynamicallyRegisteredMethods (type)) {
continue;
}
-#endif
+
string javaKey = JavaNativeTypeManager.ToJniName (type, cache).Replace ('/', '.');
regCallsWriter.WriteLine ("\t\tmono.android.Runtime.register (\"{0}\", {1}.class, {1}.__md_methods);",
type.GetAssemblyQualifiedName (cache), javaKey);
@@ -369,8 +400,9 @@ void Run (DirectoryAssemblyResolver res)
SaveResource (applicationTemplateFile, applicationTemplateFile, real_app_dir,
template => template.Replace ("// REGISTER_APPLICATION_AND_INSTRUMENTATION_CLASSES_HERE", regCallsWriter.ToString ()));
-#if ENABLE_MARSHAL_METHODS
- if (!Debug) {
+ if (useMarshalMethods) {
+ classifier.AddSpecialCaseMethods ();
+
Log.LogDebugMessage ($"Number of generated marshal methods: {classifier.MarshalMethods.Count}");
if (classifier.RejectedMethodCount > 0) {
@@ -378,13 +410,14 @@ void Run (DirectoryAssemblyResolver res)
}
if (classifier.WrappedMethodCount > 0) {
- Log.LogWarning ($"Number of methods in the project that need marshal method wrappers: {classifier.WrappedMethodCount}");
+ // TODO: change to LogWarning once the generator can output code which requires no non-blittable wrappers
+ Log.LogDebugMessage ($"Number of methods in the project that need marshal method wrappers: {classifier.WrappedMethodCount}");
}
}
void StoreMarshalAssemblyPath (string name, ITaskItem asm)
{
- if (Debug) {
+ if (!useMarshalMethods) {
return;
}
@@ -396,11 +429,34 @@ void StoreMarshalAssemblyPath (string name, ITaskItem asm)
assemblyPaths.Add (asm.ItemSpec);
}
-#endif
+
+ string AbiToRid (string abi)
+ {
+ switch (abi) {
+ case "arm64-v8a":
+ return "android-arm64";
+
+ case "armeabi-v7a":
+ return "android-arm";
+
+ case "x86":
+ return "android-x86";
+
+ case "x86_64":
+ return "android-x64";
+
+ default:
+ throw new InvalidOperationException ($"Internal error: unsupported ABI '{abi}'");
+ }
+ }
}
- bool CreateJavaSources (IEnumerable javaTypes, TypeDefinitionCache cache, MarshalMethodsClassifier classifier)
+ bool CreateJavaSources (IEnumerable javaTypes, TypeDefinitionCache cache, MarshalMethodsClassifier classifier, bool useMarshalMethods)
{
+ if (useMarshalMethods && classifier == null) {
+ throw new ArgumentNullException (nameof (classifier));
+ }
+
string outputPath = Path.Combine (OutputDirectory, "src");
string monoInit = GetMonoInitSource (AndroidSdkPlatform);
bool hasExportReference = ResolvedAssemblies.Any (assembly => Path.GetFileName (assembly.ItemSpec) == "Mono.Android.Export.dll");
@@ -408,9 +464,6 @@ bool CreateJavaSources (IEnumerable javaTypes, TypeDefinitionCac
bool ok = true;
foreach (var t in javaTypes) {
-#if ENABLE_MARSHAL_METHODS
- Console.WriteLine ($"##G0: JCW for {t.FullName}");
-#endif
if (t.IsInterface) {
// Interfaces are in typemap but they shouldn't have JCW generated for them
continue;
@@ -425,13 +478,11 @@ bool CreateJavaSources (IEnumerable javaTypes, TypeDefinitionCac
};
jti.Generate (writer);
-#if ENABLE_MARSHAL_METHODS
- if (!Debug) {
+ if (useMarshalMethods) {
if (classifier.FoundDynamicallyRegisteredMethods (t)) {
Log.LogWarning ($"Type '{t.GetAssemblyQualifiedName ()}' will register some of its Java override methods dynamically. This may adversely affect runtime performance. See preceding warnings for names of dynamically registered methods.");
}
}
-#endif
writer.Flush ();
var path = jti.GetDestinationPath (outputPath);
@@ -465,11 +516,11 @@ bool CreateJavaSources (IEnumerable javaTypes, TypeDefinitionCac
}
}
}
-#if ENABLE_MARSHAL_METHODS
- if (!Debug) {
+
+ if (useMarshalMethods) {
BuildEngine4.RegisterTaskObjectAssemblyLocal (MarshalMethodsRegisterTaskKey, new MarshalMethodsState (classifier.MarshalMethods), RegisteredTaskObjectLifetime.Build);
}
-#endif
+
return ok;
}
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs
index e06e300a462..e8638b29761 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs
@@ -68,6 +68,7 @@ public class GeneratePackageManagerJava : AndroidTask
[Required]
public bool InstantRunEnabled { get; set; }
+ public bool EnableMarshalMethods { get; set; }
public string RuntimeConfigBinFilePath { get; set; }
public string BoundExceptionType { get; set; }
@@ -175,8 +176,6 @@ static internal AndroidTargetArch GetAndroidTargetArchForAbi (string abi)
void AddEnvironment ()
{
bool usesMonoAOT = false;
- bool usesAssemblyPreload = EnablePreloadAssembliesDefault;
- bool brokenExceptionTransitions = false;
var environmentVariables = new Dictionary (StringComparer.Ordinal);
var systemProperties = new Dictionary (StringComparer.Ordinal);
@@ -189,84 +188,48 @@ void AddEnvironment ()
usesMonoAOT = true;
}
- bool haveLogLevel = false;
- bool haveMonoDebug = false;
- bool havebuildId = false;
- bool haveHttpMessageHandler = false;
- bool haveTlsProvider = false;
- bool haveMonoGCParams = false;
-
SequencePointsMode sequencePointsMode;
if (!Aot.TryGetSequencePointsMode (AndroidSequencePointsMode, out sequencePointsMode))
sequencePointsMode = SequencePointsMode.None;
- foreach (ITaskItem env in Environments ?? Array.Empty ()) {
- foreach (string line in File.ReadLines (env.ItemSpec)) {
- var lineToWrite = line;
- if (lineToWrite.StartsWith ("MONO_LOG_LEVEL=", StringComparison.Ordinal))
- haveLogLevel = true;
- if (lineToWrite.StartsWith ("MONO_GC_PARAMS=", StringComparison.Ordinal)) {
- haveMonoGCParams = true;
- if (lineToWrite.IndexOf ("bridge-implementation=old", StringComparison.Ordinal) >= 0) {
- Log.LogCodedWarning ("XA2000", Properties.Resources.XA2000_gcParams_bridgeImpl);
- }
- }
- if (lineToWrite.StartsWith ("XAMARIN_BUILD_ID=", StringComparison.Ordinal))
- havebuildId = true;
- if (lineToWrite.StartsWith ("MONO_DEBUG=", StringComparison.Ordinal)) {
- haveMonoDebug = true;
- if (sequencePointsMode != SequencePointsMode.None && !lineToWrite.Contains ("gen-compact-seq-points"))
- lineToWrite = line + ",gen-compact-seq-points";
- }
- if (lineToWrite.StartsWith ("XA_HTTP_CLIENT_HANDLER_TYPE=", StringComparison.Ordinal))
- haveHttpMessageHandler = true;
-
- if (!UsingAndroidNETSdk && lineToWrite.StartsWith ("XA_TLS_PROVIDER=", StringComparison.Ordinal))
- haveTlsProvider = true;
-
- if (lineToWrite.StartsWith ("mono.enable_assembly_preload=", StringComparison.Ordinal)) {
- int idx = lineToWrite.IndexOf ('=');
- uint val;
- if (idx < lineToWrite.Length - 1 && UInt32.TryParse (lineToWrite.Substring (idx + 1), out val)) {
- usesAssemblyPreload = idx == 1;
- }
- continue;
- }
- if (lineToWrite.StartsWith ("XA_BROKEN_EXCEPTION_TRANSITIONS=", StringComparison.Ordinal)) {
- brokenExceptionTransitions = true;
- continue;
- }
+ // Even though environment files were potentially parsed in GenerateJavaStubs, we need to do it here again because we might have additional environment
+ // files (generated by us) which weren't present by the time GeneratJavaStubs ran.
+ var environmentParser = new EnvironmentFilesParser {
+ BrokenExceptionTransitions = false,
+ UsesAssemblyPreload = EnablePreloadAssembliesDefault,
+ };
+ environmentParser.Parse (Environments, sequencePointsMode, UsingAndroidNETSdk, Log);
- AddEnvironmentVariableLine (lineToWrite);
- }
+ foreach (string line in environmentParser.EnvironmentVariableLines) {
+ AddEnvironmentVariableLine (line);
}
- if (_Debug && !haveLogLevel) {
+ if (_Debug && !environmentParser.HaveLogLevel) {
AddEnvironmentVariable (defaultLogLevel[0], defaultLogLevel[1]);
}
- if (sequencePointsMode != SequencePointsMode.None && !haveMonoDebug) {
+ if (sequencePointsMode != SequencePointsMode.None && !environmentParser.HaveMonoDebug) {
AddEnvironmentVariable (defaultMonoDebug[0], defaultMonoDebug[1]);
}
- if (!havebuildId)
+ if (!environmentParser.HavebuildId)
AddEnvironmentVariable ("XAMARIN_BUILD_ID", BuildId);
- if (!haveHttpMessageHandler) {
+ if (!environmentParser.HaveHttpMessageHandler) {
if (HttpClientHandlerType == null)
AddEnvironmentVariable (defaultHttpMessageHandler[0], defaultHttpMessageHandler[1]);
else
AddEnvironmentVariable ("XA_HTTP_CLIENT_HANDLER_TYPE", HttpClientHandlerType.Trim ());
}
- if (!UsingAndroidNETSdk && !haveTlsProvider) {
+ if (!UsingAndroidNETSdk && !environmentParser.HaveTlsProvider) {
if (TlsProvider == null)
AddEnvironmentVariable (defaultTlsProvider[0], defaultTlsProvider[1]);
else
AddEnvironmentVariable ("XA_TLS_PROVIDER", TlsProvider.Trim ());
}
- if (!haveMonoGCParams) {
+ if (!environmentParser.HaveMonoGCParams) {
if (EnableSGenConcurrent)
AddEnvironmentVariable ("MONO_GC_PARAMS", "major=marksweep-conc");
else
@@ -298,17 +261,15 @@ void AddEnvironment ()
};
int assemblyCount = 0;
+ bool enableMarshalMethods = EnableMarshalMethods;
HashSet archAssemblyNames = null;
-#if ENABLE_MARSHAL_METHODS
- var uniqueAssemblyNames = new HashSet (StringComparer.OrdinalIgnoreCase);
-#endif
+ HashSet uniqueAssemblyNames = new HashSet (StringComparer.OrdinalIgnoreCase);
Action updateAssemblyCount = (ITaskItem assembly) => {
string assemblyName = Path.GetFileName (assembly.ItemSpec);
-#if ENABLE_MARSHAL_METHODS
if (!uniqueAssemblyNames.Contains (assemblyName)) {
uniqueAssemblyNames.Add (assemblyName);
}
-#endif
+
if (!UseAssemblyStore) {
assemblyCount++;
return;
@@ -415,11 +376,11 @@ void AddEnvironment ()
IsBundledApp = IsBundledApplication,
UsesMonoAOT = usesMonoAOT,
UsesMonoLLVM = EnableLLVM,
- UsesAssemblyPreload = usesAssemblyPreload,
+ UsesAssemblyPreload = environmentParser.UsesAssemblyPreload,
MonoAOTMode = aotMode.ToString ().ToLowerInvariant (),
AotEnableLazyLoad = AndroidAotEnableLazyLoad,
AndroidPackageName = AndroidPackageName,
- BrokenExceptionTransitions = brokenExceptionTransitions,
+ BrokenExceptionTransitions = environmentParser.BrokenExceptionTransitions,
PackageNamingPolicy = pnp,
BoundExceptionType = boundExceptionType,
InstantRunEnabled = InstantRunEnabled,
@@ -439,19 +400,25 @@ void AddEnvironment ()
JNIEnvRegisterJniNativesToken = jnienv_registerjninatives_method_token,
JniRemappingReplacementTypeCount = jniRemappingNativeCodeInfo == null ? 0 : jniRemappingNativeCodeInfo.ReplacementTypeCount,
JniRemappingReplacementMethodIndexEntryCount = jniRemappingNativeCodeInfo == null ? 0 : jniRemappingNativeCodeInfo.ReplacementMethodIndexEntryCount,
+ MarshalMethodsEnabled = EnableMarshalMethods,
};
appConfigAsmGen.Init ();
-#if ENABLE_MARSHAL_METHODS
- var marshalMethodsState = BuildEngine4.GetRegisteredTaskObjectAssemblyLocal (GenerateJavaStubs.MarshalMethodsRegisterTaskKey, RegisteredTaskObjectLifetime.Build);
- var marshalMethodsAsmGen = new MarshalMethodsNativeAssemblyGenerator (
- assemblyCount,
- uniqueAssemblyNames,
- marshalMethodsState?.MarshalMethods,
- Log
- );
+ var marshalMethodsState = BuildEngine4.GetRegisteredTaskObjectAssemblyLocal (GenerateJavaStubs.MarshalMethodsRegisterTaskKey, RegisteredTaskObjectLifetime.Build);
+ MarshalMethodsNativeAssemblyGenerator marshalMethodsAsmGen;
+
+ if (enableMarshalMethods) {
+ marshalMethodsAsmGen = new MarshalMethodsNativeAssemblyGenerator (
+ assemblyCount,
+ uniqueAssemblyNames,
+ marshalMethodsState?.MarshalMethods,
+ Log
+ );
+ } else {
+ marshalMethodsAsmGen = new MarshalMethodsNativeAssemblyGenerator (uniqueAssemblyNames);
+ }
marshalMethodsAsmGen.Init ();
-#endif
+
foreach (string abi in SupportedAbis) {
string targetAbi = abi.ToLowerInvariant ();
string environmentBaseAsmFilePath = Path.Combine (EnvironmentOutputDirectory, $"environment.{targetAbi}");
@@ -465,13 +432,12 @@ void AddEnvironment ()
sw.Flush ();
Files.CopyIfStreamChanged (sw.BaseStream, environmentLlFilePath);
}
-#if ENABLE_MARSHAL_METHODS
+
using (var sw = MemoryStreamPool.Shared.CreateStreamWriter ()) {
marshalMethodsAsmGen.Write (targetArch, sw, marshalMethodsLlFilePath);
sw.Flush ();
Files.CopyIfStreamChanged (sw.BaseStream, marshalMethodsLlFilePath);
}
-#endif
}
void AddEnvironmentVariable (string name, string value)
@@ -505,7 +471,7 @@ void GetRequiredTokens (string assemblyFilePath, out int android_runtime_jnienv_
}
if (android_runtime_jnienv_class_token == -1 || jnienv_initialize_method_token == -1 || jnienv_registerjninatives_method_token == -1) {
- throw new InvalidOperationException ($"Unable to find the required Android.Runtime.JNIEnv method tokens");
+ throw new InvalidOperationException ($"Unable to find the required Android.Runtime.JNIEnvInit method tokens");
}
}
@@ -556,7 +522,7 @@ bool TypeMatches (TypeDefinition td)
}
string name = reader.GetString (td.Name);
- if (String.Compare (name, "JNIEnv", StringComparison.Ordinal) != 0) {
+ if (String.Compare (name, "JNIEnvInit", StringComparison.Ordinal) != 0) {
return false;
}
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/PrepareAbiItems.cs b/src/Xamarin.Android.Build.Tasks/Tasks/PrepareAbiItems.cs
index 62ccbf3bfbc..d826e94110c 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/PrepareAbiItems.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/PrepareAbiItems.cs
@@ -15,10 +15,7 @@ public class PrepareAbiItems : AndroidTask
const string EnvBase = "environment";
const string CompressedAssembliesBase = "compressed_assemblies";
const string JniRemappingBase = "jni_remap";
-
-#if ENABLE_MARSHAL_METHODS
const string MarshalMethodsBase = "marshal_methods";
-#endif
public override string TaskPrefix => "PAI";
@@ -57,10 +54,8 @@ public override bool RunTask ()
baseName = CompressedAssembliesBase;
} else if (String.Compare ("jniremap", Mode, StringComparison.OrdinalIgnoreCase) == 0) {
baseName = JniRemappingBase;
-#if ENABLE_MARSHAL_METHODS
} else if (String.Compare ("marshal_methods", Mode, StringComparison.OrdinalIgnoreCase) == 0) {
baseName = MarshalMethodsBase;
-#endif
} else {
Log.LogError ($"Unknown mode: {Mode}");
return false;
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 0f19a5a6926..f561c36c590 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
@@ -24,6 +24,66 @@ namespace Xamarin.Android.Build.Tests
[Parallelizable (ParallelScope.Children)]
public partial class BuildTest2 : BaseTest
{
+ static object [] MarshalMethodsDefaultStatusSource = new object [] {
+ new object[] {
+ /* isClassic */ false,
+ /* isRelease */ true,
+ /* marshalMethodsEnabled */ true,
+ },
+ new object[] {
+ /* isClassic */ false,
+ /* isRelease */ false,
+ /* marshalMethodsEnabled */ false,
+ },
+ new object[] {
+ /* isClassic */ true,
+ /* isRelease */ false,
+ /* marshalMethodsEnabled */ false,
+ },
+ new object[] {
+ /* isClassic */ true,
+ /* isRelease */ true,
+ /* marshalMethodsEnabled */ false,
+ },
+ };
+
+ [Test]
+ [TestCaseSource (nameof (MarshalMethodsDefaultStatusSource))]
+ public void MarshalMethodsDefaultEnabledStatus (bool isClassic, bool isRelease, bool marshalMethodsEnabled)
+ {
+ if (isClassic) {
+ if (Builder.UseDotNet) {
+ Assert.Ignore ("Ignored in .NET6+");
+ return;
+ }
+ } else if (!Builder.UseDotNet) {
+ Assert.Ignore ("Ignored in classic");
+ return;
+ }
+
+ var abis = new [] { "armeabi-v7a", "x86" };
+ var proj = new XamarinAndroidApplicationProject {
+ IsRelease = isRelease
+ };
+
+ proj.SetAndroidSupportedAbis (abis);
+
+ using (var b = CreateApkBuilder ()) {
+ Assert.IsTrue (b.Build (proj), "Build should have succeeded.");
+ Assert.IsTrue (
+ StringAssertEx.ContainsText (b.LastBuildOutput, $"_AndroidUseMarshalMethods = {marshalMethodsEnabled}"),
+ $"The '_AndroidUseMarshalMethods' MSBuild property should have had the value of '{marshalMethodsEnabled}'"
+ );
+
+ string objPath = Path.Combine (Root, b.ProjectDirectory, proj.IntermediateOutputPath);
+ List envFiles = EnvironmentHelper.GatherEnvironmentFiles (objPath, String.Join (";", abis), true);
+ EnvironmentHelper.ApplicationConfig app_config = EnvironmentHelper.ReadApplicationConfig (envFiles);
+
+ Assert.That (app_config, Is.Not.Null, "application_config must be present in the environment files");
+ Assert.AreEqual (app_config.marshal_methods_enabled, marshalMethodsEnabled, $"Marshal methods enabled status should be '{marshalMethodsEnabled}', but it was '{app_config.marshal_methods_enabled}'");
+ }
+ }
+
[Test]
public void CompressedWithoutLinker ()
{
diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs
index e78f6267066..d7d53020f59 100644
--- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.Android.Build.Tests/PackagingTest.cs
@@ -87,6 +87,7 @@ public void CheckIncludedAssemblies ([Values (false, true)] bool usesAssemblySto
new [] {
"Java.Interop.dll",
"Mono.Android.dll",
+ "Mono.Android.Runtime.dll",
"rc.bin",
"System.Console.dll",
"System.Private.CoreLib.dll",
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 0f6e7fd910d..c3c30ebae39 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
@@ -49,6 +49,7 @@ public sealed class ApplicationConfig
public bool jni_add_native_method_registration_attribute_present;
public bool have_runtime_config_blob;
public bool have_assemblies_blob;
+ public bool marshal_methods_enabled;
public byte bound_stream_io_exception_type;
public uint package_naming_policy;
public uint environment_variable_count;
@@ -66,7 +67,7 @@ public sealed class ApplicationConfig
public string android_package_name = String.Empty;
}
- const uint ApplicationConfigFieldCount = 25;
+ const uint ApplicationConfigFieldCount = 26;
const string ApplicationConfigSymbolName = "application_config";
const string AppEnvironmentVariablesSymbolName = "app_environment_variables";
@@ -255,77 +256,82 @@ static ApplicationConfig ReadApplicationConfig (EnvironmentFile envFile)
ret.have_assemblies_blob = ConvertFieldToBool ("have_assemblies_blob", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
- case 10: // bound_stream_io_exception_type: byte / .byte
+ case 10: // marshal_methods_enabled: bool / .byte
+ AssertFieldType (envFile.Path, parser.SourceFilePath, ".byte", field [0], item.LineNumber);
+ ret.marshal_methods_enabled = ConvertFieldToBool ("marshal_methods_enabled", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
+ break;
+
+ case 11: // bound_stream_io_exception_type: byte / .byte
AssertFieldType (envFile.Path, parser.SourceFilePath, ".byte", field [0], item.LineNumber);
ret.bound_stream_io_exception_type = ConvertFieldToByte ("bound_stream_io_exception_type", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
- case 11: // package_naming_policy: uint32_t / .word | .long
+ case 12: // package_naming_policy: uint32_t / .word | .long
Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}");
ret.package_naming_policy = ConvertFieldToUInt32 ("package_naming_policy", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
- case 12: // environment_variable_count: uint32_t / .word | .long
+ case 13: // environment_variable_count: uint32_t / .word | .long
Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}");
ret.environment_variable_count = ConvertFieldToUInt32 ("environment_variable_count", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
- case 13: // system_property_count: uint32_t / .word | .long
+ case 14: // system_property_count: uint32_t / .word | .long
Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}");
ret.system_property_count = ConvertFieldToUInt32 ("system_property_count", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
- case 14: // number_of_assemblies_in_apk: uint32_t / .word | .long
+ case 15: // number_of_assemblies_in_apk: uint32_t / .word | .long
Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}");
ret.number_of_assemblies_in_apk = ConvertFieldToUInt32 ("number_of_assemblies_in_apk", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
- case 15: // bundled_assembly_name_width: uint32_t / .word | .long
+ case 16: // bundled_assembly_name_width: uint32_t / .word | .long
Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}");
ret.bundled_assembly_name_width = ConvertFieldToUInt32 ("bundled_assembly_name_width", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
- case 16: // number_of_assembly_store_files: uint32_t / .word | .long
+ case 17: // number_of_assembly_store_files: uint32_t / .word | .long
Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}");
ret.number_of_assembly_store_files = ConvertFieldToUInt32 ("number_of_assembly_store_files", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
- case 17: // number_of_dso_cache_entries: uint32_t / .word | .long
+ case 18: // number_of_dso_cache_entries: uint32_t / .word | .long
Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}");
ret.number_of_dso_cache_entries = ConvertFieldToUInt32 ("number_of_dso_cache_entries", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
- case 18: // android_runtime_jnienv_class_token: uint32_t / .word | .long
+ case 19: // android_runtime_jnienv_class_token: uint32_t / .word | .long
Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}");
ret.number_of_dso_cache_entries = ConvertFieldToUInt32 ("android_runtime_jnienv_class_token", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
- case 19: // jnienv_initialize_method_token: uint32_t / .word | .long
+ case 20: // jnienv_initialize_method_token: uint32_t / .word | .long
Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}");
ret.number_of_dso_cache_entries = ConvertFieldToUInt32 ("jnienv_initialize_method_token", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
- case 20: // jnienv_registerjninatives_method_token: uint32_t / .word | .long
+ case 21: // jnienv_registerjninatives_method_token: uint32_t / .word | .long
Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}");
ret.number_of_dso_cache_entries = ConvertFieldToUInt32 ("jnienv_registerjninatives_method_token", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
- case 21: // jni_remapping_replacement_type_count: uint32_t / .word | .long
+ case 22: // jni_remapping_replacement_type_count: uint32_t / .word | .long
Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}");
ret.jni_remapping_replacement_type_count = ConvertFieldToUInt32 ("jni_remapping_replacement_type_count", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
- case 22: // jni_remapping_replacement_method_index_entry_count: uint32_t / .word | .long
+ case 23: // jni_remapping_replacement_method_index_entry_count: uint32_t / .word | .long
Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}");
ret.jni_remapping_replacement_method_index_entry_count = ConvertFieldToUInt32 ("jni_remapping_replacement_method_index_entry_count", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
- case 23: // mono_components_mask: uint32_t / .word | .long
+ case 24: // mono_components_mask: uint32_t / .word | .long
Assert.IsTrue (expectedUInt32Types.Contains (field [0]), $"Unexpected uint32_t field type in '{envFile.Path}:{item.LineNumber}': {field [0]}");
ret.mono_components_mask = ConvertFieldToUInt32 ("mono_components_mask", envFile.Path, parser.SourceFilePath, item.LineNumber, field [1]);
break;
- case 24: // android_package_name: string / [pointer type]
+ case 25: // android_package_name: string / [pointer type]
Assert.IsTrue (expectedPointerTypes.Contains (field [0]), $"Unexpected pointer field type in '{envFile.Path}:{item.LineNumber}': {field [0]}");
pointers.Add (field [1].Trim ());
break;
diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.apkdesc
index 880a58bb615..33cfb16c606 100644
--- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.apkdesc
+++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleDotNet.apkdesc
@@ -5,10 +5,13 @@
"Size": 3032
},
"assemblies/Java.Interop.dll": {
- "Size": 59188
+ "Size": 59187
},
"assemblies/Mono.Android.dll": {
- "Size": 88549
+ "Size": 88390
+ },
+ "assemblies/Mono.Android.Runtime.dll": {
+ "Size": 5776
},
"assemblies/rc.bin": {
"Size": 1182
@@ -26,10 +29,10 @@
"Size": 3388
},
"assemblies/System.Private.CoreLib.dll": {
- "Size": 533485
+ "Size": 533617
},
"assemblies/System.Runtime.dll": {
- "Size": 3498
+ "Size": 3655
},
"assemblies/System.Runtime.InteropServices.dll": {
"Size": 3770
@@ -47,13 +50,13 @@
"Size": 62998
},
"assemblies/UnnamedProject.dll": {
- "Size": 3560
+ "Size": 3559
},
"classes.dex": {
- "Size": 360720
+ "Size": 18612
},
"lib/arm64-v8a/libmonodroid.so": {
- "Size": 425416
+ "Size": 433600
},
"lib/arm64-v8a/libmonosgen-2.0.so": {
"Size": 3073392
@@ -68,16 +71,16 @@
"Size": 148696
},
"lib/arm64-v8a/libxamarin-app.so": {
- "Size": 10216
+ "Size": 16896
},
"META-INF/BNDLTOOL.RSA": {
"Size": 1213
},
"META-INF/BNDLTOOL.SF": {
- "Size": 3339
+ "Size": 3446
},
"META-INF/MANIFEST.MF": {
- "Size": 3212
+ "Size": 3319
},
"res/drawable-hdpi-v4/icon.png": {
"Size": 4762
@@ -104,5 +107,5 @@
"Size": 1904
}
},
- "PackageSize": 2791978
+ "PackageSize": 2730619
}
\ No newline at end of file
diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleLegacy.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleLegacy.apkdesc
index ac323f1cebb..e814dd13ded 100644
--- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleLegacy.apkdesc
+++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64SimpleLegacy.apkdesc
@@ -5,10 +5,10 @@
"Size": 2604
},
"assemblies/Java.Interop.dll": {
- "Size": 68921
+ "Size": 69028
},
"assemblies/Mono.Android.dll": {
- "Size": 259883
+ "Size": 264788
},
"assemblies/mscorlib.dll": {
"Size": 769017
@@ -17,13 +17,13 @@
"Size": 28199
},
"assemblies/System.dll": {
- "Size": 9179
+ "Size": 9180
},
"assemblies/UnnamedProject.dll": {
- "Size": 2877
+ "Size": 2881
},
"classes.dex": {
- "Size": 362940
+ "Size": 370480
},
"lib/arm64-v8a/libmono-btls-shared.so": {
"Size": 1613872
@@ -32,7 +32,7 @@
"Size": 750976
},
"lib/arm64-v8a/libmonodroid.so": {
- "Size": 332128
+ "Size": 332880
},
"lib/arm64-v8a/libmonosgen-2.0.so": {
"Size": 4051864
@@ -41,7 +41,7 @@
"Size": 66184
},
"lib/arm64-v8a/libxamarin-app.so": {
- "Size": 20240
+ "Size": 21112
},
"META-INF/ANDROIDD.RSA": {
"Size": 1213
@@ -74,5 +74,5 @@
"Size": 1724
}
},
- "PackageSize": 4028116
+ "PackageSize": 4036308
}
\ No newline at end of file
diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.apkdesc
index 692414a6c1e..db99dbbb8a5 100644
--- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.apkdesc
+++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsDotNet.apkdesc
@@ -5,13 +5,16 @@
"Size": 3568
},
"assemblies/FormsViewGroup.dll": {
- "Size": 7114
+ "Size": 7314
},
"assemblies/Java.Interop.dll": {
- "Size": 66767
+ "Size": 66760
},
"assemblies/Mono.Android.dll": {
- "Size": 442556
+ "Size": 444587
+ },
+ "assemblies/Mono.Android.Runtime.dll": {
+ "Size": 5776
},
"assemblies/mscorlib.dll": {
"Size": 3859
@@ -104,7 +107,7 @@
"Size": 16635
},
"assemblies/System.Runtime.dll": {
- "Size": 3647
+ "Size": 3808
},
"assemblies/System.Runtime.InteropServices.dll": {
"Size": 3770
@@ -143,16 +146,16 @@
"Size": 1860
},
"assemblies/UnnamedProject.dll": {
- "Size": 117252
+ "Size": 117251
},
"assemblies/Xamarin.AndroidX.Activity.dll": {
"Size": 5872
},
"assemblies/Xamarin.AndroidX.AppCompat.AppCompatResources.dll": {
- "Size": 5889
+ "Size": 6118
},
"assemblies/Xamarin.AndroidX.AppCompat.dll": {
- "Size": 112363
+ "Size": 112654
},
"assemblies/Xamarin.AndroidX.CardView.dll": {
"Size": 6596
@@ -161,13 +164,13 @@
"Size": 16407
},
"assemblies/Xamarin.AndroidX.Core.dll": {
- "Size": 96515
+ "Size": 96736
},
"assemblies/Xamarin.AndroidX.DrawerLayout.dll": {
- "Size": 14065
+ "Size": 14237
},
"assemblies/Xamarin.AndroidX.Fragment.dll": {
- "Size": 39713
+ "Size": 39952
},
"assemblies/Xamarin.AndroidX.Legacy.Support.Core.UI.dll": {
"Size": 5924
@@ -185,16 +188,16 @@
"Size": 12476
},
"assemblies/Xamarin.AndroidX.RecyclerView.dll": {
- "Size": 84474
+ "Size": 84803
},
"assemblies/Xamarin.AndroidX.SavedState.dll": {
"Size": 4869
},
"assemblies/Xamarin.AndroidX.SwipeRefreshLayout.dll": {
- "Size": 10180
+ "Size": 10391
},
"assemblies/Xamarin.AndroidX.ViewPager.dll": {
- "Size": 17786
+ "Size": 18044
},
"assemblies/Xamarin.Forms.Core.dll": {
"Size": 528450
@@ -209,13 +212,13 @@
"Size": 60774
},
"assemblies/Xamarin.Google.Android.Material.dll": {
- "Size": 39911
+ "Size": 40159
},
"classes.dex": {
- "Size": 3473196
+ "Size": 3090268
},
"lib/arm64-v8a/libmonodroid.so": {
- "Size": 425416
+ "Size": 433600
},
"lib/arm64-v8a/libmonosgen-2.0.so": {
"Size": 3073392
@@ -230,7 +233,7 @@
"Size": 148696
},
"lib/arm64-v8a/libxamarin-app.so": {
- "Size": 99640
+ "Size": 324464
},
"META-INF/android.support.design_material.version": {
"Size": 12
@@ -344,13 +347,13 @@
"Size": 1213
},
"META-INF/BNDLTOOL.SF": {
- "Size": 79628
+ "Size": 79735
},
"META-INF/com.google.android.material_material.version": {
"Size": 10
},
"META-INF/MANIFEST.MF": {
- "Size": 79501
+ "Size": 79608
},
"META-INF/proguard/androidx-annotations.pro": {
"Size": 339
@@ -1985,5 +1988,5 @@
"Size": 341228
}
},
- "PackageSize": 8078308
+ "PackageSize": 8057909
}
\ No newline at end of file
diff --git a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsLegacy.apkdesc b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsLegacy.apkdesc
index 4498b397cca..aea0929a8d2 100644
--- a/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsLegacy.apkdesc
+++ b/src/Xamarin.Android.Build.Tasks/Tests/Xamarin.ProjectTools/Resources/Base/BuildReleaseArm64XFormsLegacy.apkdesc
@@ -8,10 +8,10 @@
"Size": 7215
},
"assemblies/Java.Interop.dll": {
- "Size": 69945
+ "Size": 70056
},
"assemblies/Mono.Android.dll": {
- "Size": 570605
+ "Size": 571897
},
"assemblies/Mono.Security.dll": {
"Size": 68433
@@ -32,7 +32,7 @@
"Size": 110693
},
"assemblies/System.Numerics.dll": {
- "Size": 15682
+ "Size": 15683
},
"assemblies/System.Runtime.Serialization.dll": {
"Size": 186660
@@ -44,7 +44,7 @@
"Size": 395656
},
"assemblies/UnnamedProject.dll": {
- "Size": 116896
+ "Size": 116897
},
"assemblies/Xamarin.AndroidX.Activity.dll": {
"Size": 7697
@@ -65,7 +65,7 @@
"Size": 131930
},
"assemblies/Xamarin.AndroidX.DrawerLayout.dll": {
- "Size": 15426
+ "Size": 15425
},
"assemblies/Xamarin.AndroidX.Fragment.dll": {
"Size": 43135
@@ -95,7 +95,7 @@
"Size": 11272
},
"assemblies/Xamarin.AndroidX.ViewPager.dll": {
- "Size": 19423
+ "Size": 19424
},
"assemblies/Xamarin.Forms.Core.dll": {
"Size": 524736
@@ -113,7 +113,7 @@
"Size": 43497
},
"classes.dex": {
- "Size": 3475144
+ "Size": 3482568
},
"lib/arm64-v8a/libmono-btls-shared.so": {
"Size": 1613872
@@ -122,7 +122,7 @@
"Size": 750976
},
"lib/arm64-v8a/libmonodroid.so": {
- "Size": 332128
+ "Size": 332880
},
"lib/arm64-v8a/libmonosgen-2.0.so": {
"Size": 4051864
@@ -131,7 +131,7 @@
"Size": 66184
},
"lib/arm64-v8a/libxamarin-app.so": {
- "Size": 105288
+ "Size": 106160
},
"META-INF/android.support.design_material.version": {
"Size": 12
@@ -1883,5 +1883,5 @@
"Size": 341040
}
},
- "PackageSize": 9545886
+ "PackageSize": 9549982
}
\ No newline at end of file
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfig.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfig.cs
index 369bc794b50..e5a1405b127 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfig.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfig.cs
@@ -34,6 +34,7 @@ sealed class ApplicationConfig
public bool jni_add_native_method_registration_attribute_present;
public bool have_runtime_config_blob;
public bool have_assemblies_blob;
+ public bool marshal_methods_enabled;
public byte bound_stream_io_exception_type;
public uint package_naming_policy;
public uint environment_variable_count;
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs
index 979d705c162..16bc2daa34a 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs
@@ -173,6 +173,7 @@ sealed class XamarinAndroidBundledAssembly
public MonoComponent MonoComponents { get; set; }
public PackageNamingPolicy PackageNamingPolicy { get; set; }
public List NativeLibraries { get; set; }
+ public bool MarshalMethodsEnabled { get; set; }
public ApplicationConfigNativeAssemblyGenerator (IDictionary environmentVariables, IDictionary systemProperties, TaskLoggingHelper log)
{
@@ -201,6 +202,7 @@ public override void Init ()
jni_add_native_method_registration_attribute_present = JniAddNativeMethodRegistrationAttributePresent,
have_runtime_config_blob = HaveRuntimeConfigBlob,
have_assemblies_blob = HaveAssemblyStore,
+ marshal_methods_enabled = MarshalMethodsEnabled,
bound_stream_io_exception_type = (byte)BoundExceptionType,
package_naming_policy = (uint)PackageNamingPolicy,
environment_variable_count = (uint)(environmentVariables == null ? 0 : environmentVariables.Count * 2),
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/EnvironmentFilesParser.cs b/src/Xamarin.Android.Build.Tasks/Utilities/EnvironmentFilesParser.cs
new file mode 100644
index 00000000000..c663d3eaa8d
--- /dev/null
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/EnvironmentFilesParser.cs
@@ -0,0 +1,82 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+
+using Microsoft.Android.Build.Tasks;
+using Microsoft.Build.Framework;
+using Microsoft.Build.Utilities;
+
+namespace Xamarin.Android.Tasks
+{
+ class EnvironmentFilesParser
+ {
+ public bool BrokenExceptionTransitions { get; set; }
+ public bool HavebuildId { get; private set; }
+ public bool HaveHttpMessageHandler { get; private set; }
+ public bool HaveLogLevel { get; private set; }
+ public bool HaveMonoDebug { get; private set; }
+ public bool HaveMonoGCParams { get; private set; }
+ public bool HaveTlsProvider { get; private set; }
+ public bool UsesAssemblyPreload { get; set; }
+ public List EnvironmentVariableLines { get; } = new List ();
+
+ public bool AreBrokenExceptionTransitionsEnabled (ITaskItem[] environments)
+ {
+ foreach (ITaskItem env in environments ?? Array.Empty ()) {
+ foreach (string line in File.ReadLines (env.ItemSpec)) {
+ if (IsBrokenExceptionTransitionsLine (line.Trim ())) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+ public void Parse (ITaskItem[] environments, SequencePointsMode sequencePointsMode, bool usingAndroidNETSdk, TaskLoggingHelper log)
+ {
+ foreach (ITaskItem env in environments ?? Array.Empty ()) {
+ foreach (string line in File.ReadLines (env.ItemSpec)) {
+ var lineToWrite = line.Trim ();
+ if (lineToWrite.StartsWith ("MONO_LOG_LEVEL=", StringComparison.Ordinal))
+ HaveLogLevel = true;
+ if (lineToWrite.StartsWith ("MONO_GC_PARAMS=", StringComparison.Ordinal)) {
+ HaveMonoGCParams = true;
+ if (lineToWrite.IndexOf ("bridge-implementation=old", StringComparison.Ordinal) >= 0) {
+ log.LogCodedWarning ("XA2000", Properties.Resources.XA2000_gcParams_bridgeImpl);
+ }
+ }
+ if (lineToWrite.StartsWith ("XAMARIN_BUILD_ID=", StringComparison.Ordinal))
+ HavebuildId = true;
+ if (lineToWrite.StartsWith ("MONO_DEBUG=", StringComparison.Ordinal)) {
+ HaveMonoDebug = true;
+ if (sequencePointsMode != SequencePointsMode.None && !lineToWrite.Contains ("gen-compact-seq-points"))
+ lineToWrite = line + ",gen-compact-seq-points";
+ }
+ if (lineToWrite.StartsWith ("XA_HTTP_CLIENT_HANDLER_TYPE=", StringComparison.Ordinal))
+ HaveHttpMessageHandler = true;
+
+ if (!usingAndroidNETSdk && lineToWrite.StartsWith ("XA_TLS_PROVIDER=", StringComparison.Ordinal))
+ HaveTlsProvider = true;
+
+ if (lineToWrite.StartsWith ("mono.enable_assembly_preload=", StringComparison.Ordinal)) {
+ int idx = lineToWrite.IndexOf ('=');
+ uint val;
+ if (idx < lineToWrite.Length - 1 && UInt32.TryParse (lineToWrite.Substring (idx + 1), out val)) {
+ UsesAssemblyPreload = idx == 1;
+ }
+ continue;
+ }
+ if (IsBrokenExceptionTransitionsLine (lineToWrite)) {
+ BrokenExceptionTransitions = true;
+ continue;
+ }
+
+ EnvironmentVariableLines.Add (lineToWrite);
+ }
+ }
+ }
+
+ bool IsBrokenExceptionTransitionsLine (string lineToWrite) => lineToWrite.StartsWith ("XA_BROKEN_EXCEPTION_TRANSITIONS=", StringComparison.Ordinal);
+ }
+}
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.Code.cs b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.Code.cs
index d5537296a7e..85b9662c524 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.Code.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrGenerator.Code.cs
@@ -26,7 +26,7 @@ abstract partial class LlvmIrGenerator
///
/// Writes the function definition up to the opening curly brace
///
- public void WriteFunctionStart (LlvmIrFunction function)
+ public void WriteFunctionStart (LlvmIrFunction function, string? comment = null)
{
if (function == null) {
throw new ArgumentNullException (nameof (function));
@@ -38,6 +38,12 @@ public void WriteFunctionStart (LlvmIrFunction function)
}
Output.WriteLine ();
+ if (!String.IsNullOrEmpty (comment)) {
+ foreach (string line in comment.Split ('\n')) {
+ WriteCommentLine (line);
+ }
+ }
+
if (attributes != null) {
WriteCommentLine ($"Function attributes: {attributes.Render ()}");
}
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsAssemblyRewriter.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsAssemblyRewriter.cs
index 8af1d3878ea..acd69f2d7ea 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsAssemblyRewriter.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsAssemblyRewriter.cs
@@ -1,4 +1,3 @@
-#if ENABLE_MARSHAL_METHODS
using System;
using System.Collections.Generic;
using System.IO;
@@ -8,12 +7,20 @@
using Microsoft.Build.Utilities;
using Mono.Cecil;
using Mono.Cecil.Cil;
-using Xamarin.Android.Tools;
namespace Xamarin.Android.Tasks
{
class MarshalMethodsAssemblyRewriter
{
+ sealed class AssemblyImports
+ {
+ public MethodReference MonoUnhandledExceptionMethod;
+ public TypeReference SystemException;
+ public MethodReference UnhandledExceptionMethod;
+ public CustomAttribute UnmanagedCallersOnlyAttribute;
+ public MethodReference WaitForBridgeProcessingMethod;
+ }
+
IDictionary> methods;
ICollection uniqueAssemblies;
IDictionary > assemblyPaths;
@@ -27,19 +34,55 @@ public MarshalMethodsAssemblyRewriter (IDictionary targetAssemblyPaths, bool brokenExceptionTransitions)
{
+ if (resolver == null) {
+ throw new ArgumentNullException (nameof (resolver));
+ }
+
+ if (targetAssemblyPaths == null) {
+ throw new ArgumentNullException (nameof (targetAssemblyPaths));
+ }
+
+ if (targetAssemblyPaths.Count == 0) {
+ throw new ArgumentException ("must contain at least one target path", nameof (targetAssemblyPaths));
+ }
+
+ AssemblyDefinition? monoAndroidRuntime = resolver.Resolve ("Mono.Android.Runtime");
+ if (monoAndroidRuntime == null) {
+ throw new InvalidOperationException ($"Internal error: unable to load the Mono.Android.Runtime assembly");
+ }
+
+ TypeDefinition runtime = FindType (monoAndroidRuntime, "Android.Runtime.AndroidRuntimeInternal", required: true)!;
+ MethodDefinition waitForBridgeProcessingMethod = FindMethod (runtime, "WaitForBridgeProcessing", required: true)!;
+
+ TypeDefinition androidEnvironment = FindType (monoAndroidRuntime, "Android.Runtime.AndroidEnvironmentInternal", required: true)!;
+ MethodDefinition unhandledExceptionMethod = FindMethod (androidEnvironment, "UnhandledException", required: true)!;
+
+ TypeDefinition runtimeNativeMethods = FindType (monoAndroidRuntime, "Android.Runtime.RuntimeNativeMethods", required: true);
+ MethodDefinition monoUnhandledExceptionMethod = FindMethod (runtimeNativeMethods, "monodroid_debugger_unhandled_exception", required: true);
+
+ AssemblyDefinition corlib = resolver.Resolve ("System.Private.CoreLib");
+ TypeDefinition systemException = FindType (corlib, "System.Exception", required: true);
+
MethodDefinition unmanagedCallersOnlyAttributeCtor = GetUnmanagedCallersOnlyAttributeConstructor (resolver);
- var unmanagedCallersOnlyAttributes = new Dictionary ();
+ var assemblyImports = new Dictionary ();
foreach (AssemblyDefinition asm in uniqueAssemblies) {
- unmanagedCallersOnlyAttributes.Add (asm, CreateImportedUnmanagedCallersOnlyAttribute (asm, unmanagedCallersOnlyAttributeCtor));
+ var imports = new AssemblyImports {
+ MonoUnhandledExceptionMethod = asm.MainModule.ImportReference (monoUnhandledExceptionMethod),
+ SystemException = asm.MainModule.ImportReference (systemException),
+ UnhandledExceptionMethod = asm.MainModule.ImportReference (unhandledExceptionMethod),
+ UnmanagedCallersOnlyAttribute = CreateImportedUnmanagedCallersOnlyAttribute (asm, unmanagedCallersOnlyAttributeCtor),
+ WaitForBridgeProcessingMethod = asm.MainModule.ImportReference (waitForBridgeProcessingMethod),
+ };
+
+ assemblyImports.Add (asm, imports);
}
- Console.WriteLine ();
- Console.WriteLine ("Modifying assemblies");
+ log.LogDebugMessage ("Rewriting assemblies for marshal methods support");
var processedMethods = new Dictionary (StringComparer.Ordinal);
- Console.WriteLine ("Adding the [UnmanagedCallersOnly] attribute to native callback methods and removing unneeded fields+methods");
foreach (IList methodList in methods.Values) {
foreach (MarshalMethodEntry method in methodList) {
string fullNativeCallbackName = method.NativeCallback.FullName;
@@ -48,33 +91,29 @@ public void Rewrite (DirectoryAssemblyResolver resolver)
continue;
}
- Console.WriteLine ($"\t{fullNativeCallbackName} (token: 0x{method.NativeCallback.MetadataToken.RID:x})");
- Console.WriteLine ($"\t Top type == '{method.DeclaringType}'");
- Console.WriteLine ($"\t NativeCallback == '{method.NativeCallback}'");
- Console.WriteLine ($"\t Connector == '{method.Connector}'");
- Console.WriteLine ($"\t method.NativeCallback.CustomAttributes == {ToStringOrNull (method.NativeCallback.CustomAttributes)}");
- Console.WriteLine ($"\t method.Connector.DeclaringType == {ToStringOrNull (method.Connector?.DeclaringType)}");
- Console.WriteLine ($"\t method.Connector.DeclaringType.Methods == {ToStringOrNull (method.Connector.DeclaringType?.Methods)}");
- Console.WriteLine ($"\t method.CallbackField == {ToStringOrNull (method.CallbackField)}");
- Console.WriteLine ($"\t method.CallbackField?.DeclaringType == {ToStringOrNull (method.CallbackField?.DeclaringType)}");
- Console.WriteLine ($"\t method.CallbackField?.DeclaringType.Fields == {ToStringOrNull (method.CallbackField?.DeclaringType?.Fields)}");
-
- if (method.NeedsBlittableWorkaround) {
- method.NativeCallbackWrapper = GenerateBlittableWrapper (method, unmanagedCallersOnlyAttributes);
- } else {
- method.NativeCallback.CustomAttributes.Add (unmanagedCallersOnlyAttributes [method.NativeCallback.Module.Assembly]);
+ method.NativeCallbackWrapper = GenerateWrapper (method, assemblyImports, brokenExceptionTransitions);
+ if (method.Connector != null) {
+ if (method.Connector.IsStatic && method.Connector.IsPrivate) {
+ log.LogDebugMessage ($"Removing connector method {method.Connector.FullName}");
+ method.Connector.DeclaringType?.Methods?.Remove (method.Connector);
+ } else {
+ log.LogWarning ($"NOT removing connector method {method.Connector.FullName} because it's either not static or not private");
+ }
}
- method.Connector?.DeclaringType?.Methods?.Remove (method.Connector);
- method.CallbackField?.DeclaringType?.Fields?.Remove (method.CallbackField);
+ if (method.CallbackField != null) {
+ if (method.CallbackField.IsStatic && method.CallbackField.IsPrivate) {
+ log.LogDebugMessage ($"Removing callback delegate backing field {method.CallbackField.FullName}");
+ method.CallbackField.DeclaringType?.Fields?.Remove (method.CallbackField);
+ } else {
+ log.LogWarning ($"NOT removing callback field {method.CallbackField.FullName} because it's either not static or not private");
+ }
+ }
processedMethods.Add (fullNativeCallbackName, method.NativeCallback);
}
}
- Console.WriteLine ();
- Console.WriteLine ("Rewriting assemblies");
-
var newAssemblyPaths = new List ();
foreach (AssemblyDefinition asm in uniqueAssemblies) {
foreach (string path in GetAssemblyPaths (asm)) {
@@ -82,8 +121,13 @@ public void Rewrite (DirectoryAssemblyResolver resolver)
WriteSymbols = (File.Exists (path + ".mdb") || File.Exists (Path.ChangeExtension (path, ".pdb"))),
};
+
string output = $"{path}.new";
- Console.WriteLine ($"\t{asm.Name} => {output}");
+ log.LogDebugMessage ($"Writing new version of assembly: {output}");
+
+ // TODO: this should be used eventually, but it requires that all the types are reloaded from the assemblies before typemaps are generated
+ // since Cecil doesn't update the MVID in the already loaded types
+ //asm.MainModule.Mvid = Guid.NewGuid ();
asm.Write (output, writerParams);
newAssemblyPaths.Add (output);
}
@@ -92,119 +136,172 @@ public void Rewrite (DirectoryAssemblyResolver resolver)
// Replace old versions of the assemblies only after we've finished rewriting without issues, otherwise leave the new
// versions around.
foreach (string path in newAssemblyPaths) {
- string target = Path.Combine (Path.GetDirectoryName (path), Path.GetFileNameWithoutExtension (path));
- MoveFile (path, target);
+ string? pdb = null;
+ string? mdb = null;
- string source = Path.ChangeExtension (path, ".pdb");
+ string source = Path.ChangeExtension (Path.Combine (Path.GetDirectoryName (path), Path.GetFileNameWithoutExtension (path)), ".pdb");
if (File.Exists (source)) {
- target = Path.ChangeExtension (Path.Combine (Path.GetDirectoryName (source), Path.GetFileNameWithoutExtension (source)), ".pdb");
-
- MoveFile (source, target);
+ pdb = source;
}
source = $"{path}.mdb";
if (File.Exists (source)) {
- target = Path.ChangeExtension (path, ".mdb");
- MoveFile (source, target);
+ mdb = source;
}
- }
- Console.WriteLine ();
- Console.WriteLine ("Method tokens:");
- foreach (IList methodList in methods.Values) {
- foreach (MarshalMethodEntry method in methodList) {
- Console.WriteLine ($"\t{method.NativeCallback.FullName} (token: 0x{method.NativeCallback.MetadataToken.RID:x})");
+ foreach (string targetPath in targetAssemblyPaths) {
+ string target = Path.Combine (targetPath, Path.GetFileNameWithoutExtension (path));
+ CopyFile (path, target);
+
+ if (!String.IsNullOrEmpty (pdb)) {
+ target = Path.ChangeExtension (Path.Combine (targetPath, Path.GetFileNameWithoutExtension (pdb)), ".pdb");
+ CopyFile (pdb, target);
+ }
+
+ if (!String.IsNullOrEmpty (mdb)) {
+ target = Path.Combine (targetPath, Path.ChangeExtension (Path.GetFileName (path), ".mdb"));
+ CopyFile (mdb, target);
+ }
}
+
+ RemoveFile (path);
+ RemoveFile (pdb);
+ RemoveFile (mdb);
}
- void MoveFile (string source, string target)
+ void CopyFile (string source, string target)
{
- Console.WriteLine ($"Moving '{source}' => '{target}'");
- Files.CopyIfChanged (source, target);
- try {
- File.Delete (source);
- } catch (Exception ex) {
- log.LogWarning ($"Unable to delete source file '{source}' when moving it to '{target}'");
- log.LogDebugMessage (ex.ToString ());
- }
+ log.LogDebugMessage ($"Copying rewritten assembly: {source} -> {target}");
+ File.Copy (source, target, true);
}
- string ToStringOrNull (object? o)
+ void RemoveFile (string? path)
{
- if (o == null) {
- return "'null'";
+ if (String.IsNullOrEmpty (path) || !File.Exists (path)) {
+ return;
}
- return o.ToString ();
+ try {
+ File.Delete (path);
+ } catch (Exception ex) {
+ log.LogWarning ($"Unable to delete source file '{path}'");
+ log.LogDebugMessage (ex.ToString ());
+ }
}
}
- MethodDefinition GenerateBlittableWrapper (MarshalMethodEntry method, Dictionary unmanagedCallersOnlyAttributes)
+ MethodDefinition GenerateWrapper (MarshalMethodEntry method, Dictionary assemblyImports, bool brokenExceptionTransitions)
{
- Console.WriteLine ($"\t Generating blittable wrapper for: {method.NativeCallback.FullName}");
MethodDefinition callback = method.NativeCallback;
+ AssemblyImports imports = assemblyImports [callback.Module.Assembly];
string wrapperName = $"{callback.Name}_mm_wrapper";
TypeReference retType = MapToBlittableTypeIfNecessary (callback.ReturnType, out bool returnTypeMapped);
- bool hasReturnValue = String.Compare ("System.Void", retType.FullName, StringComparison.Ordinal) != 0;
+ bool hasReturnValue = String.Compare ("System.Void", callback.ReturnType.FullName, StringComparison.Ordinal) != 0;
var wrapperMethod = new MethodDefinition (wrapperName, callback.Attributes, retType);
+
callback.DeclaringType.Methods.Add (wrapperMethod);
- wrapperMethod.CustomAttributes.Add (unmanagedCallersOnlyAttributes [callback.Module.Assembly]);
+ wrapperMethod.CustomAttributes.Add (imports.UnmanagedCallersOnlyAttribute);
MethodBody body = wrapperMethod.Body;
- int nparam = 0;
+ body.InitLocals = true;
+
+ VariableDefinition? retval = null;
+ if (hasReturnValue) {
+ retval = new VariableDefinition (retType);
+ body.Variables.Add (retval);
+ }
+
+ body.Instructions.Add (Instruction.Create (OpCodes.Call, imports.WaitForBridgeProcessingMethod));
+ var exceptionHandler = new ExceptionHandler (ExceptionHandlerType.Catch) {
+ CatchType = imports.SystemException,
+ };
+ body.ExceptionHandlers.Add (exceptionHandler);
+
+ Instruction? firstTryInstruction = null;
+ Instruction? inst = null;
+ uint nparam = 0;
foreach (ParameterDefinition pdef in callback.Parameters) {
TypeReference newType = MapToBlittableTypeIfNecessary (pdef.ParameterType, out _);
wrapperMethod.Parameters.Add (new ParameterDefinition (pdef.Name, pdef.Attributes, newType));
- OpCode ldargOp;
- bool paramRef = false;
- switch (nparam++) {
- case 0:
- ldargOp = OpCodes.Ldarg_0;
- break;
-
- case 1:
- ldargOp = OpCodes.Ldarg_1;
- break;
-
- case 2:
- ldargOp = OpCodes.Ldarg_2;
- break;
-
- case 3:
- ldargOp = OpCodes.Ldarg_3;
- break;
-
- default:
- ldargOp = OpCodes.Ldarg_S;
- paramRef = true;
- break;
+ inst = GetLoadArgInstruction (nparam++, pdef);
+ if (firstTryInstruction == null) {
+ firstTryInstruction = inst;
}
- Instruction ldarg;
+ body.Instructions.Add (inst);
- if (!paramRef) {
- ldarg = Instruction.Create (ldargOp);
- } else {
- ldarg = Instruction.Create (ldargOp, pdef);
+ if (!pdef.ParameterType.IsBlittable ()) {
+ GenerateNonBlittableConversion (pdef.ParameterType, newType);
}
+ }
- body.Instructions.Add (ldarg);
+ inst = Instruction.Create (OpCodes.Call, callback);
+ if (firstTryInstruction == null) {
+ firstTryInstruction = inst;
+ }
+ body.Instructions.Add (inst);
- if (!pdef.ParameterType.IsBlittable ()) {
- GenerateNonBlittableConversion (pdef.ParameterType, newType);
+ exceptionHandler.TryStart = firstTryInstruction;
+
+ if (hasReturnValue && !returnTypeMapped) {
+ body.Instructions.Add (Instruction.Create (OpCodes.Stloc, retval));
+ }
+
+ Instruction ret = Instruction.Create (OpCodes.Ret);
+ Instruction? retValLoadInst = null;
+ Instruction leaveTarget;
+
+ if (hasReturnValue) {
+ if (returnTypeMapped) {
+ GenerateRetValCast (callback.ReturnType, retType);
+ body.Instructions.Add (Instruction.Create (OpCodes.Stloc, retval));
}
+
+ retValLoadInst = Instruction.Create (OpCodes.Ldloc, retval);
+ leaveTarget = retValLoadInst;
+ } else {
+ leaveTarget = ret;
}
- body.Instructions.Add (Instruction.Create (OpCodes.Call, callback));
+ body.Instructions.Add (Instruction.Create (OpCodes.Leave_S, leaveTarget));
+
+ var exceptionVar = new VariableDefinition (imports.SystemException);
+ body.Variables.Add (exceptionVar);
+
+ var catchStartInst = Instruction.Create (OpCodes.Stloc, exceptionVar);
+ exceptionHandler.HandlerStart = catchStartInst;
+
+ // TryEnd must point to the next instruction after the try block
+ exceptionHandler.TryEnd = catchStartInst;
+
+ body.Instructions.Add (catchStartInst);
+ body.Instructions.Add (Instruction.Create (OpCodes.Ldarg_0));
+ body.Instructions.Add (Instruction.Create (OpCodes.Ldloc, exceptionVar));
+
+ if (brokenExceptionTransitions) {
+ body.Instructions.Add (Instruction.Create (OpCodes.Call, imports.MonoUnhandledExceptionMethod));
+ body.Instructions.Add (Instruction.Create (OpCodes.Throw));
+ } else {
+ body.Instructions.Add (Instruction.Create (OpCodes.Call, imports.UnhandledExceptionMethod));
- if (hasReturnValue && returnTypeMapped) {
- GenerateRetValCast (callback.ReturnType, retType);
+ if (hasReturnValue) {
+ AddSetDefaultValueInstructions (body, retType, retval);
+ }
+ }
+
+ body.Instructions.Add (Instruction.Create (OpCodes.Leave_S, leaveTarget));
+
+ // HandlerEnd must point to the next instruction after the catch block
+ if (hasReturnValue) {
+ body.Instructions.Add (retValLoadInst);
+ exceptionHandler.HandlerEnd = retValLoadInst;
+ } else {
+ exceptionHandler.HandlerEnd = ret;
}
+ body.Instructions.Add (ret);
- body.Instructions.Add (Instruction.Create (OpCodes.Ret));
- Console.WriteLine ($"\t New method: {wrapperMethod.FullName}");
return wrapperMethod;
void GenerateNonBlittableConversion (TypeReference sourceType, TypeReference targetType)
@@ -255,6 +352,91 @@ void ThrowUnsupportedType (TypeReference type)
}
}
+ void AddSetDefaultValueInstructions (MethodBody body, TypeReference type, VariableDefinition retval)
+ {
+ bool supported = false;
+
+ switch (type.FullName) {
+ case "System.Boolean":
+ case "System.Byte":
+ case "System.Int16":
+ case "System.Int32":
+ case "System.SByte":
+ case "System.UInt16":
+ case "System.UInt32":
+ supported = true;
+ body.Instructions.Add (Instruction.Create (OpCodes.Ldc_I4_0));
+ break;
+
+ case "System.Int64":
+ case "System.UInt64":
+ supported = true;
+ body.Instructions.Add (Instruction.Create (OpCodes.Ldc_I4_0));
+ body.Instructions.Add (Instruction.Create (OpCodes.Conv_I8));
+ break;
+
+ case "System.IntPtr":
+ case "System.UIntPtr":
+ supported = true;
+ body.Instructions.Add (Instruction.Create (OpCodes.Ldc_I4_0));
+ body.Instructions.Add (Instruction.Create (OpCodes.Conv_I));
+ break;
+
+ case "System.Single":
+ supported = true;
+ body.Instructions.Add (Instruction.Create (OpCodes.Ldc_R4, 0.0F));
+ break;
+
+ case "System.Double":
+ supported = true;
+ body.Instructions.Add (Instruction.Create (OpCodes.Ldc_R8, 0.0));
+ break;
+ }
+
+ if (supported) {
+ body.Instructions.Add (Instruction.Create (OpCodes.Stloc, retval));
+ return;
+ }
+
+ throw new InvalidOperationException ($"Unsupported type: '{type.FullName}'");
+ }
+
+
+ Instruction GetLoadArgInstruction (uint nparam, ParameterDefinition pdef)
+ {
+ OpCode ldargOp;
+ bool paramRef = false;
+
+ switch (nparam++) {
+ case 0:
+ ldargOp = OpCodes.Ldarg_0;
+ break;
+
+ case 1:
+ ldargOp = OpCodes.Ldarg_1;
+ break;
+
+ case 2:
+ ldargOp = OpCodes.Ldarg_2;
+ break;
+
+ case 3:
+ ldargOp = OpCodes.Ldarg_3;
+ break;
+
+ default:
+ ldargOp = OpCodes.Ldarg_S;
+ paramRef = true;
+ break;
+ }
+
+ if (!paramRef) {
+ return Instruction.Create (ldargOp);
+ }
+
+ return Instruction.Create (ldargOp, pdef);
+ }
+
TypeReference MapToBlittableTypeIfNecessary (TypeReference type, out bool typeMapped)
{
if (type.IsBlittable () || String.Compare ("System.Void", type.FullName, StringComparison.Ordinal) == 0) {
@@ -328,6 +510,41 @@ CustomAttribute CreateImportedUnmanagedCallersOnlyAttribute (AssemblyDefinition
{
return new CustomAttribute (targetAssembly.MainModule.ImportReference (unmanagedCallersOnlyAtributeCtor));
}
+
+ MethodDefinition? FindMethod (TypeDefinition type, string methodName, bool required)
+ {
+ log.LogDebugMessage ($"Looking for method '{methodName}' in type {type}");
+ foreach (MethodDefinition method in type.Methods) {
+ log.LogDebugMessage ($" method: {method.Name}");
+ if (String.Compare (methodName, method.Name, StringComparison.Ordinal) == 0) {
+ log.LogDebugMessage (" match!");
+ return method;
+ }
+ }
+
+ if (required) {
+ throw new InvalidOperationException ($"Internal error: required method '{methodName}' in type {type} not found");
+ }
+
+ return null;
+ }
+
+ TypeDefinition? FindType (AssemblyDefinition asm, string typeName, bool required)
+ {
+ log.LogDebugMessage ($"Looking for type '{typeName}' in assembly '{asm}'");
+ foreach (TypeDefinition t in asm.MainModule.Types) {
+ log.LogDebugMessage ($" checking {t.FullName}");
+ if (String.Compare (typeName, t.FullName, StringComparison.Ordinal) == 0) {
+ log.LogDebugMessage ($" match!");
+ return t;
+ }
+ }
+
+ if (required) {
+ throw new InvalidOperationException ($"Internal error: required type '{typeName}' in assembly {asm} not found");
+ }
+
+ return null;
+ }
}
}
-#endif
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsClassifier.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsClassifier.cs
index 478c52389d0..3a7d4712b3f 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsClassifier.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsClassifier.cs
@@ -5,14 +5,14 @@
using Java.Interop.Tools.Cecil;
using Java.Interop.Tools.JavaCallableWrappers;
using Java.Interop.Tools.TypeNameMappings;
+
using Microsoft.Android.Build.Tasks;
using Microsoft.Build.Utilities;
using Mono.Cecil;
namespace Xamarin.Android.Tasks
{
-#if ENABLE_MARSHAL_METHODS
- public sealed class MarshalMethodEntry
+ sealed class MarshalMethodEntry
{
///
/// The "real" native callback, used if it doesn't contain any non-blittable types in its parameters
@@ -27,16 +27,17 @@ public sealed class MarshalMethodEntry
///
public MethodDefinition? NativeCallbackWrapper { get; set; }
public TypeDefinition DeclaringType { get; }
- public MethodDefinition Connector { get; }
- public MethodDefinition RegisteredMethod { get; }
- public MethodDefinition ImplementedMethod { get; }
- public FieldDefinition CallbackField { get; }
+ public MethodDefinition? Connector { get; }
+ public MethodDefinition? RegisteredMethod { get; }
+ public MethodDefinition? ImplementedMethod { get; }
+ public FieldDefinition? CallbackField { get; }
public string JniTypeName { get; }
public string JniMethodName { get; }
public string JniMethodSignature { get; }
public bool NeedsBlittableWorkaround { get; }
public MethodDefinition NativeCallback => NativeCallbackWrapper ?? nativeCallbackReal;
+ public bool IsSpecial { get; }
public MarshalMethodEntry (TypeDefinition declaringType, MethodDefinition nativeCallback, MethodDefinition connector, MethodDefinition
registeredMethod, MethodDefinition implementedMethod, FieldDefinition callbackField, string jniTypeName,
@@ -52,6 +53,17 @@ public MarshalMethodEntry (TypeDefinition declaringType, MethodDefinition native
JniMethodName = EnsureNonEmpty (jniName, nameof (jniName));
JniMethodSignature = EnsureNonEmpty (jniSignature, nameof (jniSignature));
NeedsBlittableWorkaround = needsBlittableWorkaround;
+ IsSpecial = false;
+ }
+
+ public MarshalMethodEntry (TypeDefinition declaringType, MethodDefinition nativeCallback, string jniTypeName, string jniName, string jniSignature)
+ {
+ DeclaringType = declaringType ?? throw new ArgumentNullException (nameof (declaringType));
+ nativeCallbackReal = nativeCallback ?? throw new ArgumentNullException (nameof (nativeCallback));
+ JniTypeName = EnsureNonEmpty (jniTypeName, nameof (jniTypeName));
+ JniMethodName = EnsureNonEmpty (jniName, nameof (jniName));
+ JniMethodSignature = EnsureNonEmpty (jniSignature, nameof (jniSignature));
+ IsSpecial = true;
}
string EnsureNonEmpty (string s, string argName)
@@ -63,11 +75,9 @@ string EnsureNonEmpty (string s, string argName)
return s;
}
}
-#endif
class MarshalMethodsClassifier : JavaCallableMethodClassifier
{
-#if ENABLE_MARSHAL_METHODS
sealed class ConnectorInfo
{
public string MethodName { get; }
@@ -142,8 +152,7 @@ string MapType (TypeReference typeRef)
}
if (typeDef.IsEnum) {
- // TODO: get the underlying type
- return "System.Int32";
+ return GetEnumUnderlyingType (typeDef).FullName;
}
}
@@ -155,9 +164,27 @@ string MapType (TypeReference typeRef)
return typeName;
}
+ // Android.Graphics.Color is mapped to/from a native `int`
+ if (String.Compare (typeName, "Android.Graphics.Color", StringComparison.Ordinal) == 0) {
+ return "System.Int32";
+ }
+
return "System.IntPtr";
}
+ static TypeReference GetEnumUnderlyingType (TypeDefinition td)
+ {
+ var fields = td.Fields;
+
+ for (int i = 0; i < fields.Count; i++) {
+ var field = fields [i];
+ if (!field.IsStatic)
+ return field.FieldType;
+ }
+
+ throw new InvalidOperationException ($"Unable to determine underlying type of the '{td.FullName}' enum");
+ }
+
public bool Matches (MethodDefinition method)
{
if (method.Parameters.Count != paramTypes.Count || !method.IsStatic) {
@@ -166,7 +193,7 @@ public bool Matches (MethodDefinition method)
}
if (String.Compare (returnType, method.ReturnType.FullName, StringComparison.Ordinal) != 0) {
- log.LogWarning ($"Method '{method.FullName}' doesn't match native callback signature (invalid return type)");
+ log.LogWarning ($"Method '{method.FullName}' doesn't match native callback signature (invalid return type: expected '{returnType}', found '{method.ReturnType.FullName}')");
return false;
}
@@ -203,23 +230,19 @@ public bool Matches (MethodDefinition method)
public ICollection Assemblies => assemblies;
public ulong RejectedMethodCount => rejectedMethodCount;
public ulong WrappedMethodCount => wrappedMethodCount;
-#endif
public MarshalMethodsClassifier (TypeDefinitionCache tdCache, DirectoryAssemblyResolver res, TaskLoggingHelper log)
{
-#if ENABLE_MARSHAL_METHODS
this.log = log ?? throw new ArgumentNullException (nameof (log));
this.tdCache = tdCache ?? throw new ArgumentNullException (nameof (tdCache));
resolver = res ?? throw new ArgumentNullException (nameof (tdCache));
marshalMethods = new Dictionary> (StringComparer.Ordinal);
assemblies = new HashSet ();
typesWithDynamicallyRegisteredMethods = new HashSet ();
-#endif
}
public override bool ShouldBeDynamicallyRegistered (TypeDefinition topType, MethodDefinition registeredMethod, MethodDefinition implementedMethod, CustomAttribute? registerAttribute)
{
-#if ENABLE_MARSHAL_METHODS
if (registeredMethod == null) {
throw new ArgumentNullException (nameof (registeredMethod));
}
@@ -237,20 +260,144 @@ public override bool ShouldBeDynamicallyRegistered (TypeDefinition topType, Meth
}
typesWithDynamicallyRegisteredMethods.Add (topType);
-#endif // def ENABLE_MARSHAL_METHODS
return true;
}
-#if ENABLE_MARSHAL_METHODS
public bool FoundDynamicallyRegisteredMethods (TypeDefinition type)
{
return typesWithDynamicallyRegisteredMethods.Contains (type);
}
+ void AddTypeManagerSpecialCaseMethods ()
+ {
+ const string FullTypeName = "Java.Interop.TypeManager+JavaTypeManager, Mono.Android";
+
+ AssemblyDefinition monoAndroid = resolver.Resolve ("Mono.Android");
+ TypeDefinition? typeManager = monoAndroid?.MainModule.FindType ("Java.Interop.TypeManager");
+ TypeDefinition? javaTypeManager = typeManager?.GetNestedType ("JavaTypeManager");
+
+ if (javaTypeManager == null) {
+ throw new InvalidOperationException ($"Internal error: unable to find the {FullTypeName} type in the Mono.Android assembly");
+ }
+
+ MethodDefinition? nActivate_mm = null;
+ MethodDefinition? nActivate = null;
+
+ foreach (MethodDefinition method in javaTypeManager.Methods) {
+ if (nActivate_mm == null && IsMatchingMethod (method, "n_Activate_mm")) {
+ if (method.GetCustomAttributes ("System.Runtime.InteropServices.UnmanagedCallersOnlyAttribute").Any (cattr => cattr != null)) {
+ nActivate_mm = method;
+ } else {
+ log.LogWarning ($"Method '{method.FullName}' isn't decorated with the UnmanagedCallersOnly attribute");
+ continue;
+ }
+ }
+
+ if (nActivate == null && IsMatchingMethod (method, "n_Activate")) {
+ nActivate = method;
+ }
+
+ if (nActivate_mm != null && nActivate != null) {
+ break;
+ }
+ }
+
+ if (nActivate_mm == null) {
+ ThrowMissingMethod ("nActivate_mm");
+ }
+
+ if (nActivate == null) {
+ ThrowMissingMethod ("nActivate");
+ }
+
+ string? jniTypeName = null;
+ foreach (CustomAttribute cattr in javaTypeManager.GetCustomAttributes ("Android.Runtime.RegisterAttribute")) {
+ if (cattr.ConstructorArguments.Count != 1) {
+ log.LogDebugMessage ($"[Register] attribute on type '{FullTypeName}' is expected to have 1 constructor argument, found {cattr.ConstructorArguments.Count}");
+ continue;
+ }
+
+ jniTypeName = (string)cattr.ConstructorArguments[0].Value;
+ if (!String.IsNullOrEmpty (jniTypeName)) {
+ break;
+ }
+ }
+
+ string? jniMethodName = null;
+ string? jniSignature = null;
+ foreach (CustomAttribute cattr in nActivate.GetCustomAttributes ("Android.Runtime.RegisterAttribute")) {
+ if (cattr.ConstructorArguments.Count != 3) {
+ log.LogDebugMessage ($"[Register] attribute on method '{nActivate.FullName}' is expected to have 3 constructor arguments, found {cattr.ConstructorArguments.Count}");
+ continue;
+ }
+
+ jniMethodName = (string)cattr.ConstructorArguments[0].Value;
+ jniSignature = (string)cattr.ConstructorArguments[1].Value;
+
+ if (!String.IsNullOrEmpty (jniMethodName) && !String.IsNullOrEmpty (jniSignature)) {
+ break;
+ }
+ }
+
+ bool missingInfo = false;
+ if (String.IsNullOrEmpty (jniTypeName)) {
+ missingInfo = true;
+ log.LogDebugMessage ($"Failed to obtain Java type name from the [Register] attribute on type '{FullTypeName}'");
+ }
+
+ if (String.IsNullOrEmpty (jniMethodName)) {
+ missingInfo = true;
+ log.LogDebugMessage ($"Failed to obtain Java method name from the [Register] attribute on method '{nActivate.FullName}'");
+ }
+
+ if (String.IsNullOrEmpty (jniSignature)) {
+ missingInfo = true;
+ log.LogDebugMessage ($"Failed to obtain Java method signature from the [Register] attribute on method '{nActivate.FullName}'");
+ }
+
+ if (missingInfo) {
+ throw new InvalidOperationException ($"Missing information while constructing marshal method for the '{nActivate_mm.FullName}' method");
+ }
+
+ var entry = new MarshalMethodEntry (javaTypeManager, nActivate_mm, jniTypeName, jniMethodName, jniSignature);
+ marshalMethods.Add (".:!SpEcIaL:Java.Interop.TypeManager+JavaTypeManager::n_Activate_mm", new List { entry });
+
+ void ThrowMissingMethod (string name)
+ {
+ throw new InvalidOperationException ($"Internal error: unable to find the '{name}' method in the '{FullTypeName}' type");
+ }
+
+ bool IsMatchingMethod (MethodDefinition method, string name)
+ {
+ if (String.Compare (name, method.Name, StringComparison.Ordinal) != 0) {
+ return false;
+ }
+
+ if (!method.IsStatic) {
+ log.LogWarning ($"Method '{method.FullName}' is not static");
+ return false;
+ }
+
+ if (!method.IsPrivate) {
+ log.LogWarning ($"Method '{method.FullName}' is not private");
+ return false;
+ }
+
+ return true;
+ }
+ }
+
+ ///
+ /// Adds MarshalMethodEntry for each method that won't be returned by the JavaInterop type scanner, mostly
+ /// used for hand-written methods (e.g. Java.Interop.TypeManager+JavaTypeManager::n_Activate)
+ ///
+ public void AddSpecialCaseMethods ()
+ {
+ AddTypeManagerSpecialCaseMethods ();
+ }
+
bool IsDynamicallyRegistered (TypeDefinition topType, MethodDefinition registeredMethod, MethodDefinition implementedMethod, CustomAttribute registerAttribute)
{
- Console.WriteLine ($"Classifying:\n\tmethod: {implementedMethod.FullName}\n\tregistered method: {registeredMethod.FullName})\n\tAttr: {registerAttribute.AttributeType.FullName} (parameter count: {registerAttribute.ConstructorArguments.Count})");
- Console.WriteLine ($"\tTop type: {topType.FullName}\n\tManaged type: {registeredMethod.DeclaringType.FullName}, {registeredMethod.DeclaringType.GetPartialAssemblyName (tdCache)}");
if (registerAttribute.ConstructorArguments.Count != 3) {
log.LogWarning ($"Method '{registeredMethod.FullName}' will be registered dynamically, not enough arguments to the [Register] attribute to generate marshal method.");
return true;
@@ -258,8 +405,6 @@ bool IsDynamicallyRegistered (TypeDefinition topType, MethodDefinition registere
var connector = new ConnectorInfo ((string)registerAttribute.ConstructorArguments[2].Value);
- Console.WriteLine ($"\tconnector: {connector.MethodName} (from spec: '{(string)registerAttribute.ConstructorArguments[2].Value}')");
-
if (IsStandardHandler (topType, connector, registeredMethod, implementedMethod, jniName: (string)registerAttribute.ConstructorArguments[0].Value, jniSignature: (string)registerAttribute.ConstructorArguments[1].Value)) {
return false;
}
@@ -269,7 +414,6 @@ bool IsDynamicallyRegistered (TypeDefinition topType, MethodDefinition registere
return true;
}
- // TODO: Probably should check if all the methods and fields are private and static - only then it is safe(ish) to remove them
bool IsStandardHandler (TypeDefinition topType, ConnectorInfo connector, MethodDefinition registeredMethod, MethodDefinition implementedMethod, string jniName, string jniSignature)
{
const string HandlerNameStart = "Get";
@@ -291,7 +435,6 @@ bool IsStandardHandler (TypeDefinition topType, ConnectorInfo connector, MethodD
string delegateFieldName = $"cb_{Char.ToLowerInvariant (callbackNameCore[0])}{callbackNameCore.Substring (1)}";
TypeDefinition connectorDeclaringType = connector.AssemblyName == null ? registeredMethod.DeclaringType : FindType (resolver.Resolve (connector.AssemblyName), connector.TypeName);
- Console.WriteLine ($"\tconnector name: {connectorName}\n\tnative callback name: {nativeCallbackName}\n\tdelegate field name: {delegateFieldName}");
MethodDefinition connectorMethod = FindMethod (connectorDeclaringType, connectorName);
if (connectorMethod == null) {
@@ -355,13 +498,7 @@ bool IsStandardHandler (TypeDefinition topType, ConnectorInfo connector, MethodD
// method.CallbackField?.DeclaringType == 'null'
// method.CallbackField?.DeclaringType.Fields == 'null'
- Console.WriteLine ($"##G1: {implementedMethod.DeclaringType.FullName} -> {JavaNativeTypeManager.ToJniName (implementedMethod.DeclaringType, tdCache)}");
- Console.WriteLine ($"##G1: top type: {topType.FullName} -> {JavaNativeTypeManager.ToJniName (topType, tdCache)}");
- Console.WriteLine ($"##G1: connectorMethod: {connectorMethod?.FullName}");
- Console.WriteLine ($"##G1: delegateField: {delegateField?.FullName}");
-
StoreMethod (
- connectorName,
registeredMethod,
new MarshalMethodEntry (
topType,
@@ -468,7 +605,8 @@ bool LogReasonWhyAndReturnFailure (string why)
void WarnWhy (string why)
{
- log.LogWarning ($"Method '{method.FullName}' {why}. A workaround is required, this may make the application slower");
+ // TODO: change to LogWarning once the generator can output code which requires no non-blittable wrappers
+ log.LogDebugMessage ($"Method '{method.FullName}' {why}. A workaround is required, this may make the application slower");
}
}
@@ -545,7 +683,7 @@ FieldDefinition FindField (TypeDefinition type, string fieldName, bool lookForIn
return FindField (tdCache.Resolve (type.BaseType), fieldName, lookForInherited);
}
- void StoreMethod (string connectorName, MethodDefinition registeredMethod, MarshalMethodEntry entry)
+ void StoreMethod (MethodDefinition registeredMethod, MarshalMethodEntry entry)
{
string typeName = registeredMethod.DeclaringType.FullName.Replace ('/', '+');
string key = $"{typeName}, {registeredMethod.DeclaringType.GetPartialAssemblyName (tdCache)}\t{registeredMethod.Name}";
@@ -571,6 +709,5 @@ void StoreAssembly (AssemblyDefinition asm)
assemblies.Add (asm);
}
-#endif
}
}
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsNativeAssemblyGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsNativeAssemblyGenerator.cs
index e34a3395f44..d83208521e5 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsNativeAssemblyGenerator.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsNativeAssemblyGenerator.cs
@@ -1,16 +1,10 @@
-#if ENABLE_MARSHAL_METHODS
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
-using System.Reflection.Metadata;
using System.Text;
-using Java.Interop.Tools.TypeNameMappings;
-using Java.Interop.Tools.JavaCallableWrappers;
-
using Microsoft.Android.Build.Tasks;
-using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Xamarin.Android.Tasks.LLVMIR;
@@ -205,6 +199,20 @@ sealed class MarshalMethodName
List methods;
List> classes = new List> ();
+ readonly bool generateEmptyCode;
+
+ ///
+ /// Constructor to be used ONLY when marshal methods are DISABLED
+ ///
+ public MarshalMethodsNativeAssemblyGenerator (ICollection uniqueAssemblyNames)
+ {
+ this.uniqueAssemblyNames = uniqueAssemblyNames ?? throw new ArgumentNullException (nameof (uniqueAssemblyNames));
+ generateEmptyCode = true;
+ }
+
+ ///
+ /// Constructor to be used ONLY when marshal methods are ENABLED
+ ///
public MarshalMethodsNativeAssemblyGenerator (int numberOfAssembliesInApk, ICollection uniqueAssemblyNames, IDictionary> marshalMethods, TaskLoggingHelper logger)
{
this.numberOfAssembliesInApk = numberOfAssembliesInApk;
@@ -212,15 +220,12 @@ public MarshalMethodsNativeAssemblyGenerator (int numberOfAssembliesInApk, IColl
this.marshalMethods = marshalMethods;
this.logger = logger ?? throw new ArgumentNullException (nameof (logger));
- if (uniqueAssemblyNames.Count != numberOfAssembliesInApk) {
- throw new InvalidOperationException ("Internal error: number of assemblies in the apk doesn't match the number of unique assembly names");
- }
+ generateEmptyCode = false;
}
public override void Init ()
{
- Console.WriteLine ($"Marshal methods count: {marshalMethods?.Count ?? 0}");
- if (marshalMethods == null || marshalMethods.Count == 0) {
+ if (generateEmptyCode || marshalMethods == null || marshalMethods.Count == 0) {
return;
}
@@ -267,11 +272,8 @@ public override void Init ()
continue;
}
- Console.WriteLine ($"Overloaded MM: {mmiList[0].NativeSymbolName}");
foreach (MarshalMethodInfo overloadedMethod in mmiList) {
- Console.WriteLine ($" implemented in: {overloadedMethod.Method.DeclaringType.FullName} ({overloadedMethod.Method.RegisteredMethod.FullName})");
overloadedMethod.NativeSymbolName = MakeNativeSymbolName (overloadedMethod.Method, useFullNativeSignature: true);
- Console.WriteLine ($" new native symbol name: {overloadedMethod.NativeSymbolName}");
}
}
@@ -307,7 +309,6 @@ string MakeNativeSymbolName (MarshalMethodEntry entry, bool useFullNativeSignatu
sb.Append (MangleForJni ($"n_{entry.JniMethodName}"));
if (useFullNativeSignature) {
- Console.WriteLine (" Using FULL signature");
string signature = entry.JniMethodSignature;
if (signature.Length < 2) {
ThrowInvalidSignature (signature, "must be at least two characters long");
@@ -339,20 +340,10 @@ void ThrowInvalidSignature (string signature, string reason)
void ProcessAndAddMethod (List allMethods, MarshalMethodEntry entry, bool useFullNativeSignature, Dictionary seenClasses, Dictionary> overloadedNativeSymbolNames)
{
- Console.WriteLine ("marshal method:");
- Console.WriteLine ($" top type: {entry.DeclaringType.FullName} (token: 0x{entry.DeclaringType.MetadataToken.ToUInt32 ():x})");
- Console.WriteLine ($" registered method: [{entry.RegisteredMethod.DeclaringType.FullName}] {entry.RegisteredMethod.FullName}");
- Console.WriteLine ($" implemented method: [{entry.ImplementedMethod.DeclaringType.FullName}] {entry.ImplementedMethod.FullName}");
- Console.WriteLine ($" native callback: {entry.NativeCallback.FullName} (token: 0x{entry.NativeCallback.MetadataToken.ToUInt32 ():x})");
- Console.WriteLine ($" native callback wrapper: {entry.NativeCallbackWrapper}");
- Console.WriteLine ($" connector: {entry.Connector.FullName}");
- Console.WriteLine ($" JNI name: {entry.JniMethodName}");
- Console.WriteLine ($" JNI signature: {entry.JniMethodSignature}");
-
CecilMethodDefinition nativeCallback = entry.NativeCallback;
string nativeSymbolName = MakeNativeSymbolName (entry, useFullNativeSignature);
string klass = $"{nativeCallback.DeclaringType.FullName}, {nativeCallback.Module.Assembly.FullName}";
- Console.WriteLine ($" klass == {klass}");
+
if (!seenClasses.TryGetValue (klass, out int classIndex)) {
classIndex = classes.Count;
seenClasses.Add (klass, classIndex);
@@ -365,25 +356,14 @@ void ProcessAndAddMethod (List allMethods, MarshalMethodEntry
classes.Add (new StructureInstance (mc));
}
- Console.WriteLine (" about to parse JNI sig");
- (Type returnType, List? parameters) = ParseJniSignature (entry.JniMethodSignature, entry.ImplementedMethod);
- Console.WriteLine (" parsed!");
+ // Methods with `IsSpecial == true` are "synthetic" methods - they contain only the callback reference
+ (Type returnType, List? parameters) = ParseJniSignature (entry.JniMethodSignature, entry.IsSpecial ? entry.NativeCallback : entry.ImplementedMethod);
var method = new MarshalMethodInfo (entry, returnType, nativeSymbolName: nativeSymbolName, classIndex);
if (parameters != null && parameters.Count > 0) {
method.Parameters.AddRange (parameters);
}
- Console.WriteLine ($" Generated native symbol: {method.NativeSymbolName}");
- Console.WriteLine ($" Parsed return type: {returnType}");
- if (method.Parameters.Count > 0) {
- Console.WriteLine (" Parsed parameters:");
- foreach (LlvmIrFunctionParameter p in method.Parameters) {
- Console.WriteLine ($" {p.Type} {p.Name}");
- }
- }
- Console.WriteLine ();
-
if (!overloadedNativeSymbolNames.TryGetValue (method.NativeSymbolName, out List overloadedMethods)) {
overloadedMethods = new List ();
overloadedNativeSymbolNames.Add (method.NativeSymbolName, overloadedMethods);
@@ -395,19 +375,19 @@ void ProcessAndAddMethod (List allMethods, MarshalMethodEntry
string MangleForJni (string name)
{
- Console.WriteLine ($" mangling '{name}'");
var sb = new StringBuilder ();
foreach (char ch in name) {
switch (ch) {
- case '_':
- sb.Append ("_1");
- break;
-
case '/':
+ case '.':
sb.Append ('_');
break;
+ case '_':
+ sb.Append ("_1");
+ break;
+
case ';':
sb.Append ("_2");
break;
@@ -416,16 +396,12 @@ string MangleForJni (string name)
sb.Append ("_3");
break;
- case '$':
- sb.Append ("_00024");
- break;
-
default:
- if ((int)ch > 127) {
+ if ((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') || (ch >= '0' && ch <= '9')) {
+ sb.Append (ch);
+ } else {
sb.Append ("_0");
sb.Append (((int)ch).ToString ("x04"));
- } else {
- sb.Append (ch);
}
break;
}
@@ -477,10 +453,8 @@ string MangleForJni (string name)
Type? JniTypeToManaged (char jniType)
{
- Console.WriteLine ($" turning JNI type '{jniType}' into managed type");
if (jniSimpleTypeMap.TryGetValue (jniType, out Type managedType)) {
idx++;
- Console.WriteLine ($" will return {managedType}");
return managedType;
}
@@ -489,24 +463,21 @@ string MangleForJni (string name)
}
if (jniType == '[') {
- Console.WriteLine (" an array");
idx++;
jniType = signature[idx];
if (jniArrayTypeMap.TryGetValue (jniType, out managedType)) {
if (jniType == 'L') {
- Console.WriteLine (" skipping");
JavaClassToManaged (justSkip: true);
} else {
idx++;
}
- Console.WriteLine ($" will return {managedType}");
+
return managedType;
}
throw new InvalidOperationException ($"Unsupported JNI array type '{jniType}' at index {idx} of signature '{signature}'");
}
- Console.WriteLine (" returning NULL managed type");
return null;
}
@@ -609,17 +580,19 @@ protected override void Write (LlvmIrGenerator generator)
generator.WriteArray (mm_class_names, "mm_class_names", "Names of classes in which marshal methods reside");
var uniqueMethods = new Dictionary ();
- foreach (MarshalMethodInfo mmi in methods) {
- string asmName = Path.GetFileName (mmi.Method.NativeCallback.Module.Assembly.MainModule.FileName);
- if (!asmNameToIndex.TryGetValue (asmName, out uint idx)) {
- throw new InvalidOperationException ($"Internal error: failed to match assembly name '{asmName}' to cache array index");
- }
+ if (!generateEmptyCode && methods != null) {
+ foreach (MarshalMethodInfo mmi in methods) {
+ string asmName = Path.GetFileName (mmi.Method.NativeCallback.Module.Assembly.MainModule.FileName);
+ if (!asmNameToIndex.TryGetValue (asmName, out uint idx)) {
+ throw new InvalidOperationException ($"Internal error: failed to match assembly name '{asmName}' to cache array index");
+ }
- ulong id = ((ulong)idx << 32) | (ulong)mmi.Method.NativeCallback.MetadataToken.ToUInt32 ();
- if (uniqueMethods.ContainsKey (id)) {
- continue;
+ ulong id = ((ulong)idx << 32) | (ulong)mmi.Method.NativeCallback.MetadataToken.ToUInt32 ();
+ if (uniqueMethods.ContainsKey (id)) {
+ continue;
+ }
+ uniqueMethods.Add (id, mmi);
}
- uniqueMethods.Add (id, mmi);
}
MarshalMethodName name;
@@ -672,7 +645,7 @@ void RenderMethodNameWithParams (CecilMethodDefinition md, StringBuilder buffer)
void WriteNativeMethods (LlvmIrGenerator generator, Dictionary asmNameToIndex, LlvmIrVariableReference get_function_pointer_ref)
{
- if (methods == null || methods.Count == 0) {
+ if (generateEmptyCode || methods == null || methods.Count == 0) {
return;
}
@@ -713,7 +686,7 @@ void WriteMarshalMethod (LlvmIrGenerator generator, MarshalMethodInfo method, Ll
parameters: method.Parameters
);
- generator.WriteFunctionStart (func);
+ generator.WriteFunctionStart (func, $"Method: {nativeCallback.FullName}\nAssembly: {nativeCallback.Module.Assembly.Name}");
LlvmIrFunctionLocalVariable callbackVariable1 = generator.EmitLoadInstruction (func, backingFieldRef, "cb1");
var callbackVariable1Ref = new LlvmIrVariableReference (callbackVariable1, isGlobal: false);
@@ -816,6 +789,7 @@ void WriteClassCache (LlvmIrGenerator generator)
generator.WriteStructureArray (marshalMethodsClass, classes, LlvmIrVariableOptions.GlobalWritable, "marshal_methods_class_cache");
}
+ // TODO: this should probably be moved to a separate writer, since not only marshal methods use the cache
void WriteAssemblyImageCache (LlvmIrGenerator generator, out Dictionary asmNameToIndex)
{
bool is64Bit = generator.Is64Bit;
@@ -834,6 +808,7 @@ void WriteHashes () where T: struct
{
var hashes = new Dictionary ();
uint index = 0;
+
foreach (string name in uniqueAssemblyNames) {
string clippedName = Path.GetFileNameWithoutExtension (name);
ulong hashFull = HashName (name, is64Bit);
@@ -874,4 +849,3 @@ void WriteHashes () where T: struct
}
}
}
-#endif
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsState.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsState.cs
index 85063dc7dbe..66bf0fdba2e 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsState.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsState.cs
@@ -1,4 +1,3 @@
-#if ENABLE_MARSHAL_METHODS
using System;
using System.Collections.Generic;
@@ -14,4 +13,3 @@ public MarshalMethodsState (IDictionary> marsh
}
}
}
-#endif
diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj
index b2ac9098779..ae4a7e2eb2e 100644
--- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj
+++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Build.Tasks.csproj
@@ -22,10 +22,6 @@
$(NoWarn);CA1310
-
- $(DefineConstants);ENABLE_MARSHAL_METHODS
-
-
$(MicrosoftAndroidSdkOutDir)pdb2mdb.exe
@@ -408,6 +404,9 @@
ApplicationRegistration.java
+
+ JavaInteropTypeManager.java
+
diff --git a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets
index ff4261ab71c..dc0f72bf029 100644
--- a/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets
+++ b/src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets
@@ -41,6 +41,7 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
+
@@ -331,6 +332,10 @@ Copyright (C) 2011-2012 Xamarin. All rights reserved.
false
true
True
+ True
+ False
+ <_AndroidUseMarshalMethods Condition=" '$(AndroidIncludeDebugSymbols)' == 'True' ">False
+ <_AndroidUseMarshalMethods Condition=" '$(AndroidIncludeDebugSymbols)' != 'True' ">$(AndroidEnableMarshalMethods)
@@ -1350,7 +1355,7 @@ because xbuild doesn't support framework reference assemblies.
<_AndroidJarAndDexDirectory Condition=" '$(UsingAndroidNETSdk)' != 'True' ">$(TargetFrameworkDirectory)
<_AndroidJarAndDexDirectory Condition=" '$(UsingAndroidNETSdk)' == 'True' ">$(_XATargetFrameworkDirectories)
-
+
@@ -1371,19 +1376,28 @@ because xbuild doesn't support framework reference assemblies.
ResourceName="MonoRuntimeProvider.Bundled.20.java"
OutputPath="$(_AndroidIntermediateJavaSourceDirectory)mono\MonoRuntimeProvider.java"
/>
+
-
+
+
-
+
@@ -1462,12 +1476,16 @@ because xbuild doesn't support framework reference assemblies.
<_ManifestOutput Condition=" '$(AndroidManifestMerger)' == 'legacy' ">$(IntermediateOutputPath)android\AndroidManifest.xml
<_ManifestOutput Condition=" '$(AndroidManifestMerger)' != 'legacy' ">$(IntermediateOutputPath)AndroidManifest.xml
+ <_LinkingEnabled Condition=" '$(AndroidLinkMode)' != 'None'">True
+ <_LinkingEnabled Condition=" '$(AndroidLinkMode)' == 'None'">False
+ <_HaveMultipleRIDs Condition=" '$(RuntimeIdentifiers)' != '' ">True
+ <_HaveMultipleRIDs Condition=" '$(RuntimeIdentifiers)' == '' ">False
<_MergedManifestDocuments Condition=" '$(AndroidManifestMerger)' == 'legacy' " Include="@(ExtractedManifestDocuments)" />
@@ -1503,7 +1521,12 @@ because xbuild doesn't support framework reference assemblies.
SupportedAbis="@(_BuildTargetAbis)"
SupportedOSPlatformVersion="$(SupportedOSPlatformVersion)"
SkipJniAddNativeMethodRegistrationAttributeScan="$(_SkipJniAddNativeMethodRegistrationAttributeScan)"
- CheckedBuild="$(_AndroidCheckedBuild)">
+ CheckedBuild="$(_AndroidCheckedBuild)"
+ EnableMarshalMethods="$(_AndroidUseMarshalMethods)"
+ LinkingEnabled="$(_LinkingEnabled)"
+ HaveMultipleRIDs="$(_HaveMultipleRIDs)"
+ IntermediateOutputDirectory="$(IntermediateOutputPath)"
+ Environments="@(AndroidEnvironment);@(LibraryEnvironments)">
@@ -1594,7 +1617,6 @@ because xbuild doesn't support framework reference assemblies.
+
+
+
+
+
+
<_GeneratePackageManagerJavaDependsOn>
_GenerateJavaStubs;
+ _RunAotForAllRIDs;
_ManifestMerger;
_ConvertCustomView;
$(_AfterConvertCustomView);
- _GenerateEnvironmentFiles;
_AddStaticResources;
$(_AfterAddStaticResources);
_PrepareAssemblies;
@@ -1710,6 +1743,7 @@ because xbuild doesn't support framework reference assemblies.
RuntimeConfigBinFilePath="$(_BinaryRuntimeConfigPath)"
UsingAndroidNETSdk="$(UsingAndroidNETSdk)"
UseAssemblyStore="$(AndroidUseAssemblyStore)"
+ EnableMarshalMethods="$(_AndroidUseMarshalMethods)"
>
diff --git a/src/java-runtime/java-runtime.csproj b/src/java-runtime/java-runtime.csproj
index 175397c9410..60a41f213a2 100644
--- a/src/java-runtime/java-runtime.csproj
+++ b/src/java-runtime/java-runtime.csproj
@@ -19,6 +19,7 @@
+
diff --git a/src/Mono.Android/java/mono/android/app/IntentService.java b/src/java-runtime/java/mono/android/app/IntentService.java
similarity index 100%
rename from src/Mono.Android/java/mono/android/app/IntentService.java
rename to src/java-runtime/java/mono/android/app/IntentService.java
diff --git a/src/monodroid/CMakeLists.txt b/src/monodroid/CMakeLists.txt
index 41577f4f55a..7f45848792b 100644
--- a/src/monodroid/CMakeLists.txt
+++ b/src/monodroid/CMakeLists.txt
@@ -50,8 +50,6 @@ option(STRIP_DEBUG "Strip debugging information when linking" ${STRIP_DEBUG_DEFA
option(DISABLE_DEBUG "Disable the built-in debugging code" OFF)
option(USE_CCACHE "Use ccache, if found, to speed up recompilation" ${CCACHE_OPTION_DEFAULT})
-set(ENABLE_MARSHAL_METHODS False)
-
if((MINGW OR NOT WIN32) AND USE_CCACHE)
if(CMAKE_CXX_COMPILER MATCHES "/ccache/")
message(STATUS "ccache: compiler already uses ccache")
@@ -276,9 +274,6 @@ add_compile_definitions(HAVE_CONFIG_H)
add_compile_definitions(_REENTRANT)
add_compile_definitions(JI_DLL_EXPORT)
add_compile_definitions(MONO_DLL_EXPORT)
-if(ENABLE_MARSHAL_METHODS)
- add_compile_definitions(ENABLE_MARSHAL_METHODS)
-endif()
if(ENABLE_NET)
add_compile_definitions(NET)
@@ -476,9 +471,6 @@ endif()
set(XAMARIN_INTERNAL_API_LIB xa-internal-api${CHECKED_BUILD_INFIX})
set(XAMARIN_DEBUG_APP_HELPER_LIB xamarin-debug-app-helper${CHECKED_BUILD_INFIX})
set(XAMARIN_APP_STUB_LIB xamarin-app)
-if(ENABLE_MARSHAL_METHODS)
- set(XAMARIN_APP_MARSHALING_LIB xamarin-app-marshaling)
-endif()
string(TOLOWER ${CMAKE_BUILD_TYPE} XAMARIN_MONO_ANDROID_SUFFIX)
set(XAMARIN_MONO_ANDROID_LIB "mono-android${CHECKED_BUILD_INFIX}.${XAMARIN_MONO_ANDROID_SUFFIX}")
@@ -497,7 +489,6 @@ set(XAMARIN_MONODROID_SOURCES
${SOURCES_DIR}/logger.cc
${SOURCES_DIR}/jni-remapping.cc
${SOURCES_DIR}/monodroid-glue.cc
- ${SOURCES_DIR}/mono-image-loader.cc
${SOURCES_DIR}/osbridge.cc
${SOURCES_DIR}/shared-constants.cc
${SOURCES_DIR}/timezones.cc
@@ -515,7 +506,7 @@ if(ANDROID)
${LZ4_SOURCES}
)
- if(NOT DEBUG_BUILD AND ENABLE_NET AND ENABLE_MARSHAL_METHODS)
+ if(NOT DEBUG_BUILD AND ENABLE_NET)
list(APPEND XAMARIN_MONODROID_SOURCES
${SOURCES_DIR}/xamarin-android-app-context.cc
)
@@ -577,12 +568,6 @@ set(XAMARIN_DEBUG_APP_HELPER_SOURCES
${SOURCES_DIR}/shared-constants.cc
)
-if(ENABLE_MARSHAL_METHODS)
- set(XAMARIN_APP_MARSHALING_LIB_SOURCES
- ${SOURCES_DIR}/xamarin-app-marshaling.cc
- )
-endif()
-
# Build
configure_file(jni/host-config.h.in ${CMAKE_CURRENT_BINARY_DIR}/include/host-config.h)
@@ -622,20 +607,6 @@ target_link_options(
)
if(ANDROID)
- if(NOT DEBUG_BUILD AND ENABLE_NET AND ENABLE_MARSHAL_METHODS)
- # The marshaling lib is used only when building for devices
- add_library(
- ${XAMARIN_APP_MARSHALING_LIB}
- SHARED
- ${XAMARIN_APP_MARSHALING_LIB_SOURCES}
- )
-
- target_link_libraries(
- ${XAMARIN_APP_MARSHALING_LIB}
- ${MONOSGEN_LIB_LINK}
- )
- endif()
-
# Only Android builds need to go in separate directories, desktop builds have the same ABI
set_target_properties(
${XAMARIN_APP_STUB_LIB}
@@ -743,10 +714,3 @@ target_link_libraries(
${XAMARIN_MONO_ANDROID_LIB}
${LINK_LIBS} xamarin-app
)
-
-# if((NOT DEBUG_BUILD) AND ANDROID AND ENABLE_NET AND ENABLE_MARSHAL_METHODS)
-# target_link_libraries(
-# ${XAMARIN_MONO_ANDROID_LIB}
-# ${XAMARIN_APP_MARSHALING_LIB}
-# )
-# endif()
diff --git a/src/monodroid/jni/embedded-assemblies.cc b/src/monodroid/jni/embedded-assemblies.cc
index 0c4e0bf6986..de453cd2990 100644
--- a/src/monodroid/jni/embedded-assemblies.cc
+++ b/src/monodroid/jni/embedded-assemblies.cc
@@ -359,7 +359,6 @@ EmbeddedAssemblies::assembly_store_open_from_bundles (dynamic_local_string const& name, MonoAssemblyLoadContextGCHandle alc_gchandle, hash_t name_hash, uint8_t *assembly_data, uint32_t assembly_data_size) noexcept
{
- log_info (LOG_DEFAULT, "Loading assembly %s; hash 0x%zx", name.get (), name_hash);
MonoImageOpenStatus status;
MonoImage *image = mono_image_open_from_data_alc (
alc_gchandle,
@@ -71,7 +70,6 @@ namespace xamarin::android::internal {
force_inline static MonoImage* load (dynamic_local_string const& name, MonoAssemblyLoadContextGCHandle alc_gchandle, uint8_t *assembly_data, uint32_t assembly_data_size) noexcept
{
-
return load (name, alc_gchandle, xxhash::hash (name.get (), name.length ()), assembly_data, assembly_data_size);
}
#endif // def NET
@@ -114,23 +112,18 @@ namespace xamarin::android::internal {
force_inline static MonoImage* stash_and_return (MonoImage *image, MonoImageOpenStatus status, [[maybe_unused]] hash_t hash) noexcept
{
-#if ENABLE_MARSHAL_METHODS
- log_info (LOG_DEFAULT, "Stashing image %p; hash 0x%zx", image, hash);
-#endif
if (image == nullptr || status != MonoImageOpenStatus::MONO_IMAGE_OK) {
- log_warn (LOG_ASSEMBLY, "Failed to open assembly image for '%s'. %s", mono_image_strerror (status));
+ log_warn (LOG_ASSEMBLY, "Failed to open assembly image. %s", mono_image_strerror (status));
return nullptr;
}
#if defined (USE_CACHE)
ssize_t index = find_index (hash);
- log_info (LOG_DEFAULT, "Index matching the hash == %zd", index);
if (index < 0) {
- // TODO: Warn?
+ log_warn (LOG_ASSEMBLY, "Failed to look up image index for hash 0x%zx", hash);
return image;
}
- log_info (LOG_DEFAULT, "Stashing");
// We don't need to worry about locking here. Even if we're overwriting an entry just set by another
// thread, the image pointer is going to be the same (at least currently, it will change when we have
// support for unloadable Assembly Load Contexts) and the actual write operation to the destination is
@@ -141,7 +134,7 @@ namespace xamarin::android::internal {
}
#if defined (USE_CACHE)
- static size_t number_of_cache_index_entries;
+ static inline size_t number_of_cache_index_entries = application_config.number_of_assemblies_in_apk * number_of_assembly_name_forms_in_image_cache;;
#endif // def USE_CACHE
};
}
diff --git a/src/monodroid/jni/monodroid-glue-internal.hh b/src/monodroid/jni/monodroid-glue-internal.hh
index 122bda2c948..def9a263218 100644
--- a/src/monodroid/jni/monodroid-glue-internal.hh
+++ b/src/monodroid/jni/monodroid-glue-internal.hh
@@ -132,6 +132,7 @@ namespace xamarin::android::internal
uint8_t boundExceptionType;
int jniAddNativeMethodRegistrationAttributePresent;
bool jniRemappingInUse;
+ bool marshalMethodsEnabled;
};
#if defined (NET)
diff --git a/src/monodroid/jni/monodroid-glue.cc b/src/monodroid/jni/monodroid-glue.cc
index 516c5ff75ae..590a4ec55cc 100644
--- a/src/monodroid/jni/monodroid-glue.cc
+++ b/src/monodroid/jni/monodroid-glue.cc
@@ -841,6 +841,10 @@ MonodroidRuntime::mono_runtime_init ([[maybe_unused]] dynamic_local_string (monodroid_debugger_unhandled_exception));
- mono_add_internal_call ("Android.Runtime.JNIEnv::monodroid_unhandled_exception", reinterpret_cast(monodroid_unhandled_exception));
+ mono_add_internal_call ("Android.Runtime.RuntimeNativeMethods::monodroid_debugger_unhandled_exception", reinterpret_cast (monodroid_debugger_unhandled_exception));
+ mono_add_internal_call ("Android.Runtime.RuntimeNativeMethods::monodroid_unhandled_exception", reinterpret_cast(monodroid_unhandled_exception));
#endif // def NET
struct JnienvInitializeArgs init = {};
@@ -1090,6 +1094,7 @@ MonodroidRuntime::init_android_runtime (
init.boundExceptionType = application_config.bound_exception_type;
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;
// GC threshold is 90% of the max GREF count
init.grefGcThreshold = static_cast(androidSystem.get_gref_gc_threshold ());
@@ -1100,22 +1105,22 @@ MonodroidRuntime::init_android_runtime (
Class_getName = env->GetMethodID (init.grefClass, "getName", "()Ljava/lang/String;");
init.Class_forName = env->GetStaticMethodID (init.grefClass, "forName", "(Ljava/lang/String;ZLjava/lang/ClassLoader;)Ljava/lang/Class;");
- MonoAssembly *assm;
+ MonoAssembly *mono_android_assembly;
+
#if defined (NET)
- assm = utils.monodroid_load_assembly (default_alc, SharedConstants::MONO_ANDROID_ASSEMBLY_NAME);
+ mono_android_assembly = utils.monodroid_load_assembly (default_alc, SharedConstants::MONO_ANDROID_ASSEMBLY_NAME);
#else // def NET
- assm = utils.monodroid_load_assembly (domain, SharedConstants::MONO_ANDROID_ASSEMBLY_NAME);
+ mono_android_assembly = utils.monodroid_load_assembly (domain, SharedConstants::MONO_ANDROID_ASSEMBLY_NAME);
#endif // ndef NET
- MonoImage *image = mono_assembly_get_image (assm);
+ MonoImage *mono_android_assembly_image = mono_assembly_get_image (mono_android_assembly);
uint32_t i = 0;
-
for ( ; i < OSBridge::NUM_XA_GC_BRIDGE_TYPES; ++i) {
lookup_bridge_info (
#if !defined (NET)
domain,
#endif // ndef NET
- image,
+ mono_android_assembly_image,
&osBridge.get_java_gc_bridge_type (i),
&osBridge.get_java_gc_bridge_info (i)
);
@@ -1126,18 +1131,18 @@ MonodroidRuntime::init_android_runtime (
if constexpr (is_running_on_desktop) {
#if defined (NET)
- runtime = mono_class_from_name (image, SharedConstants::ANDROID_RUNTIME_NS_NAME, SharedConstants::JNIENV_CLASS_NAME);
+ runtime = mono_class_from_name (mono_android_assembly_image, SharedConstants::ANDROID_RUNTIME_NS_NAME, SharedConstants::JNIENVINIT_CLASS_NAME);
#else
- runtime = utils.monodroid_get_class_from_image (domain, image, SharedConstants::ANDROID_RUNTIME_NS_NAME, SharedConstants::JNIENV_CLASS_NAME);
+ runtime = utils.monodroid_get_class_from_image (domain, mono_android_assembly_image, SharedConstants::ANDROID_RUNTIME_NS_NAME, SharedConstants::JNIENVINIT_CLASS_NAME);
#endif // def NET
method = mono_class_get_method_from_name (runtime, "Initialize", 1);
} else {
- runtime = mono_class_get (image, application_config.android_runtime_jnienv_class_token);
- method = mono_get_method (image, application_config.jnienv_initialize_method_token, runtime);
+ runtime = mono_class_get (mono_android_assembly_image, application_config.android_runtime_jnienv_class_token);
+ method = mono_get_method (mono_android_assembly_image, application_config.jnienv_initialize_method_token, runtime);
}
- abort_unless (runtime != nullptr, "INTERNAL ERROR: unable to find the Android.Runtime.JNIEnv class!");
- abort_unless (method != nullptr, "INTERNAL ERROR: Unable to find the Android.Runtime.JNIEnv.Initialize method!");
+ abort_unless (runtime != nullptr, "INTERNAL ERROR: unable to find the Android.Runtime.JNIEnvInit class!");
+ abort_unless (method != nullptr, "INTERNAL ERROR: Unable to find the Android.Runtime.JNIEnvInit.Initialize method!");
MonoAssembly *ji_assm;
#if defined (NET)
@@ -1158,6 +1163,7 @@ MonodroidRuntime::init_android_runtime (
);
}
+ MonoError error;
/* 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.
*/
@@ -1165,21 +1171,13 @@ MonodroidRuntime::init_android_runtime (
if constexpr (is_running_on_desktop) {
registerType = mono_class_get_method_from_name (runtime, "RegisterJniNatives", 5);
} else {
- registerType = mono_get_method (image, application_config.jnienv_registerjninatives_method_token, runtime);
+ registerType = mono_get_method (mono_android_assembly_image, application_config.jnienv_registerjninatives_method_token, runtime);
#if defined (NET) && defined (ANDROID)
- MonoError error;
jnienv_register_jni_natives = reinterpret_cast(mono_method_get_unmanaged_callers_only_ftnptr (registerType, &error));
#endif // def NET && def ANDROID
}
}
- abort_unless (registerType != nullptr, "INTERNAL ERROR: Unable to find Android.Runtime.JNIEnv.RegisterJniNatives!");
-
- 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);
- }
+ abort_unless (registerType != nullptr, "INTERNAL ERROR: Unable to find Android.Runtime.JNIEnvInit.RegisterJniNatives! %s", mono_error_get_message (&error));
jclass lrefLoaderClass = env->GetObjectClass (loader);
init.Loader_loadClass = env->GetMethodID (lrefLoaderClass, "loadClass", "(Ljava/lang/String;)Ljava/lang/Class;");
@@ -1198,9 +1196,16 @@ MonodroidRuntime::init_android_runtime (
}
#if defined (NET) && defined (ANDROID)
- MonoError error;
auto initialize = reinterpret_cast (mono_method_get_unmanaged_callers_only_ftnptr (method, &error));
- abort_unless (initialize != nullptr, "Failed to obtain unmanaged-callers-only pointer to the Android.Runtime.JNIEnv.Initialize method");
+ if (initialize == nullptr) {
+ log_fatal (LOG_DEFAULT, "Failed to get pointer to Initialize. Mono error: %s", mono_error_get_message (&error));
+ }
+
+ abort_unless (
+ initialize != nullptr,
+ "Failed to obtain unmanaged-callers-only pointer to the Android.Runtime.JNIEnvInit.Initialize method. %s",
+ mono_error_get_message (&error)
+ );
initialize (&init);
#else // def NET && def ANDROID
void *args [] = {
@@ -2402,7 +2407,7 @@ MonodroidRuntime::Java_mono_android_Runtime_initInternal (JNIEnv *env, jclass kl
#endif // ndef NET
}
-#if defined (RELEASE) && defined (ANDROID) && defined (NET) && ENABLE_MARSHAL_METHODS
+#if defined (RELEASE) && defined (ANDROID) && defined (NET)
xamarin_app_init (get_function_pointer_at_runtime);
#endif // def RELEASE && def ANDROID && def NET
startup_in_progress = false;
@@ -2495,18 +2500,6 @@ MonodroidRuntime::Java_mono_android_Runtime_register (JNIEnv *env, jstring manag
total_time_index = internal_timing->start_event (TimingEventKind::RuntimeRegister);
}
-#if defined (ENABLE_MARSHAL_METHODS)
- const char *mt_ptr = env->GetStringUTFChars (managedType, nullptr);
- log_info (LOG_DEFAULT, "[TESTING] Registering managed type: '%s'", mt_ptr);
- bool ignore = strcmp (mt_ptr, "HelloAndroid.MainActivity, HelloAndroid") == 0;
- env->ReleaseStringUTFChars (managedType, mt_ptr);
-
- if (ignore) {
- log_info (LOG_DEFAULT, "[TESTING] This type's registration is ignored");
- return;
- }
-#endif
-
jsize managedType_len = env->GetStringLength (managedType);
const jchar *managedType_ptr = env->GetStringChars (managedType, nullptr);
int methods_len = env->GetStringLength (methods);
@@ -2530,7 +2523,7 @@ MonodroidRuntime::Java_mono_android_Runtime_register (JNIEnv *env, jstring manag
domain = mono_domain_get ();
if constexpr (is_running_on_desktop) {
- MonoClass *runtime = utils.monodroid_get_class_from_name (domain, SharedConstants::MONO_ANDROID_ASSEMBLY_NAME, SharedConstants::ANDROID_RUNTIME_NS_NAME, SharedConstants::JNIENV_CLASS_NAME);
+ MonoClass *runtime = utils.monodroid_get_class_from_name (domain, SharedConstants::MONO_ANDROID_ASSEMBLY_NAME, SharedConstants::ANDROID_RUNTIME_NS_NAME, SharedConstants::JNIENVINIT_CLASS_NAME);
register_jni_natives = mono_class_get_method_from_name (runtime, "RegisterJniNatives", 5);
}
diff --git a/src/monodroid/jni/osbridge.cc b/src/monodroid/jni/osbridge.cc
index d05b0a61947..067e9019da4 100644
--- a/src/monodroid/jni/osbridge.cc
+++ b/src/monodroid/jni/osbridge.cc
@@ -1143,10 +1143,20 @@ 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, SharedConstants::MONO_ANDROID_ASSEMBLY_NAME, SharedConstants::ANDROID_RUNTIME_NS_NAME, SharedConstants::JNIENV_CLASS_NAME);;
+ MonoClass *runtime = utils.monodroid_get_class_from_name (
+ domain,
+#if defined (NET)
+ SharedConstants::MONO_ANDROID_RUNTIME_ASSEMBLY_NAME,
+#else
+ SharedConstants::MONO_ANDROID_ASSEMBLY_NAME,
+#endif
+ SharedConstants::ANDROID_RUNTIME_NS_NAME,
+ SharedConstants::ANDROID_RUNTIME_INTERNAL_CLASS_NAME
+ );
+
node->domain = domain;
- node->bridge_processing_field = mono_class_get_field_from_name (jnienv, const_cast ("BridgeProcessing"));
- node->jnienv_vtable = mono_class_vtable (domain, jnienv);
+ node->bridge_processing_field = mono_class_get_field_from_name (runtime, const_cast ("BridgeProcessing"));
+ node->jnienv_vtable = mono_class_vtable (domain, runtime);
node->next = domains_list;
domains_list = node;
diff --git a/src/monodroid/jni/shared-constants.hh b/src/monodroid/jni/shared-constants.hh
index 0f581b1cc04..a88dbcdf569 100644
--- a/src/monodroid/jni/shared-constants.hh
+++ b/src/monodroid/jni/shared-constants.hh
@@ -17,12 +17,17 @@ namespace xamarin::android::internal
class SharedConstants
{
public:
+#if defined (NET)
+ static constexpr char MONO_ANDROID_RUNTIME_ASSEMBLY_NAME[] = "Mono.Android.Runtime";
+#endif
static constexpr char MONO_ANDROID_ASSEMBLY_NAME[] = "Mono.Android";
static constexpr char JAVA_INTEROP_ASSEMBLY_NAME[] = "Java.Interop";
static constexpr char ANDROID_RUNTIME_NS_NAME[] = "Android.Runtime";
+ static constexpr char JNIENVINIT_CLASS_NAME[] = "JNIEnvInit";
static constexpr char JNIENV_CLASS_NAME[] = "JNIEnv";
static constexpr char ANDROID_ENVIRONMENT_CLASS_NAME[] = "AndroidEnvironment";
+ static constexpr char ANDROID_RUNTIME_INTERNAL_CLASS_NAME[] = "AndroidRuntimeInternal";
static constexpr char DLL_EXTENSION[] = ".dll";
diff --git a/src/monodroid/jni/xamarin-android-app-context.cc b/src/monodroid/jni/xamarin-android-app-context.cc
index 58eaa69ab82..e54ae912b06 100644
--- a/src/monodroid/jni/xamarin-android-app-context.cc
+++ b/src/monodroid/jni/xamarin-android-app-context.cc
@@ -1,4 +1,5 @@
#include
+#include
#include "monodroid-glue-internal.hh"
#include "mono-image-loader.hh"
@@ -38,7 +39,6 @@ template
force_inline void
MonodroidRuntime::get_function_pointer (uint32_t mono_image_index, uint32_t class_index, uint32_t method_token, void*& target_ptr) noexcept
{
- log_warn (LOG_DEFAULT, __PRETTY_FUNCTION__);
log_debug (
LOG_ASSEMBLY,
"MM: Trying to look up pointer to method '%s' (token 0x%x) in class '%s' (index %u)",
@@ -55,6 +55,9 @@ MonodroidRuntime::get_function_pointer (uint32_t mono_image_index, uint32_t clas
abort ();
}
+ // We need to do that, as Mono APIs cannot be invoked from threads that aren't attached to the runtime.
+ mono_thread_attach (mono_get_root_domain ());
+
// We don't check for valid return values from image loader, class and method lookup because if any
// of them fails to find the requested entity, they will return `null`. In consequence, we can pass
// these pointers without checking all the way to `mono_method_get_unmanaged_callers_only_ftnptr`, after
@@ -76,7 +79,7 @@ MonodroidRuntime::get_function_pointer (uint32_t mono_image_index, uint32_t clas
target_ptr = ret;
}
- log_debug (LOG_ASSEMBLY, "Loaded pointer to method %s", mono_method_get_name (method));
+ log_debug (LOG_ASSEMBLY, "Loaded pointer to method %s (%p) (mono_image_index == %u; class_index == %u; method_token == 0x%x)", mono_method_full_name (method, true), ret, mono_image_index, class_index, method_token);
return;
}
diff --git a/src/monodroid/jni/xamarin-app-marshaling.cc b/src/monodroid/jni/xamarin-app-marshaling.cc
deleted file mode 100644
index 58378b6c5fe..00000000000
--- a/src/monodroid/jni/xamarin-app-marshaling.cc
+++ /dev/null
@@ -1,78 +0,0 @@
-#include
-#include
-
-#include
-
-#include
-#include
-#include
-
-#include "xamarin-app.hh"
-
-static get_function_pointer_fn get_function_pointer;
-
-void xamarin_app_init (get_function_pointer_fn fn) noexcept
-{
- get_function_pointer = fn;
-}
-
-using android_app_activity_on_create_bundle_fn = void (*) (JNIEnv *env, jclass klass, jobject savedInstanceState);
-static android_app_activity_on_create_bundle_fn android_app_activity_on_create_bundle = nullptr;
-
-extern "C" JNIEXPORT void
-JNICALL Java_helloandroid_MainActivity_n_1onCreate__Landroid_os_Bundle_2 (JNIEnv *env, jclass klass, jobject savedInstanceState) noexcept
-{
- // log_info (LOG_DEFAULT, "%s (%p, %p, %p)", __PRETTY_FUNCTION__, env, klass, savedInstanceState);
-
- if (android_app_activity_on_create_bundle == nullptr) {
- // void *fn = get_function_pointer (
- // 16 /* Mono.Android.dll index */,
- // 0 /* Android.App.Activity index */,
- // 0x0600055B /* n_OnCreate_Landroid_os_Bundle_ */
- // );
-
- // android_app_activity_on_create_bundle = reinterpret_cast(fn);
-
- get_function_pointer (16, 0, 0x0600055B, reinterpret_cast(android_app_activity_on_create_bundle));
- }
-
- android_app_activity_on_create_bundle (env, klass, savedInstanceState);
-}
-
-using android_app_activity_on_create_view_fn = jobject (*) (JNIEnv *env, jclass klass, jobject view, jstring name, jobject context, jobject attrs);
-static android_app_activity_on_create_view_fn android_app_activity_on_create_view = nullptr;
-
-extern "C" JNIEXPORT jobject
-JNICALL Java_helloandroid_MainActivity_n_1onCreateView__Landroid_view_View_2Ljava_lang_String_2Landroid_content_Context_2Landroid_util_AttributeSet_2 (JNIEnv *env, jclass klass, jobject view, jstring name, jobject context, jobject attrs) noexcept
-{
- // log_info (LOG_DEFAULT, "%s (%p, %p, %p, %p, %p, %p)", __PRETTY_FUNCTION__, env, klass, view, name, context, attrs);
-
- if (android_app_activity_on_create_view == nullptr) {
- get_function_pointer (
- 16 /* Mono.Android.dll index */,
- 0 /* Android.App.Activity index */,
- 0x06000564 /* n_OnCreateView_Landroid_view_View_Ljava_lang_String_Landroid_content_Context_Landroid_util_AttributeSet_ */,
- reinterpret_cast(android_app_activity_on_create_view)
- );
- }
-
- return android_app_activity_on_create_view (env, klass, view, name, context, attrs);
-}
-
-using onDoSomething_fn = jbyte (*) (JNIEnv *env, jclass klass, jint anInt, jlong aLong, jfloat aFloat, jboolean aBool, jchar aChar, jshort aShort, jdouble aDouble);
-static onDoSomething_fn onDoSomething = nullptr;
-
-extern "C" JNIEXPORT jbyte
-JNICALL Java_helloandroid_MainActivity_n_1onDoSomething (JNIEnv *env, jclass klass, jint anInt, jlong aLong, jfloat aFloat, jboolean aBool, jchar aChar, jshort aShort, jdouble aDouble) noexcept
-{
- if (onDoSomething == nullptr) {
- get_function_pointer (
- 16 /* Mono.Android.dll index */,
- 0 /* Android.App.Activity index */,
- 0x06000564 /* n_OnCreateView_Landroid_view_View_Ljava_lang_String_Landroid_content_Context_Landroid_util_AttributeSet_ */,
- reinterpret_cast(onDoSomething)
- );
- }
-
- return onDoSomething (env, klass, anInt, aLong, aFloat, aBool, aChar, aShort, aDouble);
-}
diff --git a/src/monodroid/jni/xamarin-app.hh b/src/monodroid/jni/xamarin-app.hh
index 23ef37cba33..0843a17d270 100644
--- a/src/monodroid/jni/xamarin-app.hh
+++ b/src/monodroid/jni/xamarin-app.hh
@@ -4,6 +4,7 @@
#include
+#include
#include
#include "monodroid.h"
@@ -210,6 +211,7 @@ struct ApplicationConfig
bool jni_add_native_method_registration_attribute_present;
bool have_runtime_config_blob;
bool have_assembly_store;
+ bool marshal_methods_enabled;
uint8_t bound_exception_type;
uint32_t package_naming_policy;
uint32_t environment_variable_count;
diff --git a/tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop-Tests.NET.csproj b/tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop-Tests.NET.csproj
index 07e28916acc..f9a8feab9a3 100644
--- a/tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop-Tests.NET.csproj
+++ b/tests/Mono.Android-Tests/Java.Interop-Tests/Java.Interop-Tests.NET.csproj
@@ -24,6 +24,7 @@
+
diff --git a/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/AndroidManifest.xml b/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/AndroidManifest.xml
index aae3d489b4b..4a0f610906d 100644
--- a/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/AndroidManifest.xml
+++ b/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/AndroidManifest.xml
@@ -1,6 +1,6 @@
-
+
diff --git a/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj b/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj
index 4543d58f976..40c61020b59 100644
--- a/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj
+++ b/tests/Mono.Android-Tests/Runtime-Microsoft.Android.Sdk/Mono.Android.NET-Tests.csproj
@@ -5,6 +5,7 @@
$(DotNetAndroidTargetFramework)
+ 21
Xamarin.Android.RuntimeTests
Exe
true
@@ -41,6 +42,7 @@
+
diff --git a/tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/MainActivity.cs b/tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/MainActivity.cs
index 9bfe8087a5d..06bdfccaf49 100644
--- a/tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/MainActivity.cs
+++ b/tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/MainActivity.cs
@@ -7,7 +7,7 @@
namespace Xamarin.Android.RuntimeTests
{
- [Activity (Label = "runtime", MainLauncher = true,
+ [Activity (Label = "Mono.Android Tests", MainLauncher = true,
Name="xamarin.android.runtimetests.MainActivity")]
public partial class MainActivity : TestSuiteActivity
{