Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion external/Java.Interop
Submodule Java.Interop updated 46 files
+37 −1 Java.Interop.sln
+26 −0 Makefile
+43 −0 build-tools/automation/templates/core-tests.yaml
+5 −0 samples/Hello-NativeAOTFromAndroid/Hello-NativeAOTFromAndroid.csproj
+25 −0 samples/Hello-NativeAOTFromAndroid/Hello-NativeAOTFromAndroid.targets
+1 −0 samples/Hello-NativeAOTFromAndroid/JavaInteropRuntime.cs
+5 −0 samples/Hello-NativeAOTFromJNI/Hello-NativeAOTFromJNI.csproj
+24 −0 samples/Hello-NativeAOTFromJNI/Hello-NativeAOTFromJNI.targets
+0 −32 samples/Hello-NativeAOTFromJNI/JavaCallableAttributes.cs
+1 −0 samples/Hello-NativeAOTFromJNI/JavaInteropRuntime.cs
+1 −26 samples/Hello-NativeAOTFromJNI/ManagedType.cs
+25 −0 src/Java.Interop.Export/Java.Interop.Export.csproj
+0 −0 src/Java.Interop.Export/Java.Interop/JavaCallableAttribute.cs
+0 −0 src/Java.Interop.Export/Java.Interop/JavaCallableConstructorAttribute.cs
+545 −0 src/Java.Interop.Export/Java.Interop/MarshalMemberBuilder.cs
+27 −0 src/Java.Interop.Tools.Expressions/Java.Interop.Tools.Expressions.csproj
+828 −0 src/Java.Interop.Tools.Expressions/Java.Interop.Tools.Expressions/CecilCompilerExpressionVisitor.cs
+443 −0 src/Java.Interop.Tools.Expressions/Java.Interop.Tools.Expressions/ExpressionAssemblyBuilder.cs
+9 −0 src/Java.Interop.Tools.Expressions/Java.Interop.Tools.Expressions/ExpressionMethodRegistration.cs
+1 −0 src/Java.Interop/GlobalSuppressions.cs
+164 −10 src/Java.Interop/Java.Interop/JniRuntime.JniMarshalMemberBuilder.cs
+3 −0 src/Java.Interop/Java.Interop/JniRuntime.cs
+101 −4 src/Java.Runtime.Environment/Java.Interop/JreTypeManager.cs
+10 −0 src/Java.Runtime.Environment/Java.Interop/ManagedValueManager.cs
+2 −13 src/Java.Runtime.Environment/Java.Interop/MonoRuntimeValueManager.cs
+1 −0 src/Java.Runtime.Environment/Java.Runtime.Environment.csproj
+0 −50 tests/Java.Base-Tests/Java.Base/JavaToManagedTests.cs
+19 −0 tests/Java.Interop-Tests/Java.Interop/JniRuntimeTest.cs
+4 −4 tests/Java.Interop-Tests/Java.Interop/TestType.cs
+41 −0 tests/Java.Interop.Export-Tests/Java.Interop.Export-Tests.csproj
+53 −0 tests/Java.Interop.Export-Tests/Java.Interop.Export-Tests.targets
+286 −0 tests/Java.Interop.Export-Tests/Java.Interop/ExportTest.cs
+36 −0 tests/Java.Interop.Export-Tests/Java.Interop/JavaCallableExample.cs
+27 −0 tests/Java.Interop.Export-Tests/Java.Interop/JavaCallableExampleTests.cs
+29 −0 tests/Java.Interop.Export-Tests/Java.Interop/JavaVMFixture.cs
+570 −0 tests/Java.Interop.Export-Tests/Java.Interop/MarshalMemberBuilderTest.cs
+162 −0 tests/Java.Interop.Export-Tests/java/net/dot/jni/test/ExportType.java
+11 −0 tests/Java.Interop.Export-Tests/java/net/dot/jni/test/UseJavaCallableExample.java
+33 −0 tests/Java.Interop.Tools.Expressions-Tests/Java.Interop.Tools.Expressions-Tests.csproj
+444 −0 .../Java.Interop.Tools.Expressions-Tests/Java.Interop.Tools.ExpressionsTests/ExpressionAssemblyBuilderTests.cs
+1 −0 tests/Java.Interop.Tools.Expressions-Tests/Usings.cs
+2 −0 tests/Java.Interop.Tools.JavaCallableWrappers-Tests/Java.Interop.Tools.JavaCallableWrappers-Tests.csproj
+848 −0 tools/jnimarshalmethod-gen/App.cs
+29 −0 tools/jnimarshalmethod-gen/Message.cs
+549 −0 tools/jnimarshalmethod-gen/TypeMover.cs
+40 −0 tools/jnimarshalmethod-gen/Xamarin.Android.Tools.JniMarshalMethodGenerator.csproj
7 changes: 7 additions & 0 deletions pr-message.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[marshal methods] Move marshal method pipeline from outer build to inner (per-RID) build

## Summary

Moves marshal method classification, assembly rewriting, and `.ll` LLVM IR generation from the outer build into the inner (per-RID) build's `RewriteMarshalMethods` task, running after ILLink trimming on already-trimmed assemblies. This eliminates the token staleness problem where classification captured token state, then rewriting mutated tokens, then downstream tasks saw stale data.

When marshal methods are enabled, the task classifies, rewrites assemblies, and generates a full `.ll`. When disabled, it generates an empty/minimal `.ll` with just the structural scaffolding the native runtime links against. `GenerateNativeMarshalMethodSources` is stripped down to P/Invoke preservation only.
8 changes: 8 additions & 0 deletions run-mm-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#!/bin/bash
set -e
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
TEST_DLL="$SCRIPT_DIR/bin/TestRelease/net10.0/Xamarin.Android.Build.Tests.dll"
rm -rf "$SCRIPT_DIR/bin/TestRelease/temp/MarshalMethodsCollectionScanning"
exec "$SCRIPT_DIR/dotnet-local.sh" test "$TEST_DLL" \
--filter "Name~MarshalMethodsCollectionScanning" \
-- NUnit.NumberOfTestWorkers=1
17 changes: 17 additions & 0 deletions run-test-coreclr-trimmable.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/bin/bash
# Reproduce the CoreCLRTrimmable build failure
set -e

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
TEST_PROJ="$SCRIPT_DIR/tests/Mono.Android-Tests/Mono.Android-Tests/Mono.Android.NET-Tests.csproj"

export PATH="$HOME/android-toolchain/sdk/platform-tools:$PATH"

exec "$SCRIPT_DIR/dotnet-local.sh" build "$TEST_PROJ" \
-c Release \
-p:AndroidSdkDirectory="$HOME/android-toolchain/sdk" \
-p:UseMonoRuntime=false \
-p:_AndroidTypeMapImplementation=trimmable \
-p:TestsFlavor=CoreCLRTrimmable \
-t:SignAndroidPackage \
-v:n
17 changes: 17 additions & 0 deletions run-test-mm-app-runs.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
#!/bin/bash
# Run the MarshalMethodsAppRuns device test
set -e

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
TEST_DLL="$SCRIPT_DIR/bin/TestRelease/MSBuildDeviceIntegration/net10.0/MSBuildDeviceIntegration.dll"

export PATH="$HOME/android-toolchain/sdk/platform-tools:$PATH"

if [ ! -f "$TEST_DLL" ]; then
echo "ERROR: Test DLL not found at $TEST_DLL"
exit 1
fi

exec "$SCRIPT_DIR/dotnet-local.sh" test "$TEST_DLL" \
--filter "Name~MarshalMethodsAppRuns" \
-- NUnit.NumberOfTestWorkers=1
25 changes: 25 additions & 0 deletions run-test-similar-androidx.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/bash
# Wrapper script to run the SimilarAndroidXAssemblyNames(False,MonoVM) test case locally.
# Usage: ./run-test-similar-androidx.sh [Configuration]
# Configuration defaults to "Release"

set -euo pipefail

SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
CONFIG="${1:-Release}"
TFM="net10.0"
DLL="$SCRIPT_DIR/bin/Test${CONFIG}/${TFM}/Xamarin.Android.Build.Tests.dll"

if [[ ! -f "$DLL" ]]; then
echo "ERROR: Test DLL not found at: $DLL"
echo " You may need to build the tests first, or pass a different Configuration."
exit 1
fi

echo "=== Running: SimilarAndroidXAssemblyNames(False,MonoVM) ==="
echo " DLL: $DLL"
echo ""

exec "$SCRIPT_DIR/dotnet-local.sh" test "$DLL" \
--logger "console;verbosity=detailed" \
-- NUnit.Where="method == SimilarAndroidXAssemblyNames and test =~ /False,MonoVM/"
18 changes: 18 additions & 0 deletions run-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
#!/bin/bash
# Run the CustomApplicationClassAndMultiDex(True,MonoVM) test locally
# This test failed in CI with a build failure related to marshal methods pipeline changes.
set -euo pipefail

REPO_ROOT="$(cd "$(dirname "$0")" && pwd)"
TEST_DLL="$REPO_ROOT/bin/TestRelease/net10.0/Xamarin.Android.Build.Tests.dll"

if [ ! -f "$TEST_DLL" ]; then
echo "ERROR: Test DLL not found at $TEST_DLL"
echo "You may need to build the tests first."
exit 1
fi

echo "Running: CustomApplicationClassAndMultiDex(True,MonoVM)"
exec "$REPO_ROOT/dotnet-local.sh" test "$TEST_DLL" \
--filter "Name~CustomApplicationClassAndMultiDex" \
-- NUnit.NumberOfTestWorkers=1
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,6 @@ public class FindJavaObjectsStep : BaseStep, IAssemblyModifierPipelineStep

public bool ErrorOnCustomJavaObject { get; set; }

public bool UseMarshalMethods { get; set; }

public TaskLoggingHelper Log { get; set; }

public FindJavaObjectsStep (TaskLoggingHelper log) => Log = log;
Expand Down Expand Up @@ -114,11 +112,6 @@ List<CallableWrapperType> ConvertToCallableWrappers (List<TypeDefinition> types)
DefaultMonoRuntimeInitialization = "mono.MonoPackageManager.LoadApplication (context);",
};

if (UseMarshalMethods) {
var classifier = new MarshalMethodsClassifier (Context, Context.Resolver, Log);
reader_options.MethodClassifier = new MarshalMethodsCollection (classifier);
}

foreach (var type in types) {
var wrapper = CecilImporter.CreateType (type, Context, reader_options);
wrappers.Add (wrapper);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,13 +83,6 @@
EnableNativeRuntimeLinking="$(_AndroidEnableNativeRuntimeLinking)">
</GenerateJavaStubs>

<RewriteMarshalMethods
Condition=" '$(_AndroidUseMarshalMethods)' == 'true' And '$(AndroidIncludeDebugSymbols)' == 'false' "
EnableManagedMarshalMethodsLookup="$(_AndroidUseManagedMarshalMethodsLookup)"
Environments="@(_EnvironmentFiles)"
IntermediateOutputDirectory="$(IntermediateOutputPath)">
</RewriteMarshalMethods>

<GenerateTypeMappings
AndroidRuntime="$(_AndroidRuntime)"
Debug="$(AndroidIncludeDebugSymbols)"
Expand Down Expand Up @@ -132,6 +125,7 @@
<GenerateAdditionalProviderSources
AdditionalProviderSources="@(_AdditionalProviderSources)"
AndroidRuntime="$(_AndroidRuntime)"
EnableMarshalMethods="$(_AndroidUseMarshalMethods)"
Environments="@(_EnvironmentFiles)"
HttpClientHandlerType="$(AndroidHttpClientHandlerType)"
EnableSGenConcurrent="$(AndroidEnableSGenConcurrent)"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,6 @@ protected virtual void BuildPipeline (AssemblyPipeline pipeline, MSBuildLinkCont
var findJavaObjectsStep = new FindJavaObjectsStep (Log) {
ApplicationJavaClass = ApplicationJavaClass,
ErrorOnCustomJavaObject = ErrorOnCustomJavaObject,
UseMarshalMethods = EnableMarshalMethods,
};

findJavaObjectsStep.Initialize (context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,13 @@ public class GenerateAdditionalProviderSources : AndroidTask
public string? HttpClientHandlerType { get; set; }
public bool EnableSGenConcurrent { get; set; }

/// <summary>
/// When true, <c>ApplicationRegistration.java</c> is generated by the inner build's
/// <see cref="RewriteMarshalMethods"/> task (which has the classifier to filter types).
/// This task skips generating it to avoid overwriting the inner build's output.
/// </summary>
public bool EnableMarshalMethods { get; set; }

AndroidRuntime androidRuntime;
JavaPeerStyle codeGenerationTarget;

Expand Down Expand Up @@ -114,29 +121,34 @@ void Generate (NativeCodeGenStateObject codeGenState)
}

// Create additional application java sources.
StringWriter regCallsWriter = new StringWriter ();
regCallsWriter.WriteLine ("// Application and Instrumentation ACWs must be registered first.");

foreach ((string jniName, string assemblyQualifiedName) in codeGenState.ApplicationsAndInstrumentationsToRegister) {
regCallsWriter.WriteLine (
codeGenerationTarget == JavaPeerStyle.XAJavaInterop1 ?
"\t\tmono.android.Runtime.register (\"{0}\", {1}.class, {1}.__md_methods);" :
"\t\tnet.dot.jni.ManagedPeer.registerNativeMembers ({1}.class, {1}.__md_methods);",
assemblyQualifiedName,
jniName
);
}
// When marshal methods are enabled, ApplicationRegistration.java is generated
// by RewriteMarshalMethods in the inner build, which has the classifier to
// filter out types with no dynamically registered methods.
if (!EnableMarshalMethods) {
StringWriter regCallsWriter = new StringWriter ();
regCallsWriter.WriteLine ("// Application and Instrumentation ACWs must be registered first.");

foreach ((string jniName, string assemblyQualifiedName) in codeGenState.ApplicationsAndInstrumentationsToRegister) {
regCallsWriter.WriteLine (
codeGenerationTarget == JavaPeerStyle.XAJavaInterop1 ?
"\t\tmono.android.Runtime.register (\"{0}\", {1}.class, {1}.__md_methods);" :
"\t\tnet.dot.jni.ManagedPeer.registerNativeMembers ({1}.class, {1}.__md_methods);",
assemblyQualifiedName,
jniName
);
}

regCallsWriter.Close ();
regCallsWriter.Close ();

var real_app_dir = Path.Combine (OutputDirectory, "src", "net", "dot", "android");
string applicationTemplateFile = "ApplicationRegistration.java";
SaveResource (
applicationTemplateFile,
applicationTemplateFile,
real_app_dir,
template => template.Replace ("// REGISTER_APPLICATION_AND_INSTRUMENTATION_CLASSES_HERE", regCallsWriter.ToString ())
);
var real_app_dir = Path.Combine (OutputDirectory, "src", "net", "dot", "android");
string applicationTemplateFile = "ApplicationRegistration.java";
SaveResource (
applicationTemplateFile,
applicationTemplateFile,
real_app_dir,
template => template.Replace ("// REGISTER_APPLICATION_AND_INSTRUMENTATION_CLASSES_HERE", regCallsWriter.ToString ())
);
}

void AppendEnvVarEntry (StringBuilder sb, string value)
{
Expand Down
10 changes: 4 additions & 6 deletions src/Xamarin.Android.Build.Tasks/Tasks/GenerateJavaStubs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -206,12 +206,10 @@ internal static Dictionary<string, ITaskItem> MaybeGetArchAssemblies (Dictionary
var tdCache = new TypeDefinitionCache ();
(List<TypeDefinition> allJavaTypes, List<TypeDefinition> javaTypesForJCW) = ScanForJavaTypes (resolver, tdCache, assemblies, userAssemblies, useMarshalMethods);

MarshalMethodsCollection? marshalMethodsCollection = null;

if (useMarshalMethods)
marshalMethodsCollection = MarshalMethodsCollection.FromAssemblies (arch, assemblies.Values.ToList (), resolver, Log);

return new NativeCodeGenState (arch, tdCache, resolver, allJavaTypes, javaTypesForJCW, marshalMethodsCollection);
// Marshal method classification is now done in the inner build by
// RewriteMarshalMethods, so we never classify here. The NativeCodeGenState
// will have a null Classifier; downstream tasks must handle that.
return new NativeCodeGenState (arch, tdCache, resolver, allJavaTypes, javaTypesForJCW, classifier: null);
}

(List<TypeDefinition> allJavaTypes, List<TypeDefinition> javaTypesForJCW) ScanForJavaTypes (XAAssemblyResolver res, TypeDefinitionCache cache, Dictionary<string, ITaskItem> assemblies, Dictionary<string, ITaskItem> userAssemblies, bool useMarshalMethods)
Expand Down
Loading