Skip to content
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -97,8 +97,10 @@ sealed class TypeMapAssemblyEmitter
MemberReferenceHandle _getUninitializedObjectRef;
MemberReferenceHandle _notSupportedExceptionCtorRef;
MemberReferenceHandle _jniObjectReferenceCtorRef;
MemberReferenceHandle _setPeerReferenceRef;
MemberReferenceHandle _jniEnvDeleteRefRef;
MemberReferenceHandle _shouldSkipActivationRef;
MemberReferenceHandle _markActivationPeerReplaceableRef;
MemberReferenceHandle _waitForBridgeProcessingRef;
MemberReferenceHandle _androidEnvironmentUnhandledExceptionRef;
MemberReferenceHandle _ucoAttrCtorRef;
Expand Down Expand Up @@ -308,6 +310,11 @@ void EmitMemberReferences ()
p.AddParameter ().Type ().Type (_jniObjectReferenceTypeRef, true);
}));

_setPeerReferenceRef = _pe.AddMemberRef (_iJavaPeerableRef, "SetPeerReference",
sig => sig.MethodSignature (isInstanceMethod: true).Parameters (1,
rt => rt.Void (),
p => p.AddParameter ().Type ().Type (_jniObjectReferenceRef, true)));

// JNIEnv.DeleteRef(IntPtr, JniHandleOwnership) — static, internal
// Used by JI-style activation to clean up the original handle after constructing the peer.
// Matches the legacy TypeManager.CreateProxy behavior.
Expand All @@ -325,6 +332,11 @@ void EmitMemberReferences ()
rt => rt.Type ().Boolean (),
p => { p.AddParameter ().Type ().IntPtr (); }));

_markActivationPeerReplaceableRef = _pe.AddMemberRef (_javaPeerProxyNonGenericRef, "MarkActivationPeerReplaceable",
sig => sig.MethodSignature ().Parameters (1,
rt => rt.Void (),
p => p.AddParameter ().Type ().IntPtr ()));

_waitForBridgeProcessingRef = _pe.AddMemberRef (_androidRuntimeInternalRef, "WaitForBridgeProcessing",
sig => sig.MethodSignature ().Parameters (0, rt => rt.Void (), p => { }));

Expand Down Expand Up @@ -868,6 +880,14 @@ MemberReferenceHandle AddActivationCtorRef (EntityHandle declaringTypeRef)
}));
}

MemberReferenceHandle AddDefaultCtorRef (EntityHandle declaringTypeRef)
{
return _pe.AddMemberRef (declaringTypeRef, ".ctor",
sig => sig.MethodSignature (isInstanceMethod: true).Parameters (0,
rt => rt.Void (),
p => { }));
}

MethodDefinitionHandle EmitUcoMethod (UcoMethodData uco, JavaPeerProxyData proxy)
{
var jniParams = JniSignatureHelper.ParseParameterTypes (uco.JniSignature);
Expand Down Expand Up @@ -988,6 +1008,41 @@ MethodDefinitionHandle EmitUcoConstructor (UcoConstructorData uco, JavaPeerProxy
return noopHandle;
}

if (jniParams.Count == 0) {
var defaultCtorRef = AddDefaultCtorRef (targetTypeRef);
var defaultCtorHandle = _pe.EmitBody (uco.WrapperName,
MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig,
encodeSig,
(encoder, cfb) => EmitUcoConstructorBodyWithMarshal (encoder, cfb, enc => {
enc.OpCode (ILOpCode.Ldtoken);
enc.Token (targetTypeRef);
enc.Call (_getTypeFromHandleRef);
enc.Call (_getUninitializedObjectRef);
enc.OpCode (ILOpCode.Castclass);
enc.Token (targetTypeRef);
enc.StoreLocal (4);

enc.LoadLocalAddress (3); // jniRef
enc.LoadArgument (1); // self
enc.LoadConstantI4 (0); // JniObjectReferenceType.Invalid
enc.Call (_jniObjectReferenceCtorRef);

enc.LoadLocal (4);
enc.LoadLocal (3);
enc.OpCode (ILOpCode.Callvirt);
enc.Token (_setPeerReferenceRef);

enc.LoadLocal (4);
enc.Call (defaultCtorRef);

enc.LoadArgument (1); // self
enc.Call (_markActivationPeerReplaceableRef);
}),
blob => EncodeUcoConstructorLocals_DefaultConstructor (blob, targetTypeRef));
AddUnmanagedCallersOnlyAttribute (defaultCtorHandle);
return defaultCtorHandle;
}

MethodDefinitionHandle handle;
if (activationCtor.Style == ActivationCtorStyle.JavaInterop) {
var ctorRef = AddJavaInteropActivationCtorRef (
Expand Down Expand Up @@ -1027,6 +1082,8 @@ MethodDefinitionHandle EmitUcoConstructor (UcoConstructorData uco, JavaPeerProxy
enc.LoadConstantI4 (1); // JniObjectReferenceOptions.Copy
enc.Call (ctorRef);
}
enc.LoadArgument (1); // self
enc.Call (_markActivationPeerReplaceableRef);
}),
EncodeUcoConstructorLocals_JavaInterop);
} else {
Expand Down Expand Up @@ -1059,6 +1116,8 @@ MethodDefinitionHandle EmitUcoConstructor (UcoConstructorData uco, JavaPeerProxy
enc.LoadConstantI4 (0); // JniHandleOwnership.DoNotTransfer
enc.Call (ctorRef);
}
enc.LoadArgument (1); // self
enc.Call (_markActivationPeerReplaceableRef);
}),
EncodeUcoConstructorLocals_Standard);
}
Expand Down Expand Up @@ -1180,6 +1239,31 @@ void EncodeUcoConstructorLocals_JavaInterop (BlobBuilder blob)
blob.WriteCompressedInteger (CodedIndex.TypeDefOrRefOrSpec (_jniObjectReferenceRef));
}

/// <summary>
/// LOCAL_SIG for UCO constructors that invoke a no-arg managed constructor.
/// Locals: 0=JniTransition, 1=JniRuntime, 2=Exception, 3=JniObjectReference, 4=target type.
/// </summary>
void EncodeUcoConstructorLocals_DefaultConstructor (BlobBuilder blob, EntityHandle targetTypeRef)
{
blob.WriteByte (0x07); // LOCAL_SIG
blob.WriteCompressedInteger (5);
// local 0: JniTransition (valuetype)
blob.WriteByte (0x11); // ELEMENT_TYPE_VALUETYPE
blob.WriteCompressedInteger (CodedIndex.TypeDefOrRefOrSpec (_jniTransitionRef));
// local 1: JniRuntime (class)
blob.WriteByte (0x12); // ELEMENT_TYPE_CLASS
blob.WriteCompressedInteger (CodedIndex.TypeDefOrRefOrSpec (_jniRuntimeRef));
// local 2: Exception (class)
blob.WriteByte (0x12); // ELEMENT_TYPE_CLASS
blob.WriteCompressedInteger (CodedIndex.TypeDefOrRefOrSpec (_exceptionRef));
// local 3: JniObjectReference (valuetype)
blob.WriteByte (0x11); // ELEMENT_TYPE_VALUETYPE
blob.WriteCompressedInteger (CodedIndex.TypeDefOrRefOrSpec (_jniObjectReferenceRef));
// local 4: target type (class)
blob.WriteByte (0x12); // ELEMENT_TYPE_CLASS
blob.WriteCompressedInteger (CodedIndex.TypeDefOrRefOrSpec (targetTypeRef));
}

void EmitRegisterNatives (JavaPeerProxyData proxy,
Dictionary<string, MethodDefinitionHandle> wrapperHandles)
{
Expand Down
11 changes: 11 additions & 0 deletions src/Mono.Android/Java.Interop/JavaPeerProxy.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,17 @@ public static bool ShouldSkipActivation (IntPtr jniSelf)
return (state & JniManagedPeerStates.Activatable) != JniManagedPeerStates.Activatable
&& (state & JniManagedPeerStates.Replaceable) != JniManagedPeerStates.Replaceable;
}

public static void MarkActivationPeerReplaceable (IntPtr jniSelf)
{
var reference = new JniObjectReference (jniSelf, JniObjectReferenceType.Invalid);
var peer = JniEnvironment.Runtime.ValueManager.PeekPeer (reference);
if (peer == null) {
return;
}

peer.SetJniManagedPeerState (peer.JniManagedPeerState | JniManagedPeerStates.Replaceable);
}
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -302,14 +302,11 @@ public void ActivatedDirectObjectSubclassesShouldBeRegistered ()
}
}

// TODO: https://github.com/dotnet/android/issues/11170 — throwable subclass not registered under trimmable typemap
[Test]
public void ActivatedDirectThrowableSubclassesShouldBeRegistered ()
{
if (Build.VERSION.SdkInt <= BuildVersionCodes.GingerbreadMr1)
Assert.Ignore ("Skipping test due to Bug #34141");

Console.Error.WriteLine ($"# jonp: BEGIN ActivatedDirectThrowableSubclassesShouldBeRegistered!!!");

using (var ThrowableActivatedFromJava_class = Java.Lang.Class.FromType (typeof (ThrowableActivatedFromJava))) {
var ThrowableActivatedFromJava_init = JNIEnv.GetMethodID (ThrowableActivatedFromJava_class.Handle, "<init>", "()V");
Expand All @@ -325,7 +322,6 @@ public void ActivatedDirectThrowableSubclassesShouldBeRegistered ()
Assert.IsTrue (v.Constructed);
v.Dispose ();
}
Console.Error.WriteLine ($"# jonp: END ActivatedDirectThrowableSubclassesShouldBeRegistered!!!");
}

[Test]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ namespace Java.LangTests
[TestFixture]
public class ObjectTest
{
// TODO: https://github.com/dotnet/android/issues/11170 — trimmable typemap doesn't resolve most-derived managed type
[Test]
public void GetObject_ReturnsMostDerivedType ()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,12 +43,6 @@ protected NUnitInstrumentation(IntPtr handle, JniHandleOwnership transfer)
// Open generic type handling differs from non-trimmable
"Java.InteropTests.JnienvTest.NewOpenGenericTypeThrows",

// Throwable subclass registration
"Java.InteropTests.JnienvTest.ActivatedDirectThrowableSubclassesShouldBeRegistered",

// Instance identity after JNI round-trip
"Java.LangTests.ObjectTest.JnienvCreateInstance_RegistersMultipleInstances",

// Global ref leak when inflating custom views
"Xamarin.Android.RuntimeTests.CustomWidgetTests.InflateCustomView_ShouldNotLeakGlobalRefs",
};
Expand Down