diff --git a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs index ccf1ac857a4..7f9585bb197 100644 --- a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs +++ b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs @@ -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; @@ -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. @@ -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 => { })); @@ -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); @@ -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 ( @@ -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 { @@ -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); } @@ -1180,6 +1239,31 @@ void EncodeUcoConstructorLocals_JavaInterop (BlobBuilder blob) blob.WriteCompressedInteger (CodedIndex.TypeDefOrRefOrSpec (_jniObjectReferenceRef)); } + /// + /// LOCAL_SIG for UCO constructors that invoke a no-arg managed constructor. + /// Locals: 0=JniTransition, 1=JniRuntime, 2=Exception, 3=JniObjectReference, 4=target type. + /// + 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 wrapperHandles) { diff --git a/src/Mono.Android/Java.Interop/JavaPeerProxy.cs b/src/Mono.Android/Java.Interop/JavaPeerProxy.cs index 88f7fa32244..190f1c597f7 100644 --- a/src/Mono.Android/Java.Interop/JavaPeerProxy.cs +++ b/src/Mono.Android/Java.Interop/JavaPeerProxy.cs @@ -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); + } } /// diff --git a/tests/Mono.Android-Tests/Mono.Android-Tests/Java.Interop/JnienvTest.cs b/tests/Mono.Android-Tests/Mono.Android-Tests/Java.Interop/JnienvTest.cs index 148c7dc9383..ae49c425e93 100644 --- a/tests/Mono.Android-Tests/Mono.Android-Tests/Java.Interop/JnienvTest.cs +++ b/tests/Mono.Android-Tests/Mono.Android-Tests/Java.Interop/JnienvTest.cs @@ -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, "", "()V"); @@ -325,7 +322,6 @@ public void ActivatedDirectThrowableSubclassesShouldBeRegistered () Assert.IsTrue (v.Constructed); v.Dispose (); } - Console.Error.WriteLine ($"# jonp: END ActivatedDirectThrowableSubclassesShouldBeRegistered!!!"); } [Test] diff --git a/tests/Mono.Android-Tests/Mono.Android-Tests/Java.Lang/ObjectTest.cs b/tests/Mono.Android-Tests/Mono.Android-Tests/Java.Lang/ObjectTest.cs index b4d921acd17..764fc416379 100644 --- a/tests/Mono.Android-Tests/Mono.Android-Tests/Java.Lang/ObjectTest.cs +++ b/tests/Mono.Android-Tests/Mono.Android-Tests/Java.Lang/ObjectTest.cs @@ -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 () { diff --git a/tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/NUnitInstrumentation.cs b/tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/NUnitInstrumentation.cs index d3783cdee0b..573940a6250 100644 --- a/tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/NUnitInstrumentation.cs +++ b/tests/Mono.Android-Tests/Mono.Android-Tests/Xamarin.Android.RuntimeTests/NUnitInstrumentation.cs @@ -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", };