diff --git a/.gitmodules b/.gitmodules
index 20bbd195b37..06bad4aef97 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -22,6 +22,9 @@
path = external/xamarin-android-tools
url = https://github.com/xamarin/xamarin-android-tools
branch = main
+[submodule "external/libunwind"]
+ path = external/libunwind
+ url = https://github.com/libunwind/libunwind.git
[submodule "external/xxHash"]
path = external/xxHash
url = https://github.com/Cyan4973/xxHash.git
diff --git a/Configuration.props b/Configuration.props
index 932f8b0a107..68373be4123 100644
--- a/Configuration.props
+++ b/Configuration.props
@@ -104,6 +104,8 @@
True
$(MSBuildThisFileDirectory)external\opentk
$(MSBuildThisFileDirectory)external\sqlite
+ $(MSBuildThisFileDirectory)external\libunwind
+ $(BootstrapOutputDirectory)\libunwind
$(MSBuildThisFileDirectory)
$(MSBuildThisFileDirectory)src-ThirdParty\
armeabi-v7a;x86
@@ -144,6 +146,8 @@
$([System.IO.Path]::GetFullPath ('$(JavaInteropSourceDirectory)'))
$([System.IO.Path]::GetFullPath ('$(MonoSourceDirectory)'))
$([System.IO.Path]::GetFullPath ('$(SqliteSourceDirectory)'))
+ $([System.IO.Path]::GetFullPath ('$(LibUnwindSourceDirectory)'))
+ $([System.IO.Path]::GetFullPath ('$(LibUnwindGeneratedHeadersDirectory)'))
$([System.IO.Path]::GetFullPath ('$(OpenTKSourceDirectory)'))
net8.0
diff --git a/TheoriesToTest.txt b/TheoriesToTest.txt
new file mode 100644
index 00000000000..7517af47f8a
--- /dev/null
+++ b/TheoriesToTest.txt
@@ -0,0 +1,4 @@
+* See if it's a threading issue - add full locking to `get_function_pointer` instead of atomics that we use now
+* See if it's an ALC issue - call `get_function_pointer` every time and check the returned pointer value
+* Implement Jon's idea to always have the "old" dynamic registration stuff in JCW and only use it when some flag
+ is set and marshal methods are active.
diff --git a/Xamarin.Android.sln b/Xamarin.Android.sln
index c85d197a5aa..b46ca2898aa 100644
--- a/Xamarin.Android.sln
+++ b/Xamarin.Android.sln
@@ -47,6 +47,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Java.Interop.Tools.Diagnost
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Java.Interop.Tools.Cecil", "external\Java.Interop\src\Java.Interop.Tools.Cecil\Java.Interop.Tools.Cecil.csproj", "{D48EE8D0-0A0A-4493-AEF5-DAF5F8CF86AD}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "libunwind", "src\libunwind-xamarin\libunwind-xamarin.csproj", "{F8E4961B-C427-47F9-92D6-0BEB5B76B3D7}"
+EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "monodroid", "src\monodroid\monodroid.csproj", "{53EE4C57-1C03-405A-8243-8DA539546C88}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{CAB438D8-B0F5-4AF0-BEBD-9E2ADBD7B483}"
diff --git a/build-tools/cmake/xa_common.cmake b/build-tools/cmake/xa_common.cmake
index d732318eb05..250473e2633 100644
--- a/build-tools/cmake/xa_common.cmake
+++ b/build-tools/cmake/xa_common.cmake
@@ -1 +1,143 @@
-set(CMAKE_OSX_DEPLOYMENT_TARGET 10.12)
+set(CMAKE_CXX_STANDARD 20)
+set(CMAKE_CXX_STANDARD_REQUIRED ON)
+set(CMAKE_CXX_EXTENSIONS OFF)
+
+set(CMAKE_C_STANDARD 11)
+set(CMAKE_C_STANDARD_REQUIRED ON)
+set(CMAKE_C_EXTENSIONS OFF)
+
+include("${CMAKE_ANDROID_NDK}/build/cmake/abis.cmake")
+
+if(CMAKE_BUILD_TYPE STREQUAL Debug)
+ set(DEBUG_BUILD True)
+else()
+ set(DEBUG_BUILD False)
+endif()
+
+set(XA_NO_INLINE "$ENV{XA_NO_INLINE}")
+if(XA_NO_INLINE)
+ set(DONT_INLINE_DEFAULT ON)
+else()
+ set(DONT_INLINE_DEFAULT OFF)
+endif()
+
+set(XA_NO_STRIP "$ENV{XA_NO_STRIP}")
+if(XA_NO_STRIP OR DEBUG_BUILD)
+ set(STRIP_DEBUG_DEFAULT OFF)
+endif()
+
+option(ENABLE_CLANG_ASAN "Enable the clang AddressSanitizer support" OFF)
+option(ENABLE_CLANG_UBSAN "Enable the clang UndefinedBehaviorSanitizer support" OFF)
+
+if(ENABLE_CLANG_ASAN OR ENABLE_CLANG_UBSAN)
+ set(STRIP_DEBUG_DEFAULT OFF)
+ set(ANALYZERS_ENABLED ON)
+else()
+ if(NOT XA_NO_STRIP)
+ set(STRIP_DEBUG_DEFAULT ON)
+ endif()
+ set(ANALYZERS_ENABLED OFF)
+endif()
+
+option(COMPILER_DIAG_COLOR "Show compiler diagnostics/errors in color" ON)
+option(STRIP_DEBUG "Strip debugging information when linking" ${STRIP_DEBUG_DEFAULT})
+option(DISABLE_DEBUG "Disable the built-in debugging code" OFF)
+option(USE_CCACHE "Use ccache, if found, to speed up recompilation" ON)
+option(DONT_INLINE "Do not inline any functions which are usually inlined, to get better stack traces" ${DONT_INLINE_DEFAULT})
+
+if(USE_CCACHE)
+ if(CMAKE_CXX_COMPILER MATCHES "/ccache/")
+ message(STATUS "ccache: compiler already uses ccache")
+ else()
+ find_program(CCACHE ccache)
+ if(CCACHE)
+ set(CMAKE_CXX_COMPILER_LAUNCHER "${CCACHE}")
+ set(CMAKE_C_COMPILER_LAUNCHER "${CCACHE}")
+ message(STATUS "ccache: compiler will be lauched with ${CCACHE}")
+ endif()
+ endif()
+endif()
+
+if(ANDROID_STL STREQUAL none)
+ set(USES_LIBSTDCPP False)
+else()
+ set(USES_LIBSTDCPP True)
+endif()
+
+#
+# General config
+#
+if(CMAKE_HOST_SYSTEM_NAME STREQUAL Linux)
+ set(IS_LINUX True)
+else()
+ set(IS_LINUX False)
+endif()
+
+if(CMAKE_HOST_SYSTEM_NAME STREQUAL Darwin)
+ set(IS_MACOS True)
+else()
+ set(IS_MACOS False)
+endif()
+
+set(XA_BUILD_DIR "${CMAKE_CURRENT_SOURCE_DIR}/../../bin/Build${XA_BUILD_CONFIGURATION}")
+include("${XA_BUILD_DIR}/xa_build_configuration.cmake")
+
+#
+# Paths
+#
+if(ANDROID_ABI MATCHES "^arm64-v8a")
+ set(NET_RUNTIME_DIR "${NETCORE_APP_RUNTIME_DIR_ARM64}")
+ set(TOOLCHAIN_TRIPLE "${NDK_ABI_arm64-v8a_TRIPLE}")
+elseif(ANDROID_ABI MATCHES "^armeabi-v7a")
+ set(NET_RUNTIME_DIR "${NETCORE_APP_RUNTIME_DIR_ARM}")
+ set(TOOLCHAIN_TRIPLE "${NDK_ABI_armeabi-v7a_TRIPLE}")
+elseif(ANDROID_ABI MATCHES "^x86_64")
+ set(NET_RUNTIME_DIR "${NETCORE_APP_RUNTIME_DIR_X86_64}")
+ set(TOOLCHAIN_TRIPLE "${NDK_ABI_x86_64_TRIPLE}")
+elseif(ANDROID_ABI MATCHES "^x86")
+ set(NET_RUNTIME_DIR "${NETCORE_APP_RUNTIME_DIR_X86}")
+ set(TOOLCHAIN_TRIPLE "${NDK_ABI_x86_TRIPLE}")
+else()
+ message(FATAL "${ANDROID_ABI} is not supported for .NET 6+ builds")
+endif()
+
+file(REAL_PATH "../../" REPO_ROOT_DIR)
+set(EXTERNAL_DIR "${REPO_ROOT_DIR}/external")
+set(JAVA_INTEROP_SRC_PATH "${EXTERNAL_DIR}/Java.Interop/src/java-interop")
+set(SHARED_SOURCES_DIR "${REPO_ROOT_DIR}/src/native/shared")
+set(TRACING_SOURCES_DIR "${REPO_ROOT_DIR}/src/native/tracing")
+#
+# Include directories
+#
+include_directories(SYSTEM ${CMAKE_SYSROOT}/usr/include/c++/v1/)
+include_directories(SYSTEM "${NET_RUNTIME_DIR}/native/include/mono-2.0")
+include_directories("${JAVA_INTEROP_SRC_PATH}")
+include_directories("${SHARED_SOURCES_DIR}")
+include_directories("${TRACING_SOURCES_DIR}")
+
+#
+# Compiler defines
+#
+add_compile_definitions(XA_VERSION="${XA_VERSION}")
+add_compile_definitions(_REENTRANT)
+add_compile_definitions(PLATFORM_ANDROID)
+
+if(DEBUG_BUILD AND NOT DISABLE_DEBUG)
+ add_compile_definitions(DEBUG)
+endif()
+
+if(ANDROID_ABI MATCHES "^(arm64-v8a|x86_64)")
+ add_compile_definitions(ANDROID64)
+endif()
+
+if (ANDROID_NDK_MAJOR LESS 20)
+ add_compile_definitions(__ANDROID_API_Q__=29)
+endif()
+
+#
+# Shared sources
+#
+set(XA_SHARED_SOURCES
+ ${SHARED_SOURCES_DIR}/helpers.cc
+ ${SHARED_SOURCES_DIR}/new_delete.cc
+)
diff --git a/build-tools/cmake/xa_macros.cmake b/build-tools/cmake/xa_macros.cmake
index df23bdedcd0..69759acf363 100644
--- a/build-tools/cmake/xa_macros.cmake
+++ b/build-tools/cmake/xa_macros.cmake
@@ -200,7 +200,8 @@ function(xa_common_prepare)
-fstack-protector-strong
-fstrict-return
-fno-strict-aliasing
- -ffunction-sections
+ -fno-function-sections
+ -fno-data-sections
-funswitch-loops
-Wa,-noexecstack
-fPIC
diff --git a/build-tools/cmake/xa_preamble.cmake b/build-tools/cmake/xa_preamble.cmake
new file mode 100644
index 00000000000..d28fc5e10c4
--- /dev/null
+++ b/build-tools/cmake/xa_preamble.cmake
@@ -0,0 +1,7 @@
+set(CMAKE_OSX_DEPLOYMENT_TARGET 10.12)
+
+#
+# Read product version
+#
+file(STRINGS "../../Directory.Build.props" XA_PRODUCT_VERSION_XML REGEX "^[ \t]*(.*)")
+string(REGEX REPLACE "^[ \t]*(.*)" "\\1" XA_VERSION "${XA_PRODUCT_VERSION_XML}")
diff --git a/build-tools/create-packs/Microsoft.Android.Runtime.proj b/build-tools/create-packs/Microsoft.Android.Runtime.proj
index 8982eb6f911..41fbd68991d 100644
--- a/build-tools/create-packs/Microsoft.Android.Runtime.proj
+++ b/build-tools/create-packs/Microsoft.Android.Runtime.proj
@@ -41,6 +41,8 @@ projects that use the Microsoft.Android framework in .NET 6+.
<_AndroidRuntimePackAssets Include="$(MicrosoftAndroidSdkOutDir)lib\$(AndroidRID)\libmono-android.debug.so" />
<_AndroidRuntimePackAssets Include="$(MicrosoftAndroidSdkOutDir)lib\$(AndroidRID)\libmono-android.release.so" />
<_AndroidRuntimePackAssets Include="$(MicrosoftAndroidSdkOutDir)lib\$(AndroidRID)\libxamarin-debug-app-helper.so" />
+ <_AndroidRuntimePackAssets Include="$(MicrosoftAndroidSdkOutDir)lib\$(AndroidRID)\libmarshal-methods-tracing.a" />
+ <_AndroidRuntimePackAssets Include="$(MicrosoftAndroidSdkOutDir)lib\$(AndroidRID)\libxamarin-native-tracing.so" />
diff --git a/build-tools/installers/create-installers.targets b/build-tools/installers/create-installers.targets
index daa577014a5..34551f5c26f 100644
--- a/build-tools/installers/create-installers.targets
+++ b/build-tools/installers/create-installers.targets
@@ -172,12 +172,20 @@
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)ManifestOverlays\Timing.xml" />
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-arm64\libc.so" />
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-arm64\libm.so" />
+ <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-arm64\libdl.so" />
+ <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-arm64\liblog.so" />
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-arm\libc.so" />
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-arm\libm.so" />
+ <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-arm\libdl.so" />
+ <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-arm\liblog.so" />
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-x64\libc.so" />
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-x64\libm.so" />
+ <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-x64\libdl.so" />
+ <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-x64\liblog.so" />
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-x86\libc.so" />
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-x86\libm.so" />
+ <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-x86\libdl.so" />
+ <_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)libstubs\android-x86\liblog.so" />
<_MSBuildTargetsSrcFiles Include="$(MSBuildTargetsSrcDir)\Xamarin.Android.AvailableItems.targets" />
diff --git a/build-tools/scripts/cmake-android.props b/build-tools/scripts/cmake-android.props
new file mode 100644
index 00000000000..6335db11af8
--- /dev/null
+++ b/build-tools/scripts/cmake-android.props
@@ -0,0 +1,6 @@
+
+
+
+ <_CmakeAndroidFlags>--debug-output -GNinja -DCMAKE_MAKE_PROGRAM="$(NinjaPath)" -DXA_BUILD_CONFIGURATION=$(Configuration) -DXA_LIB_TOP_DIR=$(MicrosoftAndroidSdkOutDir) -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DMONO_PATH="$(MonoSourceFullPath)" -DANDROID_STL="none" -DANDROID_CPP_FEATURES="no-rtti no-exceptions" -DANDROID_TOOLCHAIN=clang -DCMAKE_TOOLCHAIN_FILE="$(AndroidNdkDirectory)/build/cmake/android.toolchain.cmake" -DANDROID_NDK=$(AndroidNdkDirectory)
+
+
diff --git a/build-tools/xaprepare/xaprepare/Application/KnownProperties.cs b/build-tools/xaprepare/xaprepare/Application/KnownProperties.cs
index 2d9fd7efcfa..32f56aa57ff 100644
--- a/build-tools/xaprepare/xaprepare/Application/KnownProperties.cs
+++ b/build-tools/xaprepare/xaprepare/Application/KnownProperties.cs
@@ -38,6 +38,8 @@ static class KnownProperties
public const string JavaInteropFullPath = "JavaInteropFullPath";
public const string JavaSdkDirectory = "JavaSdkDirectory";
public const string JdkIncludePath = "JdkIncludePath";
+ public const string LibUnwindGeneratedHeadersFullPath = nameof (LibUnwindGeneratedHeadersFullPath);
+ public const string LibUnwindSourceFullPath = nameof (LibUnwindSourceFullPath);
public const string LibZipSourceFullPath = "LibZipSourceFullPath";
public const string ManagedRuntime = "ManagedRuntime";
public const string MicrosoftAndroidSdkOutDir = "MicrosoftAndroidSdkOutDir";
diff --git a/build-tools/xaprepare/xaprepare/Application/Properties.Defaults.cs.in b/build-tools/xaprepare/xaprepare/Application/Properties.Defaults.cs.in
index 6e59af3ab6e..39b44422eb7 100644
--- a/build-tools/xaprepare/xaprepare/Application/Properties.Defaults.cs.in
+++ b/build-tools/xaprepare/xaprepare/Application/Properties.Defaults.cs.in
@@ -42,6 +42,8 @@ namespace Xamarin.Android.Prepare
properties.Add (KnownProperties.JavaInteropFullPath, StripQuotes (@"@JavaInteropFullPath@"));
properties.Add (KnownProperties.JavaSdkDirectory, StripQuotes (@"@JavaSdkDirectory@"));
properties.Add (KnownProperties.JdkIncludePath, StripQuotes (@"@JdkIncludePath@"));
+ properties.Add (KnownProperties.LibUnwindGeneratedHeadersFullPath, StripQuotes (@"@LibUnwindGeneratedHeadersFullPath@"));
+ properties.Add (KnownProperties.LibUnwindSourceFullPath, StripQuotes (@"@LibUnwindSourceFullPath"));
properties.Add (KnownProperties.LibZipSourceFullPath, StripQuotes (@"@LibZipSourceFullPath@"));
properties.Add (KnownProperties.ManagedRuntime, StripQuotes (@"@ManagedRuntime@"));
properties.Add (KnownProperties.MicrosoftAndroidSdkOutDir, StripQuotes (@"@MicrosoftAndroidSdkOutDir@"));
diff --git a/build-tools/xaprepare/xaprepare/xaprepare.targets b/build-tools/xaprepare/xaprepare/xaprepare.targets
index 4c8e3be8906..dc8465d148b 100644
--- a/build-tools/xaprepare/xaprepare/xaprepare.targets
+++ b/build-tools/xaprepare/xaprepare/xaprepare.targets
@@ -75,6 +75,7 @@
+
diff --git a/external/libunwind b/external/libunwind
new file mode 160000
index 00000000000..9cc4d98b22a
--- /dev/null
+++ b/external/libunwind
@@ -0,0 +1 @@
+Subproject commit 9cc4d98b22ae57bc1d8c253988feb85d4298a634
diff --git a/java.interop-tracing.diff b/java.interop-tracing.diff
new file mode 100644
index 00000000000..bb00e359fa4
--- /dev/null
+++ b/java.interop-tracing.diff
@@ -0,0 +1,213 @@
+diff --git a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.cs b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.cs
+index 76f2bfc0..7a57f6b2 100644
+--- a/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.cs
++++ b/src/Java.Interop.Tools.JavaCallableWrappers/Java.Interop.Tools.JavaCallableWrappers/JavaCallableWrapperGenerator.cs
+@@ -133,6 +133,7 @@ namespace Java.Interop.Tools.JavaCallableWrappers {
+
+ JavaCallableWrapperGenerator (TypeDefinition type, string? outerType, Action log, IMetadataResolver resolver, JavaCallableMethodClassifier? methodClassifier = null)
+ {
++ Console.WriteLine ($"JCWG: processing {type}");
+ this.methodClassifier = methodClassifier;
+ this.type = type;
+ this.log = log;
+@@ -224,6 +225,8 @@ namespace Java.Interop.Tools.JavaCallableWrappers {
+ curCtors = new List ();
+ AddConstructors (ctorTypes [i], outerType, baseCtors, curCtors, false);
+ }
++
++ Console.WriteLine ($"JCWG: done processing {type}");
+ }
+
+ static void ExtractJavaNames (string jniName, out string package, out string type)
+@@ -285,8 +288,10 @@ namespace Java.Interop.Tools.JavaCallableWrappers {
+
+ void AddConstructor (MethodDefinition ctor, TypeDefinition type, string? outerType, List? baseCtors, List curCtors, bool onlyRegisteredOrExportedCtors, bool skipParameterCheck)
+ {
++ Console.WriteLine ($" JCWG: AddConstructor ({ctor}, {type}, ..., onlyRegisteredOrExportedCtors: {onlyRegisteredOrExportedCtors}, skipParameterCheck: {skipParameterCheck});");
+ string managedParameters = GetManagedParameters (ctor, outerType);
+ if (!skipParameterCheck && (managedParameters == null || ctors.Any (c => c.ManagedParameters == managedParameters))) {
++ Console.WriteLine (" JCWG: skip #1");
+ return;
+ }
+
+@@ -297,28 +302,38 @@ namespace Java.Interop.Tools.JavaCallableWrappers {
+ }
+ ctors.Add (new Signature (ctor, eattr, cache));
+ curCtors.Add (ctor);
++ Console.WriteLine (" JCWG: add #1");
+ return;
+ }
+
+ RegisterAttribute rattr = GetMethodRegistrationAttributes (ctor).FirstOrDefault ();
+ if (rattr != null) {
+- if (ctors.Any (c => c.JniSignature == rattr.Signature))
++ if (ctors.Any (c => c.JniSignature == rattr.Signature)) {
++ Console.WriteLine (" JCWG: skip #2");
+ return;
++ }
+ ctors.Add (new Signature (ctor, rattr, managedParameters, outerType, cache));
+ curCtors.Add (ctor);
++ Console.WriteLine (" JCWG: add #2");
+ return;
+ }
+
+- if (onlyRegisteredOrExportedCtors)
++ if (onlyRegisteredOrExportedCtors) {
++ Console.WriteLine (" JCWG: skip #1");
+ return;
++ }
+
+ string? jniSignature = GetJniSignature (ctor, cache);
+
+- if (jniSignature == null)
++ if (jniSignature == null) {
++ Console.WriteLine (" JCWG: skip #3");
+ return;
++ }
+
+- if (ctors.Any (c => c.JniSignature == jniSignature))
++ if (ctors.Any (c => c.JniSignature == jniSignature)) {
++ Console.WriteLine (" JCWG: skip #4");
+ return;
++ }
+
+ if (baseCtors == null) {
+ throw new InvalidOperationException ("`baseCtors` should not be null!");
+@@ -327,11 +342,13 @@ namespace Java.Interop.Tools.JavaCallableWrappers {
+ if (baseCtors.Any (m => m.Parameters.AreParametersCompatibleWith (ctor.Parameters, cache))) {
+ ctors.Add (new Signature (".ctor", jniSignature, "", managedParameters, outerType, null));
+ curCtors.Add (ctor);
++ Console.WriteLine (" JCWG: add #3");
+ return;
+ }
+ if (baseCtors.Any (m => !m.HasParameters)) {
+ ctors.Add (new Signature (".ctor", jniSignature, "", managedParameters, outerType, ""));
+ curCtors.Add (ctor);
++ Console.WriteLine (" JCWG: add #4");
+ return;
+ }
+ }
+@@ -549,17 +566,17 @@ namespace Java.Interop.Tools.JavaCallableWrappers {
+ GenerateHeader (writer);
+
+ bool needCtor = false;
+- if (HasDynamicallyRegisteredMethods) {
++// if (HasDynamicallyRegisteredMethods) {
+ needCtor = true;
+ writer.WriteLine ("/** @hide */");
+ writer.WriteLine ("\tpublic static final String __md_methods;");
+- }
++// }
+
+ if (children != null) {
+ for (int i = 0; i < children.Count; i++) {
+- if (!children[i].HasDynamicallyRegisteredMethods) {
+- continue;
+- }
++ // if (!children[i].HasDynamicallyRegisteredMethods) {
++ // continue;
++ // }
+ needCtor = true;
+ writer.Write ("\tstatic final String __md_");
+ writer.Write (i + 1);
+@@ -570,9 +587,9 @@ namespace Java.Interop.Tools.JavaCallableWrappers {
+ if (needCtor) {
+ writer.WriteLine ("\tstatic {");
+
+- if (HasDynamicallyRegisteredMethods) {
++// if (HasDynamicallyRegisteredMethods) {
+ GenerateRegisterType (writer, this, "__md_methods");
+- }
++// }
+
+ if (children != null) {
+ for (int i = 0; i < children.Count; ++i) {
+@@ -687,9 +704,12 @@ namespace Java.Interop.Tools.JavaCallableWrappers {
+
+ void GenerateBody (TextWriter sw)
+ {
++ Console.WriteLine ($" JCWG: GenerateBody for {name} (ctors size: {ctors.Count})");
+ foreach (Signature ctor in ctors) {
+- if (string.IsNullOrEmpty (ctor.Params) && JavaNativeTypeManager.IsApplication (type, cache))
++ if (string.IsNullOrEmpty (ctor.Params) && JavaNativeTypeManager.IsApplication (type, cache)) {
++ Console.WriteLine ($" JCWG: skipping constructor generation; params empty? {string.IsNullOrEmpty (ctor.Params)}; is application? {JavaNativeTypeManager.IsApplication (type, cache)}");
+ continue;
++ }
+ GenerateConstructor (ctor, sw);
+ }
+
+@@ -745,9 +765,9 @@ namespace Java.Interop.Tools.JavaCallableWrappers {
+
+ void GenerateRegisterType (TextWriter sw, JavaCallableWrapperGenerator self, string field)
+ {
+- if (!self.HasDynamicallyRegisteredMethods) {
+- return;
+- }
++ // if (!self.HasDynamicallyRegisteredMethods) {
++ // return;
++ // }
+
+ sw.Write ("\t\t");
+ sw.Write (field);
+@@ -818,9 +838,13 @@ namespace Java.Interop.Tools.JavaCallableWrappers {
+ return jniName.Replace ('/', '.').Replace ('$', '.');
+ }
+
+- bool CannotRegisterInStaticConstructor (TypeDefinition type)
++ bool CannotRegisterInStaticConstructor (TypeDefinition type, bool dolog = false)
+ {
+- return JavaNativeTypeManager.IsApplication (type, cache) || JavaNativeTypeManager.IsInstrumentation (type, cache);
++ Console.WriteLine ($" JCWG: CannotRegisterInStaticConstructor ({type})");
++ bool ret = JavaNativeTypeManager.IsApplication (type, cache) || JavaNativeTypeManager.IsInstrumentation (type, cache);
++ Console.WriteLine ($" JCWG: CannotRegisterInStaticConstructor for type {type}; ret == {ret}; IsApplication: {JavaNativeTypeManager.IsApplication (type, cache)}; IsInstrumentation: {JavaNativeTypeManager.IsInstrumentation (type, cache)}");
++
++ return ret;
+ }
+
+ class Signature {
+@@ -941,6 +965,8 @@ namespace Java.Interop.Tools.JavaCallableWrappers {
+
+ void GenerateConstructor (Signature ctor, TextWriter sw)
+ {
++ Console.WriteLine ($" JCWG: GenerateConstructor for {name}");
++
+ // TODO: we only generate constructors so that Android types w/ no
+ // default constructor can be subclasses by our generated code.
+ //
+diff --git a/tools/java-source-utils/.classpath b/tools/java-source-utils/.classpath
+index 30230c1c..cbded110 100644
+--- a/tools/java-source-utils/.classpath
++++ b/tools/java-source-utils/.classpath
+@@ -20,6 +20,12 @@
+
+
+
++
++
++
++
++
++
+
+
+
+diff --git a/tools/java-source-utils/.project b/tools/java-source-utils/.project
+index 8337a1c5..613733c8 100644
+--- a/tools/java-source-utils/.project
++++ b/tools/java-source-utils/.project
+@@ -20,4 +20,15 @@
+ org.eclipse.jdt.core.javanature
+ org.eclipse.buildship.core.gradleprojectnature
+
++
++
++ 1682588570543
++
++ 30
++
++ org.eclipse.core.resources.regexFilterMatcher
++ node_modules|.metadata|archetype-resources|META-INF/maven|__CREATED_BY_JAVA_LANGUAGE_SERVER__
++
++
++
+
diff --git a/src/Mono.Android/Android.Runtime/AndroidRuntime.cs b/src/Mono.Android/Android.Runtime/AndroidRuntime.cs
index d7876273983..60b7b52eae0 100644
--- a/src/Mono.Android/Android.Runtime/AndroidRuntime.cs
+++ b/src/Mono.Android/Android.Runtime/AndroidRuntime.cs
@@ -491,8 +491,11 @@ public void RegisterNativeMembers (
{
try {
if (methods.IsEmpty) {
- if (jniAddNativeMethodRegistrationAttributePresent)
+ RuntimeNativeMethods.monodroid_log (LogLevel.Info, LogCategories.Assembly, $"RegisterJniNatives: Type {type.FullName} registers no methods");
+ if (jniAddNativeMethodRegistrationAttributePresent) {
+ RuntimeNativeMethods.monodroid_log (LogLevel.Info, LogCategories.Assembly, $"RegisterJniNatives: Type {type.FullName} has [AddNativeMethodRegistration] attribute");
base.RegisterNativeMembers (nativeClass, type, methods.ToString ());
+ }
return;
} else if (FastRegisterNativeMembers (nativeClass, type, methods)) {
return;
@@ -539,6 +542,7 @@ public void RegisterNativeMembers (
throw new InvalidOperationException (FormattableString.Invariant ($"Specified managed method '{mname.ToString ()}' was not found. Signature: {signature.ToString ()}"));
callback = CreateDynamicCallback (minfo);
needToRegisterNatives = true;
+ RuntimeNativeMethods.monodroid_log (LogLevel.Info, LogCategories.Assembly, $"RegisterJniNativesMethod: [ ] {type.FullName}: __export__ found for method '{name.ToString ()}'");
} else {
Type callbackDeclaringType = type;
if (!callbackDeclaringTypeString.IsEmpty) {
@@ -556,6 +560,7 @@ public void RegisterNativeMembers (
if (callback != null) {
needToRegisterNatives = true;
natives [nativesIndex++] = new JniNativeMethodRegistration (name.ToString (), signature.ToString (), callback);
+ RuntimeNativeMethods.monodroid_log (LogLevel.Info, LogCategories.Assembly, $"RegisterJniNativesMethod: [ ] {type.FullName}.{name.ToString ()}");
}
}
@@ -563,20 +568,12 @@ public void RegisterNativeMembers (
}
if (needToRegisterNatives) {
+ RuntimeNativeMethods.monodroid_log (LogLevel.Info, LogCategories.Assembly, $"RegisterJniNatives: Type {type.FullName} calling JniEnvironment.Types.RegisterNatives");
JniEnvironment.Types.RegisterNatives (nativeClass.PeerReference, natives, nativesIndex);
}
} catch (Exception e) {
JniEnvironment.Runtime.RaisePendingException (e);
}
-
- bool ShouldRegisterDynamically (string callbackTypeName, string callbackString, string typeName, string callbackName)
- {
- if (String.Compare (typeName, callbackTypeName, StringComparison.Ordinal) != 0) {
- return false;
- }
-
- return String.Compare (callbackName, callbackString, StringComparison.Ordinal) == 0;
- }
}
static int CountMethods (ReadOnlySpan methodsSpan)
diff --git a/src/Mono.Android/Android.Runtime/InputStreamAdapter.cs b/src/Mono.Android/Android.Runtime/InputStreamAdapter.cs
index 5eb3b04bd87..795ed57d217 100644
--- a/src/Mono.Android/Android.Runtime/InputStreamAdapter.cs
+++ b/src/Mono.Android/Android.Runtime/InputStreamAdapter.cs
@@ -8,11 +8,18 @@ public sealed class InputStreamAdapter : Java.IO.InputStream {
public Stream BaseStream {get; private set;}
+ InputStreamAdapter () {
+ Console.WriteLine ("InputStreamAdapter () invoked");
+ Console.WriteLine (new System.Diagnostics.StackTrace(true));
+ }
+
public InputStreamAdapter (System.IO.Stream stream)
: base (
JNIEnv.StartCreateInstance ("mono/android/runtime/InputStreamAdapter", "()V"),
JniHandleOwnership.TransferLocalRef)
{
+ Console.WriteLine ("InputStreamAdapter (System.IO.Stream) invoked");
+ Console.WriteLine (new System.Diagnostics.StackTrace(true));
JNIEnv.FinishCreateInstance (Handle, "()V");
this.BaseStream = stream;
@@ -54,4 +61,3 @@ public static IntPtr ToLocalJniHandle (Stream? value)
}
}
}
-
diff --git a/src/Mono.Android/Android.Runtime/JNIEnvInit.cs b/src/Mono.Android/Android.Runtime/JNIEnvInit.cs
index 2f65464716b..22ba4dc13f3 100644
--- a/src/Mono.Android/Android.Runtime/JNIEnvInit.cs
+++ b/src/Mono.Android/Android.Runtime/JNIEnvInit.cs
@@ -76,6 +76,9 @@ static unsafe void RegisterJniNatives (IntPtr typeName_ptr, int typeName_len, In
[UnmanagedCallersOnly]
internal static unsafe void Initialize (JnienvInitializeArgs* args)
{
+#if NETCOREAPP
+ RuntimeNativeMethods.monodroid_log_traces (TraceKind.All, "In JNIEnvInit.Initialize");
+#endif
IntPtr total_timing_sequence = IntPtr.Zero;
IntPtr partial_timing_sequence = IntPtr.Zero;
diff --git a/src/Mono.Android/Android.Runtime/RuntimeNativeMethods.cs b/src/Mono.Android/Android.Runtime/RuntimeNativeMethods.cs
index a0c936c59ff..da635032800 100644
--- a/src/Mono.Android/Android.Runtime/RuntimeNativeMethods.cs
+++ b/src/Mono.Android/Android.Runtime/RuntimeNativeMethods.cs
@@ -6,8 +6,23 @@
namespace Android.Runtime
{
+ // Values must be identical to those in src/monodroid/jni/monodroid-glue-internal.hh
+ [Flags]
+ enum TraceKind : uint
+ {
+ Java = 0x01,
+ Managed = 0x02,
+ Native = 0x04,
+ Signals = 0x08,
+
+ All = Java | Managed | Native | Signals,
+ }
+
internal static class RuntimeNativeMethods
{
+ [DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
+ internal extern static void monodroid_log_traces (TraceKind kind, string first_line);
+
[DllImport (RuntimeConstants.InternalDllName, CallingConvention = CallingConvention.Cdecl)]
internal extern static void monodroid_log (LogLevel level, LogCategories category, string message);
diff --git a/src/Mono.Android/Java.Interop/TypeManager.cs b/src/Mono.Android/Java.Interop/TypeManager.cs
index fb4e87216a4..924a1d02ef3 100644
--- a/src/Mono.Android/Java.Interop/TypeManager.cs
+++ b/src/Mono.Android/Java.Interop/TypeManager.cs
@@ -224,8 +224,10 @@ static Exception CreateJavaLocationException ()
if (!JNIEnvInit.IsRunningOnDesktop) {
// Miss message is logged in the native runtime
- if (JNIEnvInit.LogAssemblyCategory)
+ if (JNIEnvInit.LogAssemblyCategory) {
JNIEnv.LogTypemapTrace (new System.Diagnostics.StackTrace (true));
+ RuntimeNativeMethods.monodroid_log (LogLevel.Warn, LogCategories.Assembly, CreateJavaLocationException ().ToString ());
+ }
return null;
}
diff --git a/src/Mono.Android/Java.Lang/Thread.cs b/src/Mono.Android/Java.Lang/Thread.cs
index 8c6155f41d3..a17aeebc16f 100644
--- a/src/Mono.Android/Java.Lang/Thread.cs
+++ b/src/Mono.Android/Java.Lang/Thread.cs
@@ -41,7 +41,15 @@ public void Run ()
Dispose ();
}
- static Dictionary instances = new Dictionary ();
+ static Dictionary instances;// = new Dictionary ();
+
+ static RunnableImplementor ()
+ {
+ Console.WriteLine ("RunnableImplementor.cctor (): begin");
+ Console.WriteLine (new System.Diagnostics.StackTrace(true));
+ instances = new ();
+ Console.WriteLine ("RunnableImplementor.cctor (): end");
+ }
public static RunnableImplementor Remove (Action handler)
{
@@ -65,4 +73,3 @@ public Thread (Action runHandler, string threadName) : this (new RunnableImpleme
public Thread (ThreadGroup group, Action runHandler, string threadName, long stackSize) : this (group, new RunnableImplementor (runHandler), threadName, stackSize) {}
}
}
-
diff --git a/src/Mono.Android/java/mono/android/TypeManager.java b/src/Mono.Android/java/mono/android/TypeManager.java
index bf64780bf9e..ab3c1beeef9 100644
--- a/src/Mono.Android/java/mono/android/TypeManager.java
+++ b/src/Mono.Android/java/mono/android/TypeManager.java
@@ -1,20 +1,26 @@
package mono.android;
+//#NOTE: make sure the `#FEATURE=*:START` and `#FEATURE=*:END` lines for different values of FEATURE don't overlap or interlace.
+//#NOTE: Each FEATURE block should be self-contained, with no other FEATURE blocks nested.
+//#NOTE: This is because the code that skips over those FEATURE blocks is VERY primitive (see the `CreateTypeManagerJava` task in XABT)
public class TypeManager {
public static void Activate (String typeName, String sig, Object instance, Object[] parameterList)
{
+//#FEATURE=CALL_TRACING:START - do not remove or modify this line, it is required during application build
+ android.util.Log.i ("monodroid-trace", String.format ("java: activating type: '%s' [%s]", typeName, sig));
+//#FEATURE=CALL_TRACING:END - do not remove or modify this line, it is required during application build
n_activate (typeName, sig, instance, parameterList);
}
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 =
+ String methods =
+//#FEATURE=MARSHAL_METHODS:START - do not remove or modify this line, it is required during application build
"n_activate:(Ljava/lang/String;Ljava/lang/String;Ljava/lang/Object;[Ljava/lang/Object;)V:GetActivateHandler\n" +
+//#FEATURE=MARSHAL_METHODS:END - do not remove or modify this line, it is required during application build
"";
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/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 05c6c56639f..bebbef71778 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
@@ -230,7 +230,8 @@ _ResolveAssemblies MSBuild target.
InputLibraries="@(_ResolvedNativeLibraries)"
ExcludedLibraries="@(_MonoExcludedLibraries)"
Components="@(_MonoComponent->Distinct())"
- IncludeDebugSymbols="$(AndroidIncludeDebugSymbols)">
+ IncludeDebugSymbols="$(AndroidIncludeDebugSymbols)"
+ IncludeNativeTracing="$(_UseNativeStackTraces)">
diff --git a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets
index 5d4506284cd..4023ad15312 100644
--- a/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets
+++ b/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.DefaultProperties.targets
@@ -45,6 +45,18 @@
false
+
+
+
+ <_AndroidMarshalMethodsTracingMode Condition=" '$(_AndroidMarshalMethodsTracingMode)' == '' ">none
+ <_AndroidUseNativeStackTraces Condition=" '$(_AndroidUseNativeStackTraces)' == '' And '$(_AndroidMarshalMethodsTracingMode)' == 'none' ">false
+ <_AndroidUseNativeStackTraces Condition=" '$(_AndroidUseNativeStackTraces)' == '' And '$(_AndroidMarshalMethodsTracingMode)' != 'none' ">true
+ <_UseNativeStackTraces Condition=" '$(_AndroidUseNativeStackTraces)' == 'true' Or '$(_AndroidMarshalMethodsTracingMode)' != 'none' ">true
+
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/CreateTypeManagerJava.cs b/src/Xamarin.Android.Build.Tasks/Tasks/CreateTypeManagerJava.cs
index 37872f31927..87309b463d9 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/CreateTypeManagerJava.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/CreateTypeManagerJava.cs
@@ -15,6 +15,9 @@ public class CreateTypeManagerJava : AndroidTask
[Required]
public string ResourceName { get; set; }
+ public bool CallTracingEnabled { get; set; }
+ public bool MarshalMethodsEnabled { get; set; }
+
[Required]
public string OutputFilePath { get; set; }
@@ -31,12 +34,18 @@ public override bool RunTask ()
var result = new StringBuilder ();
bool ignoring = false;
foreach (string line in content.Split ('\n')) {
+ if (SkipNoteLine (line)) {
+ continue;
+ }
+
if (!ignoring) {
- if (ignoring = line.StartsWith ("//#MARSHAL_METHODS:START", StringComparison.Ordinal)) {
+ ignoring = StartIgnoring (line, out bool skipLine);
+ if (ignoring || skipLine) {
continue;
}
+
result.AppendLine (line);
- } else if (line.StartsWith ("//#MARSHAL_METHODS:END", StringComparison.Ordinal)) {
+ } else if (EndIgnoring (line)) {
ignoring = false;
}
}
@@ -62,6 +71,38 @@ public override bool RunTask ()
return !Log.HasLoggedErrors;
}
+ bool SkipNoteLine (string l) => l.Trim ().StartsWith ("//#NOTE:");
+
+ bool StartIgnoring (string l, out bool skipLine)
+ {
+ string line = l.Trim ();
+ skipLine = true;
+ if (MarshalMethodsEnabled && line.StartsWith ("//#FEATURE=MARSHAL_METHODS:START", StringComparison.Ordinal)) {
+ return true;
+ }
+
+ if (!CallTracingEnabled && line.StartsWith ("//#FEATURE=CALL_TRACING:START", StringComparison.Ordinal)) {
+ return true;
+ }
+
+ skipLine = line.StartsWith ("//#FEATURE", StringComparison.Ordinal);
+ return false;
+ }
+
+ bool EndIgnoring (string l)
+ {
+ string line = l.Trim ();
+ if (MarshalMethodsEnabled && line.StartsWith ("//#FEATURE=MARSHAL_METHODS:END", StringComparison.Ordinal)) {
+ return true;
+ }
+
+ if (!CallTracingEnabled && line.StartsWith ("//#FEATURE=CALL_TRACING:END", StringComparison.Ordinal)) {
+ return true;
+ }
+
+ return false;
+ }
+
string? ReadResource (string resourceName)
{
using (var from = ExecutingAssembly.GetManifestResourceStream (resourceName)) {
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs
index 8bd49bfcd99..ed9ba40cad6 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs
@@ -355,7 +355,7 @@ IList MergeManifest (NativeCodeGenState codeGenState, Dictionary allJavaTypes, List javaTypesForJCW) = ScanForJavaTypes (resolver, tdCache, assemblies, userAssemblies, useMarshalMethods);
var jcwContext = new JCWGeneratorContext (arch, resolver, assemblies.Values, javaTypesForJCW, tdCache, useMarshalMethods);
- var jcwGenerator = new JCWGenerator (Log, jcwContext);
+ var jcwGenerator = new JCWGenerator (Log, jcwContext, IntermediateOutputDirectory);
bool success;
if (generateJavaCode) {
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs b/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs
index cecfd42ada2..1c7f7dfa4d3 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/GeneratePackageManagerJava.cs
@@ -23,6 +23,7 @@ public class GeneratePackageManagerJava : AndroidTask
public override string TaskPrefix => "GPM";
Guid buildId = Guid.NewGuid ();
+ MarshalMethodsTracingMode mmTracingMode;
[Required]
public ITaskItem[] ResolvedAssemblies { get; set; }
@@ -66,6 +67,7 @@ public class GeneratePackageManagerJava : AndroidTask
public bool InstantRunEnabled { get; set; }
public bool EnableMarshalMethods { get; set; }
+ public string MarshalMethodsTracingMode { get; set; }
public string RuntimeConfigBinFilePath { get; set; }
public string BoundExceptionType { get; set; }
@@ -91,6 +93,8 @@ bool _Debug {
public override bool RunTask ()
{
+ mmTracingMode = MonoAndroidHelper.ParseMarshalMethodsTracingMode (MarshalMethodsTracingMode);
+
BuildId = buildId.ToString ();
Log.LogDebugMessage (" [Output] BuildId: {0}", BuildId);
@@ -384,7 +388,8 @@ void AddEnvironment ()
Log,
assemblyCount,
uniqueAssemblyNames,
- EnsureCodeGenState (targetArch)
+ EnsureCodeGenState (targetArch),
+ mmTracingMode
);
} else {
marshalMethodsAsmGen = new MarshalMethodsNativeAssemblyGenerator (
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/LinkApplicationSharedLibraries.cs b/src/Xamarin.Android.Build.Tasks/Tasks/LinkApplicationSharedLibraries.cs
index 5f4b09ececa..1d8b17327ec 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/LinkApplicationSharedLibraries.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/LinkApplicationSharedLibraries.cs
@@ -44,8 +44,16 @@ sealed class InputFiles
[Required]
public string AndroidBinUtilsDirectory { get; set; }
+ public string MarshalMethodsTracingMode { get; set; }
+
+ MarshalMethodsTracingMode mmTracingMode;
+ bool mmTracingEnabled;
+
public override System.Threading.Tasks.Task RunTaskAsync ()
{
+ mmTracingMode = MonoAndroidHelper.ParseMarshalMethodsTracingMode (MarshalMethodsTracingMode);
+ mmTracingEnabled = mmTracingMode != Tasks.MarshalMethodsTracingMode.None;
+
return this.WhenAll (GetLinkerConfigs (), RunLinker);
}
@@ -122,6 +130,7 @@ IEnumerable GetLinkerConfigs ()
abis [abi] = GatherFilesForABI (item.ItemSpec, abi, ObjectFiles, runtimeNativeLibsDir, runtimeNativeLibStubsDir);
}
+ // "--eh-frame-hdr " +
const string commonLinkerArgs =
"--shared " +
"--allow-shlib-undefined " +
@@ -134,7 +143,7 @@ IEnumerable GetLinkerConfigs ()
"--warn-shared-textrel " +
"--fatal-warnings";
- string stripSymbolsArg = DebugBuild ? String.Empty : " -s";
+ string stripSymbolsArg = DebugBuild || mmTracingEnabled ? String.Empty : " -s";
string ld = Path.Combine (AndroidBinUtilsDirectory, MonoAndroidHelper.GetExecutablePath (AndroidBinUtilsDirectory, "ld"));
var targetLinkerArgs = new List ();
@@ -208,6 +217,11 @@ InputFiles GatherFilesForABI (string runtimeSharedLibrary, string abi, ITaskItem
"-lc",
};
+ if (mmTracingEnabled) {
+ extraLibraries.Add (Path.Combine (runtimeLibsDir, "libmarshal-methods-tracing.a"));
+ extraLibraries.Add ("-lxamarin-native-tracing");
+ }
+
return new InputFiles {
OutputSharedLibrary = runtimeSharedLibrary,
ObjectFiles = GetItemsForABI (abi, objectFiles),
diff --git a/src/Xamarin.Android.Build.Tasks/Tasks/ProcessNativeLibraries.cs b/src/Xamarin.Android.Build.Tasks/Tasks/ProcessNativeLibraries.cs
index 4970875d659..30a57d1398b 100644
--- a/src/Xamarin.Android.Build.Tasks/Tasks/ProcessNativeLibraries.cs
+++ b/src/Xamarin.Android.Build.Tasks/Tasks/ProcessNativeLibraries.cs
@@ -30,6 +30,7 @@ public class ProcessNativeLibraries : AndroidTask
public string [] ExcludedLibraries { get; set; }
public bool IncludeDebugSymbols { get; set; }
+ public bool IncludeNativeTracing { get; set; }
[Output]
public ITaskItem [] OutputLibraries { get; set; }
@@ -82,6 +83,9 @@ public override bool RunTask ()
if (!wantedComponents.Contains (fileName)) {
continue;
}
+ } else if (!IncludeNativeTracing && String.Compare ("libxamarin-native-tracing", fileName, StringComparison.Ordinal) == 0) {
+ Log.LogDebugMessage ($"Excluding '{library.ItemSpec}' because native stack traces support is disabled");
+ continue;
} else if (ExcludedLibraries != null && ExcludedLibraries.Contains (fileName, StringComparer.OrdinalIgnoreCase)) {
Log.LogDebugMessage ($"Excluding '{library.ItemSpec}'");
continue;
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs
index 8f7e569dfa4..7ec1b6f4951 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/ApplicationConfigNativeAssemblyGenerator.cs
@@ -327,7 +327,6 @@ void HashAndSortDSOCache (LlvmIrVariable variable, LlvmIrModuleTarget target, ob
if (entry == null) {
throw new InvalidOperationException ($"Internal error: DSO cache entry has unexpected type {instance.Obj.GetType ()}");
}
-
entry.hash = MonoAndroidHelper.GetXxHash (entry.HashedName, is64Bit);
entry.real_name_hash = MonoAndroidHelper.GetXxHash (entry.name, is64Bit);
}
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/JCWGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/JCWGenerator.cs
index d1a400bab20..f8cfa34d562 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/JCWGenerator.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/JCWGenerator.cs
@@ -42,13 +42,15 @@ class JCWGenerator
{
readonly TaskLoggingHelper log;
readonly JCWGeneratorContext context;
+ readonly string intermediateOutputDirectory;
public MarshalMethodsClassifier? Classifier { get; private set; }
- public JCWGenerator (TaskLoggingHelper log, JCWGeneratorContext context)
+ public JCWGenerator (TaskLoggingHelper log, JCWGeneratorContext context, string intermediateOutputDirectory)
{
this.log = log;
this.context = context;
+ this.intermediateOutputDirectory = intermediateOutputDirectory;
}
///
@@ -88,7 +90,7 @@ public bool GenerateAndClassify (string androidSdkPlatform, string outputPath, s
);
}
- MarshalMethodsClassifier MakeClassifier () => new MarshalMethodsClassifier (context.Arch, context.TypeDefinitionCache, context.Resolver, log);
+ MarshalMethodsClassifier MakeClassifier () => new MarshalMethodsClassifier (context.Arch, context.TypeDefinitionCache, context.Resolver, intermediateOutputDirectory, log);
bool ProcessTypes (bool generateCode, string androidSdkPlatform, MarshalMethodsClassifier? classifier, string? outputPath, string? applicationJavaClass)
{
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/JniRemappingAssemblyGenerator.cs b/src/Xamarin.Android.Build.Tasks/Utilities/JniRemappingAssemblyGenerator.cs
index dd6d0924fff..375025bfeb9 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/JniRemappingAssemblyGenerator.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/JniRemappingAssemblyGenerator.cs
@@ -210,6 +210,8 @@ public JniRemappingAssemblyGenerator (TaskLoggingHelper log, List> ();
+ Console.WriteLine ($"Type replacement input count: {typeReplacementsInput.Count}");
+
foreach (JniRemappingTypeReplacement mtr in typeReplacementsInput) {
var entry = new JniRemappingTypeReplacementEntry {
name = MakeJniRemappingString (mtr.From),
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrFunctionAttributeSet.cs b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrFunctionAttributeSet.cs
index 7c890e13417..5c8feabca5b 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrFunctionAttributeSet.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/LlvmIrGenerator/LlvmIrFunctionAttributeSet.cs
@@ -53,6 +53,19 @@ public void Add (IList attrList)
}
}
+ public void Add (AndroidTargetArch targetArch, LlvmIrFunctionAttribute attr)
+ {
+ if (privateTargetSpecificAttributes == null) {
+ privateTargetSpecificAttributes = new ();
+ }
+
+ if (!privateTargetSpecificAttributes.TryGetValue (targetArch, out List attrList)) {
+ attrList = new ();
+ privateTargetSpecificAttributes.Add (targetArch, attrList);
+ }
+ attrList.Add (attr);
+ }
+
public string Render ()
{
List list = attributes.ToList ();
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsAssemblyRewriter.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsAssemblyRewriter.cs
index 9f1034bd6b8..0a03881be93 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsAssemblyRewriter.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsAssemblyRewriter.cs
@@ -300,6 +300,12 @@ void GenerateNonBlittableConversion (TypeReference sourceType, TypeReference tar
return;
}
+ if (IsCharConversion (sourceType, targetType)) {
+ // No need to generate any code. Java's `char` is unsigned 16-bit type, the same as
+ // .NET's
+ return;
+ }
+
ThrowUnsupportedType (sourceType);
}
@@ -333,6 +339,19 @@ bool IsBooleanConversion (TypeReference sourceType, TypeReference targetType)
return false;
}
+ bool IsCharConversion (TypeReference sourceType, TypeReference targetType)
+ {
+ if (String.Compare ("System.Char", sourceType.FullName, StringComparison.Ordinal) == 0) {
+ if (String.Compare ("System.UInt16", targetType.FullName, StringComparison.Ordinal) != 0) {
+ throw new InvalidOperationException ($"Unexpected conversion from '{sourceType.FullName}' to '{targetType.FullName}'");
+ }
+
+ return true;
+ }
+
+ return false;
+ }
+
void ThrowUnsupportedType (TypeReference type)
{
throw new InvalidOperationException ($"[{targetArch}] Unsupported non-blittable type '{type.FullName}'");
@@ -437,6 +456,12 @@ TypeReference MapToBlittableTypeIfNecessary (TypeReference type, out bool typeMa
return ReturnValid (typeof(byte));
}
+ if (String.Compare ("System.Char", type.FullName, StringComparison.Ordinal) == 0) {
+ // Maps to Java JNI's jchar which is an unsigned 16-bit type
+ typeMapped = true;
+ return ReturnValid (typeof(ushort));
+ }
+
throw new NotSupportedException ($"[{targetArch}] Cannot map unsupported blittable type '{type.FullName}'");
TypeReference ReturnValid (Type typeToLookUp)
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsClassifier.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsClassifier.cs
index 1507084dfc0..5936f6e7fb5 100644
--- a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsClassifier.cs
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsClassifier.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.IO;
using System.Linq;
using Java.Interop.Tools.Cecil;
@@ -241,6 +242,7 @@ public bool Matches (MethodDefinition method)
HashSet typesWithDynamicallyRegisteredMethods;
ulong rejectedMethodCount = 0;
ulong wrappedMethodCount = 0;
+ StreamWriter ignoredMethodsLog;
readonly AndroidTargetArch targetArch;
public IDictionary> MarshalMethods => marshalMethods;
@@ -249,7 +251,7 @@ public bool Matches (MethodDefinition method)
public ulong WrappedMethodCount => wrappedMethodCount;
public TypeDefinitionCache TypeDefinitionCache => tdCache;
- public MarshalMethodsClassifier (AndroidTargetArch targetArch, TypeDefinitionCache tdCache, IAssemblyResolver res, TaskLoggingHelper log)
+ public MarshalMethodsClassifier (AndroidTargetArch targetArch, TypeDefinitionCache tdCache, IAssemblyResolver res, string intermediateOutputDirectory, TaskLoggingHelper log)
{
this.targetArch = targetArch;
this.log = log ?? throw new ArgumentNullException (nameof (log));
@@ -258,9 +260,12 @@ public MarshalMethodsClassifier (AndroidTargetArch targetArch, TypeDefinitionCac
marshalMethods = new Dictionary> (StringComparer.Ordinal);
assemblies = new HashSet ();
typesWithDynamicallyRegisteredMethods = new HashSet ();
+
+ var fs = File.Open (Path.Combine (intermediateOutputDirectory, $"{targetArch}-marshal-methods-ignored.txt"), FileMode.Create);
+ ignoredMethodsLog = new StreamWriter (fs, Files.UTF8withoutBOM);
}
- public MarshalMethodsClassifier (TypeDefinitionCache tdCache, IAssemblyResolver res, TaskLoggingHelper log)
+ public MarshalMethodsClassifier (TypeDefinitionCache tdCache, IAssemblyResolver res, string intermediateOutputDirectory, TaskLoggingHelper log)
{
this.log = log ?? throw new ArgumentNullException (nameof (log));
this.tdCache = tdCache ?? throw new ArgumentNullException (nameof (tdCache));
@@ -268,6 +273,17 @@ public MarshalMethodsClassifier (TypeDefinitionCache tdCache, IAssemblyResolver
marshalMethods = new Dictionary> (StringComparer.Ordinal);
assemblies = new HashSet ();
typesWithDynamicallyRegisteredMethods = new HashSet ();
+
+ var fs = File.Open (Path.Combine (intermediateOutputDirectory, "marshal-methods-ignored.txt"), FileMode.Create);
+ ignoredMethodsLog = new StreamWriter (fs, Files.UTF8withoutBOM);
+ }
+
+ public void FlushAndCloseOutputs ()
+ {
+ ignoredMethodsLog.WriteLine ();
+ ignoredMethodsLog.WriteLine ($"Marshal methods count: {MarshalMethods.Count}; Rejected methods count: {RejectedMethodCount}");
+ ignoredMethodsLog.Flush ();
+ ignoredMethodsLog.Close ();
}
public override bool ShouldBeDynamicallyRegistered (TypeDefinition topType, MethodDefinition registeredMethod, MethodDefinition implementedMethod, CustomAttribute? registerAttribute)
@@ -461,6 +477,15 @@ bool IsDynamicallyRegistered (TypeDefinition topType, MethodDefinition registere
return true;
}
+ void LogIgnored (TypeDefinition type, MethodDefinition method, string message, bool logWarning = true)
+ {
+ if (logWarning) {
+ log.LogWarning (message);
+ }
+
+ ignoredMethodsLog.WriteLine ($"{type.FullName}\t{method.FullName}\t{message}");
+ }
+
bool IsStandardHandler (TypeDefinition topType, ConnectorInfo connector, MethodDefinition registeredMethod, MethodDefinition implementedMethod, string jniName, string jniSignature)
{
const string HandlerNameStart = "Get";
@@ -485,23 +510,23 @@ bool IsStandardHandler (TypeDefinition topType, ConnectorInfo connector, MethodD
MethodDefinition connectorMethod = FindMethod (connectorDeclaringType, connectorName);
if (connectorMethod == null) {
- log.LogWarning ($"Connector method '{connectorName}' not found in type '{connectorDeclaringType.FullName}' {GetAssemblyPathInfo (connectorDeclaringType)}");
+ LogIgnored (topType, registeredMethod, $"Connector method '{connectorName}' not found in type '{connectorDeclaringType.FullName}' {GetAssemblyPathInfo (connectorDeclaringType)}");
return false;
}
if (String.Compare ("System.Delegate", connectorMethod.ReturnType.FullName, StringComparison.Ordinal) != 0) {
- log.LogWarning ($"Connector '{connectorName}' in type '{connectorDeclaringType.FullName}' has invalid return type, expected 'System.Delegate', found '{connectorMethod.ReturnType.FullName}' {GetAssemblyPathInfo (connectorDeclaringType)}");
+ LogIgnored (topType, registeredMethod, $"Connector '{connectorName}' in type '{connectorDeclaringType.FullName}' has invalid return type, expected 'System.Delegate', found '{connectorMethod.ReturnType.FullName}' {GetAssemblyPathInfo (connectorDeclaringType)}");
return false;
}
var ncbs = new NativeCallbackSignature (registeredMethod, log, tdCache);
MethodDefinition nativeCallbackMethod = FindMethod (connectorDeclaringType, nativeCallbackName, ncbs);
if (nativeCallbackMethod == null) {
- log.LogWarning ($"Unable to find native callback method '{nativeCallbackName}' in type '{connectorDeclaringType.FullName}', matching the '{registeredMethod.FullName}' signature (jniName: '{jniName}') {GetAssemblyPathInfo (connectorDeclaringType)}");
+ LogIgnored (topType, registeredMethod, $"Unable to find native callback method '{nativeCallbackName}' in type '{connectorDeclaringType.FullName}', matching the '{registeredMethod.FullName}' signature (jniName: '{jniName}') {GetAssemblyPathInfo (connectorDeclaringType)}");
return false;
}
- if (!EnsureIsValidUnmanagedCallersOnlyTarget (nativeCallbackMethod, out bool needsBlittableWorkaround)) {
+ if (!EnsureIsValidUnmanagedCallersOnlyTarget (topType, registeredMethod, nativeCallbackMethod, out bool needsBlittableWorkaround)) {
return false;
}
@@ -510,7 +535,7 @@ bool IsStandardHandler (TypeDefinition topType, ConnectorInfo connector, MethodD
FieldDefinition delegateField = FindField (nativeCallbackMethod.DeclaringType, delegateFieldName);
if (delegateField != null) {
if (String.Compare ("System.Delegate", delegateField.FieldType.FullName, StringComparison.Ordinal) != 0) {
- log.LogWarning ($"delegate field '{delegateFieldName}' in type '{nativeCallbackMethod.DeclaringType.FullName}' has invalid type, expected 'System.Delegate', found '{delegateField.FieldType.FullName}' {GetAssemblyPathInfo (delegateField)}");
+ LogIgnored (topType, registeredMethod, $"delegate field '{delegateFieldName}' in type '{nativeCallbackMethod.DeclaringType.FullName}' has invalid type, expected 'System.Delegate', found '{delegateField.FieldType.FullName}' {GetAssemblyPathInfo (delegateField)}");
return false;
}
}
@@ -569,23 +594,23 @@ bool IsStandardHandler (TypeDefinition topType, ConnectorInfo connector, MethodD
return true;
}
- bool EnsureIsValidUnmanagedCallersOnlyTarget (MethodDefinition method, out bool needsBlittableWorkaround)
+ bool EnsureIsValidUnmanagedCallersOnlyTarget (TypeDefinition topType, MethodDefinition registeredMethod, MethodDefinition nativeCallbackMethod, out bool needsBlittableWorkaround)
{
needsBlittableWorkaround = false;
// Requirements: https://docs.microsoft.com/en-us/dotnet/api/system.runtime.interopservices.unmanagedcallersonlyattribute?view=net-6.0#remarks
- if (!method.IsStatic) {
+ if (!nativeCallbackMethod.IsStatic) {
return LogReasonWhyAndReturnFailure ($"is not static");
}
- if (method.HasGenericParameters) {
+ if (nativeCallbackMethod.HasGenericParameters) {
return LogReasonWhyAndReturnFailure ($"has generic parameters");
}
TypeReference type;
bool needsWrapper = false;
- if (String.Compare ("System.Void", method.ReturnType.FullName, StringComparison.Ordinal) != 0) {
- type = GetRealType (method.ReturnType);
+ if (String.Compare ("System.Void", nativeCallbackMethod.ReturnType.FullName, StringComparison.Ordinal) != 0) {
+ type = GetRealType (nativeCallbackMethod.ReturnType);
if (!IsAcceptable (type)) {
needsBlittableWorkaround = true;
WarnWhy ($"has a non-blittable return type '{type.FullName}'");
@@ -593,15 +618,15 @@ bool EnsureIsValidUnmanagedCallersOnlyTarget (MethodDefinition method, out bool
}
}
- if (method.DeclaringType.HasGenericParameters) {
+ if (nativeCallbackMethod.DeclaringType.HasGenericParameters) {
return LogReasonWhyAndReturnFailure ($"is declared in a type with generic parameters");
}
- if (!method.HasParameters) {
+ if (!nativeCallbackMethod.HasParameters) {
return UpdateWrappedCountAndReturn (true);
}
- foreach (ParameterDefinition pdef in method.Parameters) {
+ foreach (ParameterDefinition pdef in nativeCallbackMethod.Parameters) {
type = GetRealType (pdef.ParameterType);
if (!IsAcceptable (type)) {
@@ -645,14 +670,14 @@ TypeReference GetRealType (TypeReference type)
bool LogReasonWhyAndReturnFailure (string why)
{
- log.LogWarning ($"Method '{method.FullName}' {why}. It cannot be used with the `[UnmanagedCallersOnly]` attribute");
+ LogIgnored (topType, registeredMethod, $"Method '{nativeCallbackMethod.FullName}' {why}. It cannot be used with the `[UnmanagedCallersOnly]` attribute");
return false;
}
void WarnWhy (string why)
{
// 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");
+ log.LogDebugMessage ($"Method '{nativeCallbackMethod.FullName}' {why}. A workaround is required, this may make the application slower");
}
}
diff --git a/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsNativeAssemblyGenerator.Tracing.cs b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsNativeAssemblyGenerator.Tracing.cs
new file mode 100644
index 00000000000..7f4afc832c9
--- /dev/null
+++ b/src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsNativeAssemblyGenerator.Tracing.cs
@@ -0,0 +1,603 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.Text;
+
+using Xamarin.Android.Tools;
+using Xamarin.Android.Tasks.LLVMIR;
+
+namespace Xamarin.Android.Tasks;
+
+using CecilMethodDefinition = global::Mono.Cecil.MethodDefinition;
+using CecilParameterDefinition = global::Mono.Cecil.ParameterDefinition;
+
+partial class MarshalMethodsNativeAssemblyGenerator
+{
+ sealed class LlvmLifetimePointerSizeArgumentPlaceholder : LlvmIrInstructionPointerSizeArgumentPlaceholder
+ {
+ public override object? GetValue (LlvmIrModuleTarget target)
+ {
+ // the llvm.lifetime functions need a 64-bit integer, target.NativePointerSize is 32-bit
+ return (ulong)(uint)base.GetValue (target);
+ }
+ }
+
+ enum TracingRenderArgumentFunction
+ {
+ None,
+ GetClassName,
+ GetObjectClassname,
+ GetCString,
+ GetBooleanString,
+ }
+
+ sealed class TracingState
+ {
+ public List