Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
4 changes: 4 additions & 0 deletions build-tools/installers/create-installers.targets
Original file line number Diff line number Diff line change
Expand Up @@ -129,12 +129,16 @@
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)bundletool.jar" />
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)java_runtime_net6.jar" ExcludeFromLegacy="true" />
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)java_runtime_fastdev_net6.jar" ExcludeFromLegacy="true" />
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)java_runtime_trimmable_net6.jar" ExcludeFromLegacy="true" />
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)java_runtime_net6.dex" ExcludeFromLegacy="true" />
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)java_runtime_fastdev_net6.dex" ExcludeFromLegacy="true" />
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)java_runtime_trimmable_net6.dex" ExcludeFromLegacy="true" />
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)java_runtime_clr.jar" ExcludeFromLegacy="true" />
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)java_runtime_fastdev_clr.jar" ExcludeFromLegacy="true" />
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)java_runtime_trimmable_clr.jar" ExcludeFromLegacy="true" />
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)java_runtime_clr.dex" ExcludeFromLegacy="true" />
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)java_runtime_fastdev_clr.dex" ExcludeFromLegacy="true" />
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)java_runtime_trimmable_clr.dex" ExcludeFromLegacy="true" />
Comment thread
jonathanpeppers marked this conversation as resolved.
Outdated
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)manifestmerger.jar" />
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)proguard-android.txt" />
<_MSBuildFiles Include="$(MicrosoftAndroidSdkOutDir)protobuf-net.dll" />
Expand Down
14 changes: 12 additions & 2 deletions src/Xamarin.Android.Build.Tasks/Xamarin.Android.Common.targets
Original file line number Diff line number Diff line change
Expand Up @@ -1391,14 +1391,24 @@ because xbuild doesn't support framework reference assemblies.
<MakeDir Directories="$(MonoAndroidIntermediateAssemblyDir)" Condition=" !Exists ('$(MonoAndroidIntermediateAssemblyDir)') " />
</Target>

<!-- Runtime jar selection: 2×2 matrix of runtime (MonoVM|CoreCLR) × typemap (default|trimmable).
Trimmable jars replace JavaProxyObject/JavaProxyThrowable with non-native implementations. -->
<Target Name="_CollectRuntimeJarFilenames">
<PropertyGroup Condition=" '$(_AndroidRuntime)' != 'CoreCLR' ">
<PropertyGroup Condition=" '$(_AndroidRuntime)' != 'CoreCLR' and '$(_AndroidTypeMapImplementation)' != 'trimmable' ">
<_RuntimeJar>$(MSBuildThisFileDirectory)\java_runtime_net6.jar</_RuntimeJar>
</PropertyGroup>

<PropertyGroup Condition=" '$(_AndroidRuntime)' == 'CoreCLR' ">
<PropertyGroup Condition=" '$(_AndroidRuntime)' != 'CoreCLR' and '$(_AndroidTypeMapImplementation)' == 'trimmable' ">
<_RuntimeJar>$(MSBuildThisFileDirectory)\java_runtime_trimmable_net6.jar</_RuntimeJar>
</PropertyGroup>

<PropertyGroup Condition=" '$(_AndroidRuntime)' == 'CoreCLR' and '$(_AndroidTypeMapImplementation)' != 'trimmable' ">
<_RuntimeJar>$(MSBuildThisFileDirectory)\java_runtime_clr.jar</_RuntimeJar>
</PropertyGroup>

<PropertyGroup Condition=" '$(_AndroidRuntime)' == 'CoreCLR' and '$(_AndroidTypeMapImplementation)' == 'trimmable' ">
<_RuntimeJar>$(MSBuildThisFileDirectory)\java_runtime_trimmable_clr.jar</_RuntimeJar>
</PropertyGroup>
</Target>
Comment thread
simonrozsival marked this conversation as resolved.

<Target Name="_AddStaticResources"
Expand Down
21 changes: 20 additions & 1 deletion src/java-runtime/java-runtime.targets
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@
<IntermediateRuntimeClassesTxt>$(IntermediateOutputPath)release-net6.txt</IntermediateRuntimeClassesTxt>
<RemoveItems>..\..\src-ThirdParty\bazel\java\mono\android\debug\MultiDexLoader.java;java\mono\android\debug-net6\BuildConfig.java;java\mono\android\debug\BuildConfig.java;java\mono\android\release\BuildConfig.java;java\mono\android\clr\MonoPackageManager.java</RemoveItems>
</_RuntimeOutput>
<_RuntimeOutput Include="$(OutputPath)java_runtime_trimmable_net6.jar">
<OutputJar>$(OutputPath)java_runtime_trimmable_net6.jar</OutputJar>
<OutputDex>$(OutputPath)java_runtime_trimmable_net6.dex</OutputDex>
Comment thread
jonathanpeppers marked this conversation as resolved.
Outdated
<IntermediateRuntimeOutputPath>$(IntermediateOutputPath)release-trimmable-net6</IntermediateRuntimeOutputPath>
<IntermediateRuntimeClassesTxt>$(IntermediateOutputPath)release-trimmable-net6.txt</IntermediateRuntimeClassesTxt>
<RemoveItems>..\..\src-ThirdParty\bazel\java\mono\android\debug\MultiDexLoader.java;java\mono\android\debug-net6\BuildConfig.java;java\mono\android\debug\BuildConfig.java;java\mono\android\release\BuildConfig.java;java\mono\android\clr\MonoPackageManager.java;$(JavaInteropSourceDirectory)\src\Java.Interop\java\net\dot\jni\internal\JavaProxyObject.java;$(JavaInteropSourceDirectory)\src\Java.Interop\java\net\dot\jni\internal\JavaProxyThrowable.java</RemoveItems>
<AddItems>java-trimmable\net\dot\jni\internal\JavaProxyObject.java;java-trimmable\net\dot\jni\internal\JavaProxyThrowable.java</AddItems>
</_RuntimeOutput>
<_RuntimeOutput Include="$(OutputPath)java_runtime_fastdev_net6.jar">
<OutputJar>$(OutputPath)java_runtime_fastdev_net6.jar</OutputJar>
<OutputDex>$(OutputPath)java_runtime_fastdev_net6.dex</OutputDex>
Expand All @@ -39,6 +47,14 @@
<IntermediateRuntimeClassesTxt>$(IntermediateOutputPath)release-clr.txt</IntermediateRuntimeClassesTxt>
<RemoveItems>..\..\src-ThirdParty\bazel\java\mono\android\debug\MultiDexLoader.java;java\mono\android\debug-net6\BuildConfig.java;java\mono\android\debug\BuildConfig.java;java\mono\android\release\BuildConfig.java;java\mono\android\MonoPackageManager.java</RemoveItems>
</_RuntimeOutput>
<_RuntimeOutput Include="$(OutputPath)java_runtime_trimmable_clr.jar">
<OutputJar>$(OutputPath)java_runtime_trimmable_clr.jar</OutputJar>
<OutputDex>$(OutputPath)java_runtime_trimmable_clr.dex</OutputDex>
<IntermediateRuntimeOutputPath>$(IntermediateOutputPath)release-trimmable-clr</IntermediateRuntimeOutputPath>
<IntermediateRuntimeClassesTxt>$(IntermediateOutputPath)release-trimmable-clr.txt</IntermediateRuntimeClassesTxt>
<RemoveItems>..\..\src-ThirdParty\bazel\java\mono\android\debug\MultiDexLoader.java;java\mono\android\debug-net6\BuildConfig.java;java\mono\android\debug\BuildConfig.java;java\mono\android\release\BuildConfig.java;java\mono\android\MonoPackageManager.java;$(JavaInteropSourceDirectory)\src\Java.Interop\java\net\dot\jni\internal\JavaProxyObject.java;$(JavaInteropSourceDirectory)\src\Java.Interop\java\net\dot\jni\internal\JavaProxyThrowable.java</RemoveItems>
<AddItems>java-trimmable\net\dot\jni\internal\JavaProxyObject.java;java-trimmable\net\dot\jni\internal\JavaProxyThrowable.java</AddItems>
</_RuntimeOutput>
<_RuntimeOutput Include="$(OutputPath)java_runtime_fastdev_clr.jar">
<OutputJar>$(OutputPath)java_runtime_fastdev_clr.jar</OutputJar>
<OutputDex>$(OutputPath)java_runtime_fastdev_clr.dex</OutputDex>
Expand All @@ -50,14 +66,17 @@

<Target Name="_BuildJavaRuntimeJar"
BeforeTargets="Build"
Inputs="@(AllRuntimeSource)"
Inputs="@(AllRuntimeSource);%(_RuntimeOutput.AddItems)"
Outputs="%(_RuntimeOutput.OutputJar)"
>
<MakeDir Directories="%(_RuntimeOutput.IntermediateRuntimeOutputPath)" />
<MakeDir Directories="$(OutputPath)" />
<!-- Remove stale D8 output so 'jar uf' doesn't bundle DEX into the runtime jar. -->
<Delete Files="%(_RuntimeOutput.IntermediateRuntimeOutputPath)\classes.dex" />
Comment thread
simonrozsival marked this conversation as resolved.
<ItemGroup>
<_RuntimeSource Include="@(AllRuntimeSource)" />
<_RuntimeSource Remove="%(_RuntimeOutput.RemoveItems)" />
<_RuntimeSource Include="%(_RuntimeOutput.AddItems)" Condition=" '%(_RuntimeOutput.AddItems)' != '' " />
</ItemGroup>
<WriteLinesToFile
File="%(_RuntimeOutput.IntermediateRuntimeClassesTxt)"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package net.dot.jni.internal;

import java.util.ArrayList;

import net.dot.jni.GCUserPeerable;

/* package */ final class JavaProxyObject
extends java.lang.Object
implements GCUserPeerable
{
ArrayList<Object> managedReferences = new ArrayList<Object>();

// This trimmable runtime copy cannot use Java.Interop's native object methods:
// those are registered through ManagedPeer.registerNativeMembers, which is not
// supported in the trimmable typemap path.
// Trimmable proxies use Java identity semantics: equals/hashCode/toString
// do not delegate to the wrapped .NET object.
@Override
public boolean equals(Object obj)
{
return this == obj;
}
Comment thread
simonrozsival marked this conversation as resolved.

@Override
public int hashCode()
{
return System.identityHashCode (this);
}

@Override
public String toString()
{
return super.toString ();
Comment thread
simonrozsival marked this conversation as resolved.
Outdated
}

public void jiAddManagedReference (java.lang.Object obj)
{
managedReferences.add (obj);
}

public void jiClearManagedReferences ()
{
managedReferences.clear ();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package net.dot.jni.internal;

import java.util.ArrayList;

import net.dot.jni.GCUserPeerable;

/* package */ final class JavaProxyThrowable
extends java.lang.Error
implements GCUserPeerable
{
ArrayList<Object> managedReferences = new ArrayList<Object>();

public JavaProxyThrowable () {
}

public JavaProxyThrowable (String message) {
Comment thread
simonrozsival marked this conversation as resolved.
Outdated
super (message);
}

public void jiAddManagedReference (java.lang.Object obj)
{
managedReferences.add (obj);
}

public void jiClearManagedReferences ()
{
managedReferences.clear ();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,69 @@ public void RegisteredPeer_CanCreateGenericHolder ()
Assert.AreEqual (42, holder.Value);
}

[Test]
public void JavaProxyObject_ValueMarshalerUsesProxyType ()
{
AssumeTrimmableTypeMapEnabled ();

var value = new object ();
var marshaler = JniEnvironment.Runtime.ValueManager.GetValueMarshaler (typeof (object));
var state = marshaler.CreateObjectReferenceArgumentState (value);

try {
Assert.AreEqual ("net/dot/jni/internal/JavaProxyObject", JNIEnv.GetClassNameFromInstance (state.ReferenceValue.Handle));
} finally {
marshaler.DestroyArgumentState (value, ref state);
}
}

[Test]
public void JavaProxyObject_CanBeUsedInObjectArray ()
{
AssumeTrimmableTypeMapEnabled ();

using var values = new JavaObjectArray<object> (1);
values [0] = new object ();

Assert.AreEqual ("[Ljava/lang/Object;", values.GetJniTypeName ());
}

[Test]
public void JavaProxyObject_ObjectMethodsUseJavaIdentitySemantics ()
{
AssumeTrimmableTypeMapEnabled ();

var value = new object ();
var other = new object ();
var marshaler = JniEnvironment.Runtime.ValueManager.GetValueMarshaler (typeof (object));
var state = marshaler.CreateObjectReferenceArgumentState (value);
var otherState = marshaler.CreateObjectReferenceArgumentState (other);

try {
IntPtr proxyClass = JNIEnv.GetObjectClass (state.ReferenceValue.Handle);
try {
IntPtr equals = JNIEnv.GetMethodID (proxyClass, "equals", "(Ljava/lang/Object;)Z");
IntPtr hashCode = JNIEnv.GetMethodID (proxyClass, "hashCode", "()I");
IntPtr toString = JNIEnv.GetMethodID (proxyClass, "toString", "()Ljava/lang/String;");

Assert.IsTrue (JNIEnv.CallBooleanMethod (state.ReferenceValue.Handle, equals, new JValue (state.ReferenceValue.Handle)));
Assert.IsFalse (JNIEnv.CallBooleanMethod (state.ReferenceValue.Handle, equals, new JValue (otherState.ReferenceValue.Handle)));
Assert.AreEqual (
JNIEnv.CallIntMethod (state.ReferenceValue.Handle, hashCode),
JNIEnv.CallIntMethod (state.ReferenceValue.Handle, hashCode));
var proxyString = JNIEnv.GetString (JNIEnv.CallObjectMethod (state.ReferenceValue.Handle, toString), JniHandleOwnership.TransferLocalRef);
Assert.IsTrue (
proxyString.StartsWith ("net.dot.jni.internal.JavaProxyObject@", StringComparison.Ordinal),
proxyString);
} finally {
Comment thread
simonrozsival marked this conversation as resolved.
JNIEnv.DeleteLocalRef (proxyClass);
}
} finally {
marshaler.DestroyArgumentState (other, ref otherState);
marshaler.DestroyArgumentState (value, ref state);
}
}

static ConcurrentDictionary<Type, JavaPeerProxy> GetProxyCache (TrimmableTypeMap instance)
{
var field = typeof (TrimmableTypeMap).GetField ("_proxyCache", BindingFlags.Instance | BindingFlags.NonPublic);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,33 +34,6 @@ protected NUnitInstrumentation(IntPtr handle, JniHandleOwnership transfer)
// net.dot.jni.test.CallVirtualFromConstructorDerived Java class not in APK
"Java.InteropTests.InvokeVirtualFromConstructorTests",

// net.dot.jni.internal.JavaProxyObject.<clinit> calls
// net.dot.jni.ManagedPeer.registerNativeMembers, which the trimmable
// typemap path rejects (Native methods must be registered by JCW
// static initializer blocks). Fixing this requires a parallel
// Android-trimmable variant of JavaProxyObject.java that registers
// its native equals/hashCode/toString via mono.android.Runtime.register
// — an architectural change tracked separately from the JavaCast / JavaAs
// work in this PR. See https://github.com/dotnet/android/issues/11170.
"Java.InteropTests.JavaObjectArray_object_ContractTest",

// Same root cause as above (JavaProxyObject static init).
"Java.InteropTests.JniValueMarshaler_object_ContractTests.JniValueMarshalerContractTests`1.CreateArgumentState",
"Java.InteropTests.JniValueMarshaler_object_ContractTests.JniValueMarshalerContractTests`1.CreateGenericArgumentState",
"Java.InteropTests.JniValueMarshaler_object_ContractTests.JniValueMarshalerContractTests`1.CreateGenericObjectReferenceArgumentState",
"Java.InteropTests.JniValueMarshaler_object_ContractTests.JniValueMarshalerContractTests`1.CreateGenericValue",
"Java.InteropTests.JniValueMarshaler_object_ContractTests.JniValueMarshalerContractTests`1.CreateObjectReferenceArgumentState",
"Java.InteropTests.JniValueMarshaler_object_ContractTests.JniValueMarshalerContractTests`1.CreateValue",
"Java.InteropTests.JniValueMarshaler_object_ContractTests.SpecificTypesAreUsed",

// net.dot.jni.test.GetThis static init — same JavaProxy*
// root cause as the JavaProxyObject exclusions above.
"Java.InteropTests.JavaObjectTest.DisposeAccessesThis",

// net.dot.jni.internal.JavaProxyThrowable static init — same JavaProxy*
// root cause as the JavaProxyObject exclusions above.
"Java.InteropTests.JavaExceptionTests.InnerExceptionIsNotAProxy",

// JNI method remapping not supported in trimmable typemap
"Java.InteropTests.JniPeerMembersTests.ReplaceInstanceMethodName",
"Java.InteropTests.JniPeerMembersTests.ReplaceInstanceMethodWithStaticMethod",
Expand Down