From 5d88e2397498ca96ceac8adad4df8184ce7074e1 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 6 May 2026 08:58:25 +0000 Subject: [PATCH 1/3] Initial plan From 612be1dc8dc92c2d598dd44f2dc675159a75fd91 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 6 May 2026 09:47:11 +0000 Subject: [PATCH 2/3] Fix trimmable constructor UCO generation Agent-Logs-Url: https://github.com/dotnet/android/sessions/2a0c90c9-484f-417c-875c-bea6afda1c98 Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> --- .../Generator/MetadataHelper.cs | 15 ++ .../Generator/Model/TypeMapAssemblyData.cs | 12 ++ .../Generator/ModelBuilder.cs | 5 + .../Generator/TypeMapAssemblyEmitter.cs | 139 ++++++++++++++++++ .../Scanner/JavaPeerInfo.cs | 44 ++++++ .../Scanner/JavaPeerScanner.cs | 37 ++++- .../TypeMapAssemblyGeneratorTests.cs | 54 +++++++ 7 files changed, 301 insertions(+), 5 deletions(-) diff --git a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/MetadataHelper.cs b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/MetadataHelper.cs index 93f8ce5c5fc..c01a04dec06 100644 --- a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/MetadataHelper.cs +++ b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/MetadataHelper.cs @@ -42,6 +42,21 @@ public static byte [] ComputeContentFingerprint (TypeMapAssemblyData data) writer.Write (proxy.TargetType.AssemblyName); writer.Write ((byte)(proxy.ActivationCtor?.Style ?? 0)); writer.Write ((byte)(proxy.InvokerActivationCtorStyle ?? 0)); + foreach (var ctor in proxy.UcoConstructors) { + writer.Write (ctor.WrapperName); + writer.Write (ctor.JniSignature); + writer.Write (ctor.ConstructorDeclaringType?.ManagedTypeName ?? ""); + writer.Write (ctor.ConstructorDeclaringType?.AssemblyName ?? ""); + if (ctor.ManagedParameterTypes is null) { + writer.Write (0); + } else { + writer.Write (ctor.ManagedParameterTypes.Count); + foreach (var parameter in ctor.ManagedParameterTypes) { + writer.Write (parameter.ManagedTypeName); + writer.Write (parameter.AssemblyName); + } + } + } } foreach (var assoc in data.Associations) { writer.Write (assoc.SourceTypeReference); diff --git a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/Model/TypeMapAssemblyData.cs b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/Model/TypeMapAssemblyData.cs index b9126586bf4..18b5e919223 100644 --- a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/Model/TypeMapAssemblyData.cs +++ b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/Model/TypeMapAssemblyData.cs @@ -228,6 +228,18 @@ sealed record UcoConstructorData /// JNI constructor signature, e.g., "(Landroid/content/Context;)V". Used for RegisterNatives registration. /// public required string JniSignature { get; init; } + + /// + /// Managed constructor parameter types. Null when this UCO constructor should + /// fall back to activation-only behavior because no concrete managed constructor + /// was identified by the scanner. + /// + public IReadOnlyList? ManagedParameterTypes { get; init; } + + /// + /// Managed type that declares the constructor to call. + /// + public TypeRefData? ConstructorDeclaringType { get; init; } } /// diff --git a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.cs b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.cs index 7547c5ac38f..f0737eaff31 100644 --- a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.cs +++ b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.cs @@ -365,6 +365,11 @@ static void BuildUcoConstructors (JavaPeerInfo peer, JavaPeerProxyData proxy) ManagedTypeName = peer.ManagedTypeName, AssemblyName = peer.AssemblyName, }, + ManagedParameterTypes = ctor.ManagedParameterTypes, + ConstructorDeclaringType = ctor.ManagedParameterTypes is null ? null : new TypeRefData { + ManagedTypeName = !ctor.ConstructorDeclaringTypeName.IsNullOrEmpty () ? ctor.ConstructorDeclaringTypeName : peer.ManagedTypeName, + AssemblyName = !ctor.ConstructorDeclaringAssemblyName.IsNullOrEmpty () ? ctor.ConstructorDeclaringAssemblyName : peer.AssemblyName, + }, }); } } diff --git a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs index af36173550f..af0a79e7fb6 100644 --- a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs +++ b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs @@ -78,6 +78,7 @@ sealed class TypeMapAssemblyEmitter TypeReferenceHandle _javaPeerProxyRef; TypeReferenceHandle _javaPeerProxyNonGenericRef; + TypeReferenceHandle _javaConvertRef; TypeReferenceHandle _iJavaPeerableRef; TypeReferenceHandle _jniHandleOwnershipRef; TypeReferenceHandle _jniObjectReferenceRef; @@ -97,6 +98,8 @@ sealed class TypeMapAssemblyEmitter MemberReferenceHandle _getUninitializedObjectRef; MemberReferenceHandle _notSupportedExceptionCtorRef; MemberReferenceHandle _jniObjectReferenceCtorRef; + MemberReferenceHandle _iJavaPeerableSetPeerReferenceRef; + MemberReferenceHandle _javaConvertFromJniHandleRef; MemberReferenceHandle _jniEnvDeleteRefRef; MemberReferenceHandle _shouldSkipActivationRef; MemberReferenceHandle _waitForBridgeProcessingRef; @@ -220,6 +223,8 @@ void EmitTypeReferences () metadata.GetOrAddString ("Java.Interop"), metadata.GetOrAddString ("JavaPeerProxy`1")); _javaPeerProxyNonGenericRef = metadata.AddTypeReference (_pe.MonoAndroidRef, metadata.GetOrAddString ("Java.Interop"), metadata.GetOrAddString ("JavaPeerProxy")); + _javaConvertRef = metadata.AddTypeReference (_pe.MonoAndroidRef, + metadata.GetOrAddString ("Java.Interop"), metadata.GetOrAddString ("JavaConvert")); _iJavaPeerableRef = metadata.AddTypeReference (_javaInteropRef, metadata.GetOrAddString ("Java.Interop"), metadata.GetOrAddString ("IJavaPeerable")); _jniHandleOwnershipRef = metadata.AddTypeReference (_pe.MonoAndroidRef, @@ -340,6 +345,19 @@ void EmitMemberReferences () p.AddParameter ().Type ().Type (_jniObjectReferenceTypeRef, true); })); + _iJavaPeerableSetPeerReferenceRef = _pe.AddMemberRef (_iJavaPeerableRef, "SetPeerReference", + sig => sig.MethodSignature (isInstanceMethod: true).Parameters (1, + rt => rt.Void (), + p => p.AddParameter ().Type ().Type (_jniObjectReferenceRef, true))); + + _javaConvertFromJniHandleRef = _pe.AddMemberRef (_javaConvertRef, "FromJniHandle", + sig => sig.MethodSignature (genericParameterCount: 1).Parameters (2, + rt => rt.Type ().GenericMethodTypeParameter (0), + p => { + p.AddParameter ().Type ().IntPtr (); + p.AddParameter ().Type ().Type (_jniHandleOwnershipRef, 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. @@ -921,6 +939,18 @@ MemberReferenceHandle AddActivationCtorRef (EntityHandle declaringTypeRef) })); } + MemberReferenceHandle AddManagedConstructorRef (EntityHandle declaringTypeRef, IReadOnlyList parameterTypes) + { + return _pe.AddMemberRef (declaringTypeRef, ".ctor", + sig => sig.MethodSignature (isInstanceMethod: true).Parameters (parameterTypes.Count, + rt => rt.Void (), + p => { + foreach (var parameterType in parameterTypes) { + EncodeManagedType (p.AddParameter ().Type (), parameterType); + } + })); + } + MethodDefinitionHandle EmitUcoMethod (UcoMethodData uco, JavaPeerProxyData proxy) { var jniParams = JniSignatureHelper.ParseParameterTypes (uco.JniSignature); @@ -1045,6 +1075,20 @@ MethodDefinitionHandle EmitUcoConstructor (UcoConstructorData uco, JavaPeerProxy return openGenericHandle; } + if (uco.ManagedParameterTypes is not null && uco.ConstructorDeclaringType is not null) { + var ctorDeclaringTypeRef = _pe.ResolveTypeRef (uco.ConstructorDeclaringType); + var ctorRef = AddManagedConstructorRef (ctorDeclaringTypeRef, uco.ManagedParameterTypes); + var directHandle = _pe.EmitBody (uco.WrapperName, + MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig, + encodeSig, + (encoder, cfb) => EmitUcoConstructorBodyWithMarshal (encoder, cfb, enc => { + EmitManagedConstructorActivation (enc, targetTypeRef, ctorRef, uco.ManagedParameterTypes, jniParams); + }), + EncodeUcoConstructorLocals_Standard); + AddUnmanagedCallersOnlyAttribute (directHandle); + return directHandle; + } + MethodDefinitionHandle handle; if (activationCtor.Style == ActivationCtorStyle.JavaInterop) { var ctorRef = AddJavaInteropActivationCtorRef ( @@ -1123,6 +1167,37 @@ MethodDefinitionHandle EmitUcoConstructor (UcoConstructorData uco, JavaPeerProxy return handle; } + void EmitManagedConstructorActivation (InstructionEncoder encoder, EntityHandle targetTypeRef, + MemberReferenceHandle ctorRef, IReadOnlyList managedParameterTypes, + IReadOnlyList jniParameterTypes) + { + encoder.OpCode (ILOpCode.Ldtoken); + encoder.Token (targetTypeRef); + encoder.Call (_getTypeFromHandleRef); + encoder.Call (_getUninitializedObjectRef); + encoder.OpCode (ILOpCode.Castclass); + encoder.Token (targetTypeRef); + encoder.OpCode (ILOpCode.Dup); + + encoder.LoadArgument (1); // self + encoder.LoadConstantI4 (0); // JniObjectReferenceType.Invalid + encoder.OpCode (ILOpCode.Newobj); + encoder.Token (_jniObjectReferenceCtorRef); + encoder.OpCode (ILOpCode.Callvirt); + encoder.Token (_iJavaPeerableSetPeerReferenceRef); + + for (int i = 0; i < managedParameterTypes.Count; i++) { + encoder.LoadArgument (i + 2); + if (jniParameterTypes [i] == JniParamKind.Object) { + encoder.LoadConstantI4 (0); // JniHandleOwnership.DoNotTransfer + encoder.OpCode (ILOpCode.Call); + encoder.Token (MakeJavaConvertFromJniHandleSpec (managedParameterTypes [i])); + } + } + + encoder.Call (ctorRef); + } + /// /// Emits the common try/catch/finally marshal-method wrapper pattern used by all /// non-generic UCO constructor bodies: @@ -1425,6 +1500,70 @@ TypeSpecificationHandle MakeGenericTypeSpec_ValueType (EntityHandle openType, En return _pe.Metadata.AddTypeSpecification (_pe.Metadata.GetOrAddBlob (sigBlob)); } + MethodSpecificationHandle MakeJavaConvertFromJniHandleSpec (ManagedParameterInfo type) + { + var blob = new BlobBuilder (32); + blob.WriteByte (0x0A); // GENMETHOD_INST + blob.WriteCompressedInteger (1); + EncodeManagedType (blob, type); + return _pe.Metadata.AddMethodSpecification (_javaConvertFromJniHandleRef, _pe.Metadata.GetOrAddBlob (blob)); + } + + void EncodeManagedType (SignatureTypeEncoder encoder, ManagedParameterInfo type) + { + EncodeManagedType (encoder.Builder, type); + } + + void EncodeManagedType (BlobBuilder builder, ManagedParameterInfo type) + { + string managedType = type.ManagedTypeName; + if (managedType.EndsWith ("[]", StringComparison.Ordinal)) { + builder.WriteByte (0x1D); // ELEMENT_TYPE_SZARRAY + EncodeManagedType (builder, new ManagedParameterInfo { + ManagedTypeName = managedType.Substring (0, managedType.Length - 2), + AssemblyName = type.AssemblyName, + }); + return; + } + + if (TryEncodePrimitiveManagedType (builder, managedType)) { + return; + } + + var typeRef = _pe.ResolveTypeRef (new TypeRefData { + ManagedTypeName = managedType, + AssemblyName = type.AssemblyName, + }); + builder.WriteByte (0x12); // ELEMENT_TYPE_CLASS + builder.WriteCompressedInteger (CodedIndex.TypeDefOrRefOrSpec (typeRef)); + } + + static bool TryEncodePrimitiveManagedType (BlobBuilder builder, string managedType) + { + byte typeCode = managedType switch { + "System.Boolean" => 0x02, + "System.Char" => 0x03, + "System.SByte" => 0x04, + "System.Byte" => 0x05, + "System.Int16" => 0x06, + "System.UInt16" => 0x07, + "System.Int32" => 0x08, + "System.UInt32" => 0x09, + "System.Int64" => 0x0A, + "System.UInt64" => 0x0B, + "System.Single" => 0x0C, + "System.Double" => 0x0D, + "System.String" => 0x0E, + "System.IntPtr" => 0x18, + _ => 0, + }; + if (typeCode == 0) { + return false; + } + builder.WriteByte (typeCode); + return true; + } + /// /// Encodes ReadOnlySpan<JniNativeMethod> directly into a signature type encoder. /// Required because doesn't accept TypeSpec handles. diff --git a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerInfo.cs b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerInfo.cs index ee285b42798..af11ed0dba5 100644 --- a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerInfo.cs +++ b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerInfo.cs @@ -224,6 +224,12 @@ public sealed record MarshalMethodInfo /// public string? SuperArgumentsString { get; init; } + /// + /// Managed constructor parameter types, when this marshal entry represents a + /// concrete managed constructor. Null for inherited seed constructors. + /// + public IReadOnlyList? ManagedParameterTypes { get; init; } + /// /// True if this method was collected from an implemented interface /// (Pass 4: CollectInterfaceMethodImplementations), not from the type itself. @@ -267,6 +273,44 @@ public sealed record JavaConstructorInfo /// Null for [Register] constructors. /// public string? SuperArgumentsString { get; init; } + + /// + /// Managed constructor parameter types, when this Java constructor corresponds to + /// a concrete managed constructor. Null for inherited seed constructors that do + /// not identify a constructor on the target type. + /// + public IReadOnlyList? ManagedParameterTypes { get; init; } + + /// + /// Managed type that declares the concrete constructor to call. + /// Null when is null. + /// + public string? ConstructorDeclaringTypeName { get; init; } + + /// + /// Assembly containing . + /// Null when is null. + /// + public string? ConstructorDeclaringAssemblyName { get; init; } +} + +/// +/// Describes a managed parameter type for generated direct constructor calls. +/// +public sealed record ManagedParameterInfo +{ + /// + /// Full managed type name, e.g. "System.String" or "Android.Content.Context". + /// Array types use C#-style "[]" suffixes. + /// + public required string ManagedTypeName { get; init; } + + /// + /// Assembly containing the parameter type, when a TypeRef is needed. + /// Primitive types and System.String leave this empty because they are encoded + /// directly in metadata signatures. + /// + public string AssemblyName { get; init; } = ""; } /// diff --git a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs index dda7460271c..0fb65eb5c4d 100644 --- a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs +++ b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs @@ -610,11 +610,12 @@ void CollectBaseConstructorChain (TypeDefinition typeDef, AssemblyIndex index, JniName = ".ctor", JniSignature = jniSignature, Connector = "", - ManagedMethodName = ".ctor", - NativeCallbackName = "n_ctor", - IsConstructor = true, - SuperArgumentsString = "", - }); + ManagedMethodName = ".ctor", + NativeCallbackName = "n_ctor", + IsConstructor = true, + SuperArgumentsString = "", + ManagedParameterTypes = ToManagedParameterInfos (sig.ParameterTypes, index.AssemblyName), + }); alreadyRegisteredSignatures.Add (jniSignature); } } @@ -896,6 +897,7 @@ static void AddMarshalMethod (List methods, RegisterInfo regi string declaringTypeName = ""; string declaringAssemblyName = ""; ParseConnectorDeclaringType (registerInfo.Connector, out declaringTypeName, out declaringAssemblyName); + var sig = methodDef.DecodeSignature (SignatureTypeProvider.Instance, genericContext: default); methods.Add (new MarshalMethodInfo { JniName = registerInfo.JniName, @@ -911,9 +913,31 @@ static void AddMarshalMethod (List methods, RegisterInfo regi JavaAccess = isExport ? GetJavaAccess (methodDef.Attributes & MethodAttributes.MemberAccessMask) : null, ThrownNames = exportInfo?.ThrownNames, SuperArgumentsString = exportInfo?.SuperArgumentsString, + ManagedParameterTypes = isConstructor ? ToManagedParameterInfos (sig.ParameterTypes, index.AssemblyName) : null, }); } + static ManagedParameterInfo [] ToManagedParameterInfos (IReadOnlyList parameterTypes, string defaultAssemblyName) + { + var result = new ManagedParameterInfo [parameterTypes.Count]; + for (int i = 0; i < parameterTypes.Count; i++) { + string parameterType = parameterTypes [i]; + result [i] = new ManagedParameterInfo { + ManagedTypeName = parameterType, + AssemblyName = NeedsTypeReference (parameterType) ? defaultAssemblyName : "", + }; + } + return result; + } + + static bool NeedsTypeReference (string managedType) + { + while (managedType.EndsWith ("[]", StringComparison.Ordinal)) { + managedType = managedType.Substring (0, managedType.Length - 2); + } + return TryGetPrimitiveJniDescriptor (managedType) is null; + } + static string GetJavaAccess (MethodAttributes access) { return access switch { @@ -1553,6 +1577,9 @@ static List BuildJavaConstructors (List JniSignature = mm.JniSignature, ConstructorIndex = ctorIndex, SuperArgumentsString = mm.SuperArgumentsString, + ManagedParameterTypes = mm.ManagedParameterTypes, + ConstructorDeclaringTypeName = mm.ManagedParameterTypes is null ? null : mm.DeclaringTypeName, + ConstructorDeclaringAssemblyName = mm.ManagedParameterTypes is null ? null : mm.DeclaringAssemblyName, }); ctorIndex++; } diff --git a/tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapAssemblyGeneratorTests.cs b/tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapAssemblyGeneratorTests.cs index ec37cbff9af..b81e475c95d 100644 --- a/tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapAssemblyGeneratorTests.cs +++ b/tests/Microsoft.Android.Sdk.TrimmableTypeMap.Tests/Generator/TypeMapAssemblyGeneratorTests.cs @@ -1109,6 +1109,60 @@ public void Generate_AliasHolder_HasDeserializableAliasKeys () } } + [Fact] + public void Generate_UcoConstructor_WithManagedParameters_CallsMatchingConstructor () + { + var peer = FindFixtureByJavaName ("my/app/ActivityWithCustomCtor"); + Assert.Contains (peer.JavaConstructors, c => c.JniSignature == "(Ljava/lang/String;)V"); + + using var stream = GenerateAssembly (new [] { peer }, "UcoCtorManagedString"); + using var pe = new PEReader (stream); + var reader = pe.GetMetadataReader (); + + var stringCtor = FindCtorMemberRef (reader, "MyApp", "ActivityWithCustomCtor", "System.String"); + var activationCtors = FindCtorMemberRefs (reader, "MyApp", "ActivityWithCustomCtor", + "System.IntPtr", "Android.Runtime.JniHandleOwnership"); + + var nctorMethod = reader.GetMethodDefinition (FindMethodDefinition (reader, "nctor_1_uco")); + var body = pe.GetMethodBody (nctorMethod.RelativeVirtualAddress); + var ilBytes = body.GetILBytes (); + Assert.NotNull (ilBytes); + Assert.True (ILContainsCallToken (ilBytes, MetadataTokens.GetToken (stringCtor)), + "nctor_1_uco should call ActivityWithCustomCtor(string), not the activation constructor."); + foreach (var activationCtor in activationCtors) { + Assert.False (ILContainsCallToken (ilBytes, MetadataTokens.GetToken (activationCtor)), + "nctor_1_uco should not call the activation constructor."); + } + } + + [Fact] + public void Generate_UcoConstructor_ObjectParameter_ConvertsJniHandleBeforeConstructorCall () + { + var peer = FindFixtureByJavaName ("my/app/CustomView"); + Assert.Contains (peer.JavaConstructors, c => c.JniSignature == "(Landroid/content/Context;)V"); + + using var stream = GenerateAssembly (new [] { peer }, "UcoCtorManagedObject"); + using var pe = new PEReader (stream); + var reader = pe.GetMetadataReader (); + + var contextCtor = FindCtorMemberRef (reader, "MyApp", "CustomView", "Android.Content.Context"); + var javaConvertFromJniHandle = Enumerable.Range (1, reader.GetTableRowCount (TableIndex.MemberRef)) + .Select (i => MetadataTokens.MemberReferenceHandle (i)) + .Single (h => reader.GetString (reader.GetMemberReference (h).Name) == "FromJniHandle"); + var javaConvertFromJniHandleSpec = Enumerable.Range (1, reader.GetTableRowCount (TableIndex.MethodSpec)) + .Select (i => MetadataTokens.MethodSpecificationHandle (i)) + .Single (h => reader.GetMethodSpecification (h).Method == javaConvertFromJniHandle); + var nctorMethod = reader.GetMethodDefinition (FindMethodDefinition (reader, "nctor_1_uco")); + var body = pe.GetMethodBody (nctorMethod.RelativeVirtualAddress); + var ilBytes = body.GetILBytes (); + Assert.NotNull (ilBytes); + + Assert.True (ILContainsCallToken (ilBytes, MetadataTokens.GetToken (javaConvertFromJniHandleSpec)), + "nctor_1_uco should convert the JNI Context handle to the managed Context parameter."); + Assert.True (ILContainsCallToken (ilBytes, MetadataTokens.GetToken (contextCtor)), + "nctor_1_uco should call CustomView(Context), not the activation constructor."); + } + [Fact] public void Generate_UcoConstructor_HasMarshalMethodMetadataAndExceptionRegions () { From d3d3f514cfd414bf9f15cd9646a60d6ee6ddac2d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Wed, 6 May 2026 09:51:28 +0000 Subject: [PATCH 3/3] Address constructor UCO review feedback Agent-Logs-Url: https://github.com/dotnet/android/sessions/2a0c90c9-484f-417c-875c-bea6afda1c98 Co-authored-by: simonrozsival <374616+simonrozsival@users.noreply.github.com> --- .../Generator/TypeMapAssemblyEmitter.cs | 10 ++++------ .../Scanner/JavaPeerScanner.cs | 18 +++++++++++++----- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs index af0a79e7fb6..375bea78501 100644 --- a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs +++ b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs @@ -1518,12 +1518,10 @@ void EncodeManagedType (BlobBuilder builder, ManagedParameterInfo type) { string managedType = type.ManagedTypeName; if (managedType.EndsWith ("[]", StringComparison.Ordinal)) { - builder.WriteByte (0x1D); // ELEMENT_TYPE_SZARRAY - EncodeManagedType (builder, new ManagedParameterInfo { - ManagedTypeName = managedType.Substring (0, managedType.Length - 2), - AssemblyName = type.AssemblyName, - }); - return; + do { + builder.WriteByte (0x1D); // ELEMENT_TYPE_SZARRAY + managedType = managedType.Substring (0, managedType.Length - 2); + } while (managedType.EndsWith ("[]", StringComparison.Ordinal)); } if (TryEncodePrimitiveManagedType (builder, managedType)) { diff --git a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs index 0fb65eb5c4d..801357a321f 100644 --- a/src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs +++ b/src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs @@ -882,7 +882,7 @@ static bool HaveIdenticalParameterTypes (MethodDefinition method1, MethodDefinit return true; } - static void AddMarshalMethod (List methods, RegisterInfo registerInfo, MethodDefinition methodDef, AssemblyIndex index, ExportInfo? exportInfo = null, bool isInterfaceImplementation = false) + void AddMarshalMethod (List methods, RegisterInfo registerInfo, MethodDefinition methodDef, AssemblyIndex index, ExportInfo? exportInfo = null, bool isInterfaceImplementation = false) { // Skip methods that are just the JNI name (type-level [Register]) if (registerInfo.Signature is null && registerInfo.Connector is null) { @@ -917,25 +917,33 @@ static void AddMarshalMethod (List methods, RegisterInfo regi }); } - static ManagedParameterInfo [] ToManagedParameterInfos (IReadOnlyList parameterTypes, string defaultAssemblyName) + ManagedParameterInfo [] ToManagedParameterInfos (IReadOnlyList parameterTypes, string defaultAssemblyName) { var result = new ManagedParameterInfo [parameterTypes.Count]; for (int i = 0; i < parameterTypes.Count; i++) { string parameterType = parameterTypes [i]; result [i] = new ManagedParameterInfo { ManagedTypeName = parameterType, - AssemblyName = NeedsTypeReference (parameterType) ? defaultAssemblyName : "", + AssemblyName = ResolveManagedParameterAssembly (parameterType, defaultAssemblyName), }; } return result; } - static bool NeedsTypeReference (string managedType) + string ResolveManagedParameterAssembly (string managedType, string defaultAssemblyName) { while (managedType.EndsWith ("[]", StringComparison.Ordinal)) { managedType = managedType.Substring (0, managedType.Length - 2); } - return TryGetPrimitiveJniDescriptor (managedType) is null; + if (TryGetPrimitiveJniDescriptor (managedType) is not null) { + return ""; + } + foreach (var assembly in assemblyCache.Values) { + if (assembly.TypesByFullName.ContainsKey (managedType)) { + return assembly.AssemblyName; + } + } + return defaultAssemblyName; } static string GetJavaAccess (MethodAttributes access)