Skip to content

Commit 42c195c

Browse files
committed
Implement remaining [Export] marshaling gaps
- Support arrays in export marshal wrappers using JNIEnv.GetArray<T>/NewArray<T>/CopyArray<T> including copy-back cleanup for array parameters - Support enum signatures and marshaling as JNI int while calling managed enum methods - Map Java.Lang.ICharSequence to Ljava/lang/CharSequence; in export signature generation - Resolve JNI object descriptors from [Register] metadata for managed object types (e.g. View[] -> [Landroid/view/View; instead of Object[]) - Improve ManagedTypeToAssemblyQualifiedName to resolve non-BCL types and array element types - Generate proxies for export-only ACW types (no activation ctor / no [Register] methods) Add fixture coverage and tests for export-only proxies plus array/enum/charsequence signatures and registration. All 312 tests pass.
1 parent 646e280 commit 42c195c

File tree

7 files changed

+670
-110
lines changed

7 files changed

+670
-110
lines changed

src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/ModelBuilder.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,8 @@ static void EmitPeers (TypeMapAssemblyData model, string jniName,
117117
var peer = peersForName [i];
118118
string entryJniName = i == 0 ? jniName : $"{jniName}[{i}]";
119119

120-
bool hasProxy = peer.ActivationCtor != null || peer.InvokerTypeName != null;
121120
bool isAcw = !peer.DoNotGenerateAcw && !peer.IsInterface && peer.MarshalMethods.Count > 0;
121+
bool hasProxy = peer.ActivationCtor != null || peer.InvokerTypeName != null || isAcw;
122122

123123
JavaPeerProxyData? proxy = null;
124124
if (hasProxy) {

src/Microsoft.Android.Sdk.TrimmableTypeMap/Generator/TypeMapAssemblyEmitter.cs

Lines changed: 327 additions & 47 deletions
Large diffs are not rendered by default.

src/Microsoft.Android.Sdk.TrimmableTypeMap/Scanner/JavaPeerScanner.cs

Lines changed: 149 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -304,7 +304,7 @@ void ScanAssembly (AssemblyIndex index, Dictionary<string, JavaPeerInfo> results
304304
return (methods, exportFields);
305305
}
306306

307-
static void AddMarshalMethod (List<MarshalMethodInfo> methods, RegisterInfo registerInfo, MethodDefinition methodDef, AssemblyIndex index)
307+
void AddMarshalMethod (List<MarshalMethodInfo> methods, RegisterInfo registerInfo, MethodDefinition methodDef, AssemblyIndex index)
308308
{
309309
// Skip methods that are just the JNI name (type-level [Register])
310310
if (registerInfo.Signature == null && registerInfo.Connector == null) {
@@ -324,10 +324,10 @@ static void AddMarshalMethod (List<MarshalMethodInfo> methods, RegisterInfo regi
324324
if (registerInfo.Connector == null || isConstructor) {
325325
var sig = methodDef.DecodeSignature (SignatureTypeProvider.Instance, genericContext: default);
326326
for (int i = 0; i < parameters.Count && i < sig.ParameterTypes.Length; i++) {
327-
parameters [i].ManagedType = ManagedTypeToAssemblyQualifiedName (sig.ParameterTypes [i]);
327+
parameters [i].ManagedType = ManagedTypeToAssemblyQualifiedName (sig.ParameterTypes [i], index);
328328
}
329329
if (sig.ReturnType != "System.Void") {
330-
managedReturnType = ManagedTypeToAssemblyQualifiedName (sig.ReturnType);
330+
managedReturnType = ManagedTypeToAssemblyQualifiedName (sig.ReturnType, index);
331331
}
332332
}
333333
methods.Add (new MarshalMethodInfo {
@@ -404,7 +404,7 @@ List<string> ResolveImplementedInterfaceJavaNames (TypeDefinition typeDef, Assem
404404
return null;
405405
}
406406

407-
static RegisterInfo ParseExportAttribute (CustomAttribute ca, MethodDefinition methodDef, AssemblyIndex index)
407+
RegisterInfo ParseExportAttribute (CustomAttribute ca, MethodDefinition methodDef, AssemblyIndex index)
408408
{
409409
var value = ca.DecodeValue (index.customAttributeTypeProvider);
410410

@@ -434,7 +434,7 @@ static RegisterInfo ParseExportAttribute (CustomAttribute ca, MethodDefinition m
434434

435435
// Build JNI signature from method signature
436436
var sig = methodDef.DecodeSignature (SignatureTypeProvider.Instance, genericContext: default);
437-
var jniSig = BuildJniSignatureFromManaged (sig);
437+
var jniSig = BuildJniSignatureFromManaged (sig, index);
438438

439439
return new RegisterInfo (exportName, jniSig, null, false,
440440
thrownNames: thrownNames, superArgumentsString: superArguments);
@@ -444,11 +444,11 @@ static RegisterInfo ParseExportAttribute (CustomAttribute ca, MethodDefinition m
444444
/// Creates a RegisterInfo for an [ExportField] method.
445445
/// The method is registered like [Export] (Connector = null) so it gets a full marshal body.
446446
/// </summary>
447-
static RegisterInfo ParseExportFieldAsRegisterInfo (MethodDefinition methodDef, AssemblyIndex index)
447+
RegisterInfo ParseExportFieldAsRegisterInfo (MethodDefinition methodDef, AssemblyIndex index)
448448
{
449449
var methodName = index.Reader.GetString (methodDef.Name);
450450
var sig = methodDef.DecodeSignature (SignatureTypeProvider.Instance, genericContext: default);
451-
var jniSig = BuildJniSignatureFromManaged (sig);
451+
var jniSig = BuildJniSignatureFromManaged (sig, index);
452452
return new RegisterInfo (methodName, jniSig, null, false);
453453
}
454454

@@ -492,19 +492,19 @@ static RegisterInfo ParseExportFieldAsRegisterInfo (MethodDefinition methodDef,
492492
return null;
493493
}
494494

495-
static string BuildJniSignatureFromManaged (MethodSignature<string> sig)
495+
string BuildJniSignatureFromManaged (MethodSignature<string> sig, AssemblyIndex index)
496496
{
497497
var sb = new System.Text.StringBuilder ();
498498
sb.Append ('(');
499499
foreach (var param in sig.ParameterTypes) {
500-
sb.Append (ManagedTypeToJniDescriptor (param));
500+
sb.Append (ManagedTypeToJniDescriptor (param, index));
501501
}
502502
sb.Append (')');
503-
sb.Append (ManagedTypeToJniDescriptor (sig.ReturnType));
503+
sb.Append (ManagedTypeToJniDescriptor (sig.ReturnType, index));
504504
return sb.ToString ();
505505
}
506506

507-
static string ManagedTypeToJniDescriptor (string managedType)
507+
string ManagedTypeToJniDescriptor (string managedType, AssemblyIndex index)
508508
{
509509
switch (managedType) {
510510
case "System.Void": return "V";
@@ -521,9 +521,17 @@ static string ManagedTypeToJniDescriptor (string managedType)
521521
case "System.Single": return "F";
522522
case "System.Double": return "D";
523523
case "System.String": return "Ljava/lang/String;";
524+
case "Java.Lang.ICharSequence": return "Ljava/lang/CharSequence;";
524525
default:
525526
if (managedType.EndsWith ("[]")) {
526-
return "[" + ManagedTypeToJniDescriptor (managedType.Substring (0, managedType.Length - 2));
527+
return "[" + ManagedTypeToJniDescriptor (managedType.Substring (0, managedType.Length - 2), index);
528+
}
529+
if (IsEnumManagedType (managedType, index)) {
530+
return "I";
531+
}
532+
var jniName = ResolveManagedTypeJniName (managedType, index);
533+
if (!string.IsNullOrEmpty (jniName)) {
534+
return "L" + jniName + ";";
527535
}
528536
return "Ljava/lang/Object;";
529537
}
@@ -533,8 +541,12 @@ static string ManagedTypeToJniDescriptor (string managedType)
533541
/// Maps a managed type name (from SignatureTypeProvider) to an assembly-qualified name
534542
/// like "System.Int32, System.Private.CoreLib" used in TypeManager.Activate calls.
535543
/// </summary>
536-
static string ManagedTypeToAssemblyQualifiedName (string managedType)
544+
string ManagedTypeToAssemblyQualifiedName (string managedType, AssemblyIndex index)
537545
{
546+
if (managedType.IndexOf (", ", StringComparison.Ordinal) >= 0) {
547+
return managedType;
548+
}
549+
538550
// BCL types all live in System.Private.CoreLib
539551
switch (managedType) {
540552
case "System.Void":
@@ -556,11 +568,131 @@ static string ManagedTypeToAssemblyQualifiedName (string managedType)
556568
case "System.UIntPtr":
557569
return managedType + ", System.Private.CoreLib";
558570
default:
559-
// For non-BCL types, we don't know the assembly at this point.
560-
// This is a best-effort mapping; full assembly resolution for
561-
// arbitrary types is a follow-up.
562-
return managedType;
571+
// Best-effort assembly resolution across loaded assemblies.
572+
var assemblyName = ResolveManagedTypeAssemblyName (managedType, index);
573+
return assemblyName != null ? managedType + ", " + assemblyName : managedType;
574+
}
575+
}
576+
577+
string? ResolveManagedTypeAssemblyName (string managedType, AssemblyIndex index)
578+
{
579+
string typeName = StripManagedTypeDecorations (managedType);
580+
if (typeName.Length == 0 || typeName [0] == '!') {
581+
return null; // generic method/type parameter
582+
}
583+
584+
if (IsBclTypeName (typeName)) {
585+
return "System.Private.CoreLib";
586+
}
587+
588+
if (TryResolveManagedTypeDefinition (typeName, index, out _, out var resolvedIndex)) {
589+
return resolvedIndex.AssemblyName;
590+
}
591+
592+
return index.AssemblyName;
593+
}
594+
595+
bool IsEnumManagedType (string managedType, AssemblyIndex index)
596+
{
597+
string typeName = StripManagedTypeDecorations (managedType);
598+
if (typeName.Length == 0 || typeName [0] == '!') {
599+
return false;
600+
}
601+
602+
if (!TryResolveManagedTypeDefinition (typeName, index, out var handle, out var resolvedIndex)) {
603+
return false;
604+
}
605+
606+
var typeDef = resolvedIndex.Reader.GetTypeDefinition (handle);
607+
if (typeDef.BaseType.Kind == HandleKind.TypeReference) {
608+
var (baseTypeName, _) = ResolveTypeReference ((TypeReferenceHandle) typeDef.BaseType, resolvedIndex);
609+
return baseTypeName == "System.Enum";
610+
}
611+
if (typeDef.BaseType.Kind == HandleKind.TypeDefinition) {
612+
var baseTypeDef = resolvedIndex.Reader.GetTypeDefinition ((TypeDefinitionHandle) typeDef.BaseType);
613+
return AssemblyIndex.GetFullName (baseTypeDef, resolvedIndex.Reader) == "System.Enum";
563614
}
615+
return false;
616+
}
617+
618+
string? ResolveManagedTypeJniName (string managedType, AssemblyIndex index)
619+
{
620+
string typeName = StripManagedTypeDecorations (managedType);
621+
if (typeName.Length == 0 || typeName [0] == '!') {
622+
return null;
623+
}
624+
625+
if (!TryResolveManagedTypeDefinition (typeName, index, out var handle, out var resolvedIndex)) {
626+
return null;
627+
}
628+
629+
if (resolvedIndex.RegisterInfoByType.TryGetValue (handle, out var regInfo) &&
630+
!string.IsNullOrEmpty (regInfo.JniName)) {
631+
return regInfo.JniName;
632+
}
633+
634+
return null;
635+
}
636+
637+
bool TryResolveManagedTypeDefinition (string managedTypeName, AssemblyIndex index, out TypeDefinitionHandle handle, out AssemblyIndex resolvedIndex)
638+
{
639+
if (TryResolveType (managedTypeName, index.AssemblyName, out handle, out resolvedIndex)) {
640+
return true;
641+
}
642+
643+
foreach (var candidate in assemblyCache.Values) {
644+
if (candidate.TypesByFullName.TryGetValue (managedTypeName, out handle)) {
645+
resolvedIndex = candidate;
646+
return true;
647+
}
648+
}
649+
650+
handle = default;
651+
resolvedIndex = null!;
652+
return false;
653+
}
654+
655+
static bool IsBclTypeName (string managedTypeName)
656+
{
657+
switch (managedTypeName) {
658+
case "System.Void":
659+
case "System.Boolean":
660+
case "System.Byte":
661+
case "System.SByte":
662+
case "System.Char":
663+
case "System.Int16":
664+
case "System.UInt16":
665+
case "System.Int32":
666+
case "System.UInt32":
667+
case "System.Int64":
668+
case "System.UInt64":
669+
case "System.Single":
670+
case "System.Double":
671+
case "System.String":
672+
case "System.Object":
673+
case "System.IntPtr":
674+
case "System.UIntPtr":
675+
return true;
676+
default:
677+
return false;
678+
}
679+
}
680+
681+
static string StripManagedTypeDecorations (string managedType)
682+
{
683+
string result = managedType;
684+
while (result.EndsWith ("[]", StringComparison.Ordinal) ||
685+
result.EndsWith ("&", StringComparison.Ordinal) ||
686+
result.EndsWith ("*", StringComparison.Ordinal)) {
687+
result = result.Substring (0, result.Length - (result.EndsWith ("[]", StringComparison.Ordinal) ? 2 : 1));
688+
}
689+
690+
int genericStart = result.IndexOf ('<');
691+
if (genericStart >= 0) {
692+
result = result.Substring (0, genericStart);
693+
}
694+
695+
return result;
564696
}
565697

566698
ActivationCtorInfo? ResolveActivationCtor (string typeName, TypeDefinition typeDef, AssemblyIndex index)
@@ -872,47 +1004,6 @@ static List<JniParameterInfo> ParseJniParameters (string jniSignature)
8721004
return result;
8731005
}
8741006

875-
static List<JavaConstructorInfo> BuildJavaConstructors (List<MarshalMethodInfo> marshalMethods)
876-
{
877-
var ctors = new List<JavaConstructorInfo> ();
878-
int ctorIndex = 0;
879-
foreach (var mm in marshalMethods) {
880-
if (!mm.IsConstructor) {
881-
continue;
882-
}
883-
ctors.Add (new JavaConstructorInfo {
884-
JniSignature = mm.JniSignature,
885-
ConstructorIndex = ctorIndex,
886-
Parameters = mm.Parameters,
887-
SuperArgumentsString = mm.SuperArgumentsString,
888-
});
889-
ctorIndex++;
890-
}
891-
return ctors;
892-
}
893-
894-
static string ExtractNamespace (string fullName)
895-
{
896-
int lastDot = fullName.LastIndexOf ('.');
897-
return lastDot >= 0 ? fullName.Substring (0, lastDot) : "";
898-
}
899-
900-
static string ExtractShortName (string fullName)
901-
{
902-
int lastDot = fullName.LastIndexOf ('.');
903-
return lastDot >= 0 ? fullName.Substring (lastDot + 1) : fullName;
904-
}
905-
906-
static List<JniParameterInfo> ParseJniParameters (string jniSignature)
907-
{
908-
var typeStrings = JniSignatureHelper.ParseParameterTypeStrings (jniSignature);
909-
var result = new List<JniParameterInfo> (typeStrings.Count);
910-
foreach (var t in typeStrings) {
911-
result.Add (new JniParameterInfo { JniType = t });
912-
}
913-
return result;
914-
}
915-
9161007
static List<JavaConstructorInfo> BuildJavaConstructors (List<MarshalMethodInfo> marshalMethods)
9171008
{
9181009
var ctors = new List<JavaConstructorInfo> ();

0 commit comments

Comments
 (0)