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 @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,18 @@ sealed record UcoConstructorData
/// JNI constructor signature, e.g., "(Landroid/content/Context;)V". Used for RegisterNatives registration.
/// </summary>
public required string JniSignature { get; init; }

/// <summary>
/// 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.
/// </summary>
public IReadOnlyList<ManagedParameterInfo>? ManagedParameterTypes { get; init; }

/// <summary>
/// Managed type that declares the constructor to call.
/// </summary>
public TypeRefData? ConstructorDeclaringType { get; init; }
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
},
});
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ sealed class TypeMapAssemblyEmitter

TypeReferenceHandle _javaPeerProxyRef;
TypeReferenceHandle _javaPeerProxyNonGenericRef;
TypeReferenceHandle _javaConvertRef;
TypeReferenceHandle _iJavaPeerableRef;
TypeReferenceHandle _jniHandleOwnershipRef;
TypeReferenceHandle _jniObjectReferenceRef;
Expand All @@ -97,6 +98,8 @@ sealed class TypeMapAssemblyEmitter
MemberReferenceHandle _getUninitializedObjectRef;
MemberReferenceHandle _notSupportedExceptionCtorRef;
MemberReferenceHandle _jniObjectReferenceCtorRef;
MemberReferenceHandle _iJavaPeerableSetPeerReferenceRef;
MemberReferenceHandle _javaConvertFromJniHandleRef;
MemberReferenceHandle _jniEnvDeleteRefRef;
MemberReferenceHandle _shouldSkipActivationRef;
MemberReferenceHandle _waitForBridgeProcessingRef;
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -921,6 +939,18 @@ MemberReferenceHandle AddActivationCtorRef (EntityHandle declaringTypeRef)
}));
}

MemberReferenceHandle AddManagedConstructorRef (EntityHandle declaringTypeRef, IReadOnlyList<ManagedParameterInfo> 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);
Expand Down Expand Up @@ -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 (
Expand Down Expand Up @@ -1123,6 +1167,37 @@ MethodDefinitionHandle EmitUcoConstructor (UcoConstructorData uco, JavaPeerProxy
return handle;
}

void EmitManagedConstructorActivation (InstructionEncoder encoder, EntityHandle targetTypeRef,
MemberReferenceHandle ctorRef, IReadOnlyList<ManagedParameterInfo> managedParameterTypes,
IReadOnlyList<JniParamKind> 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);
}

/// <summary>
/// Emits the common try/catch/finally marshal-method wrapper pattern used by all
/// non-generic UCO constructor bodies:
Expand Down Expand Up @@ -1425,6 +1500,68 @@ 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)) {
do {
builder.WriteByte (0x1D); // ELEMENT_TYPE_SZARRAY
managedType = managedType.Substring (0, managedType.Length - 2);
} while (managedType.EndsWith ("[]", StringComparison.Ordinal));
}

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;
}

/// <summary>
/// Encodes <c>ReadOnlySpan&lt;JniNativeMethod&gt;</c> directly into a signature type encoder.
/// Required because <see cref="SignatureTypeEncoder.Type"/> doesn't accept TypeSpec handles.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,12 @@ public sealed record MarshalMethodInfo
/// </summary>
public string? SuperArgumentsString { get; init; }

/// <summary>
/// Managed constructor parameter types, when this marshal entry represents a
/// concrete managed constructor. Null for inherited seed constructors.
/// </summary>
public IReadOnlyList<ManagedParameterInfo>? ManagedParameterTypes { get; init; }

/// <summary>
/// True if this method was collected from an implemented interface
/// (Pass 4: CollectInterfaceMethodImplementations), not from the type itself.
Expand Down Expand Up @@ -267,6 +273,44 @@ public sealed record JavaConstructorInfo
/// Null for [Register] constructors.
/// </summary>
public string? SuperArgumentsString { get; init; }

/// <summary>
/// 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.
/// </summary>
public IReadOnlyList<ManagedParameterInfo>? ManagedParameterTypes { get; init; }

/// <summary>
/// Managed type that declares the concrete constructor to call.
/// Null when <see cref="ManagedParameterTypes"/> is null.
/// </summary>
public string? ConstructorDeclaringTypeName { get; init; }

/// <summary>
/// Assembly containing <see cref="ConstructorDeclaringTypeName"/>.
/// Null when <see cref="ManagedParameterTypes"/> is null.
/// </summary>
public string? ConstructorDeclaringAssemblyName { get; init; }
}

/// <summary>
/// Describes a managed parameter type for generated direct constructor calls.
/// </summary>
public sealed record ManagedParameterInfo
{
/// <summary>
/// Full managed type name, e.g. "System.String" or "Android.Content.Context".
/// Array types use C#-style "[]" suffixes.
/// </summary>
public required string ManagedTypeName { get; init; }

/// <summary>
/// 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.
/// </summary>
public string AssemblyName { get; init; } = "";
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
Expand Down Expand Up @@ -881,7 +882,7 @@ static bool HaveIdenticalParameterTypes (MethodDefinition method1, MethodDefinit
return true;
}

static void AddMarshalMethod (List<MarshalMethodInfo> methods, RegisterInfo registerInfo, MethodDefinition methodDef, AssemblyIndex index, ExportInfo? exportInfo = null, bool isInterfaceImplementation = false)
void AddMarshalMethod (List<MarshalMethodInfo> 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) {
Expand All @@ -896,6 +897,7 @@ static void AddMarshalMethod (List<MarshalMethodInfo> 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,
Expand All @@ -911,9 +913,39 @@ static void AddMarshalMethod (List<MarshalMethodInfo> 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,
});
}

ManagedParameterInfo [] ToManagedParameterInfos (IReadOnlyList<string> 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 = ResolveManagedParameterAssembly (parameterType, defaultAssemblyName),
};
}
return result;
}

string ResolveManagedParameterAssembly (string managedType, string defaultAssemblyName)
{
while (managedType.EndsWith ("[]", StringComparison.Ordinal)) {
managedType = managedType.Substring (0, managedType.Length - 2);
}
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)
{
return access switch {
Expand Down Expand Up @@ -1553,6 +1585,9 @@ static List<JavaConstructorInfo> BuildJavaConstructors (List<MarshalMethodInfo>
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++;
}
Expand Down
Loading