@@ -207,7 +207,7 @@ void ScanAssembly (AssemblyIndex index, Dictionary<string, JavaPeerInfo> results
207207 // Override and interface detection is only for user ACW class types:
208208 // - MCW types (DoNotGenerateAcw) already have [Register] on every method
209209 // - Interface types don't implement other interfaces' methods in JCWs
210- var marshalMethods = CollectMarshalMethods ( typeDef , index , detectBaseOverrides : ! doNotGenerateAcw && ! isInterface ) ;
210+ var ( marshalMethods , exportFields ) = CollectMarshalMethods ( typeDef , index , detectBaseOverrides : ! doNotGenerateAcw && ! isInterface ) ;
211211
212212 // Resolve activation constructor
213213 var activationCtor = ResolveActivationCtor ( fullName , typeDef , index ) ;
@@ -232,7 +232,7 @@ void ScanAssembly (AssemblyIndex index, Dictionary<string, JavaPeerInfo> results
232232 IsUnconditional = isUnconditional ,
233233 MarshalMethods = marshalMethods ,
234234 JavaConstructors = BuildJavaConstructors ( marshalMethods ) ,
235- JavaFields = CollectExportFields ( typeDef , index ) ,
235+ JavaFields = exportFields ,
236236 ActivationCtor = activationCtor ,
237237 InvokerTypeName = invokerTypeName ,
238238 IsGenericDefinition = isGenericDefinition ,
@@ -242,14 +242,19 @@ void ScanAssembly (AssemblyIndex index, Dictionary<string, JavaPeerInfo> results
242242 }
243243 }
244244
245- List < MarshalMethodInfo > CollectMarshalMethods ( TypeDefinition typeDef , AssemblyIndex index , bool detectBaseOverrides )
245+ ( List < MarshalMethodInfo > , List < JavaFieldInfo > ) CollectMarshalMethods ( TypeDefinition typeDef , AssemblyIndex index , bool detectBaseOverrides )
246246 {
247247 var methods = new List < MarshalMethodInfo > ( ) ;
248+ var fields = new List < JavaFieldInfo > ( ) ;
248249 var registeredMethodKeys = new HashSet < string > ( StringComparer . Ordinal ) ;
249250
250- // Pass 1: collect methods with [Register] or [Export ] directly on them
251+ // Pass 1: collect methods with [Register], [Export], or [ExportField ] directly on them
251252 foreach ( var methodHandle in typeDef . GetMethods ( ) ) {
252253 var methodDef = index . Reader . GetMethodDefinition ( methodHandle ) ;
254+
255+ // Check for [ExportField] — produces both a marshal method AND a field
256+ CollectExportField ( methodDef , index , fields ) ;
257+
253258 if ( ! TryGetMethodRegisterInfo ( methodDef , index , out var registerInfo , out var exportInfo ) || registerInfo is null ) {
254259 continue ;
255260 }
@@ -300,7 +305,7 @@ List<MarshalMethodInfo> CollectMarshalMethods (TypeDefinition typeDef, AssemblyI
300305 CollectBaseConstructorChain ( typeDef , index , methods ) ;
301306 }
302307
303- return methods ;
308+ return ( methods , fields ) ;
304309 }
305310
306311 /// <summary>
@@ -380,14 +385,16 @@ void CollectBasePropertyOverrides (TypeDefinition typeDef, AssemblyIndex index,
380385 }
381386
382387 var getterName = index . Reader . GetString ( getterDef . Name ) ;
383- if ( alreadyRegistered . Contains ( getterName ) ) {
388+ var sig = getterDef . DecodeSignature ( SignatureTypeProvider . Instance , genericContext : default ) ;
389+ var sigKey = $ "{ getterName } ({ string . Join ( "," , sig . ParameterTypes ) } )";
390+ if ( alreadyRegistered . Contains ( sigKey ) ) {
384391 continue ;
385392 }
386393
387394 var baseRegistration = FindBaseRegisteredProperty ( typeDef , index , getterName , getterDef ) ;
388395 if ( baseRegistration is not null ) {
389396 methods . Add ( baseRegistration ) ;
390- alreadyRegistered . Add ( getterName ) ;
397+ alreadyRegistered . Add ( sigKey ) ;
391398 }
392399 }
393400 }
@@ -1413,75 +1420,44 @@ static List<JavaConstructorInfo> BuildJavaConstructors (List<MarshalMethodInfo>
14131420 }
14141421
14151422 /// <summary>
1416- /// Collects Java field declarations from [ExportField] attributes on methods.
1417- /// Each [ExportField("name")] on a method produces a Java field initialized by
1418- /// calling that method.
1423+ /// Checks a single method for [ExportField] and adds a JavaFieldInfo if found.
1424+ /// Called inline during Pass 1 to avoid a separate iteration.
14191425 /// </summary>
1420- static List < JavaFieldInfo > CollectExportFields ( TypeDefinition typeDef , AssemblyIndex index )
1426+ static void CollectExportField ( MethodDefinition methodDef , AssemblyIndex index , List < JavaFieldInfo > fields )
14211427 {
1422- var fields = new List < JavaFieldInfo > ( ) ;
1423-
1424- foreach ( var methodHandle in typeDef . GetMethods ( ) ) {
1425- var methodDef = index . Reader . GetMethodDefinition ( methodHandle ) ;
1426-
1427- foreach ( var caHandle in methodDef . GetCustomAttributes ( ) ) {
1428- var ca = index . Reader . GetCustomAttribute ( caHandle ) ;
1429- var attrName = AssemblyIndex . GetCustomAttributeName ( ca , index . Reader ) ;
1430-
1431- if ( attrName != "ExportFieldAttribute" ) {
1432- continue ;
1433- }
1434-
1435- var value = index . DecodeAttribute ( ca ) ;
1436- if ( value . FixedArguments . Length == 0 ) {
1437- continue ;
1438- }
1428+ foreach ( var caHandle in methodDef . GetCustomAttributes ( ) ) {
1429+ var ca = index . Reader . GetCustomAttribute ( caHandle ) ;
1430+ var attrName = AssemblyIndex . GetCustomAttributeName ( ca , index . Reader ) ;
14391431
1440- var fieldName = ( string ? ) value . FixedArguments [ 0 ] . Value ;
1441- if ( fieldName is null ) {
1442- continue ;
1443- }
1432+ if ( attrName != "ExportFieldAttribute" ) {
1433+ continue ;
1434+ }
14441435
1445- var managedName = index . Reader . GetString ( methodDef . Name ) ;
1446- var sig = methodDef . DecodeSignature ( SignatureTypeProvider . Instance , genericContext : default ) ;
1447- var javaReturnType = ManagedReturnTypeToJava ( sig . ReturnType ) ;
1448- var access = GetJavaAccess ( methodDef . Attributes & MethodAttributes . MemberAccessMask ) ;
1449- var isStatic = ( methodDef . Attributes & MethodAttributes . Static ) != 0 ;
1450-
1451- fields . Add ( new JavaFieldInfo {
1452- FieldName = fieldName ,
1453- JavaTypeName = javaReturnType ,
1454- InitializerMethodName = managedName ,
1455- Visibility = access ,
1456- IsStatic = isStatic ,
1457- } ) ;
1436+ var value = index . DecodeAttribute ( ca ) ;
1437+ if ( value . FixedArguments . Length == 0 ) {
1438+ continue ;
14581439 }
1459- }
14601440
1461- return fields ;
1462- }
1441+ var fieldName = ( string ? ) value . FixedArguments [ 0 ] . Value ;
1442+ if ( fieldName is null ) {
1443+ continue ;
1444+ }
14631445
1464- static string ManagedReturnTypeToJava ( string managedType )
1465- {
1466- switch ( managedType ) {
1467- case "System.String" : return "java.lang.String" ;
1468- case "System.Boolean" : return "boolean" ;
1469- case "System.Byte" :
1470- case "System.SByte" : return "byte" ;
1471- case "System.Char" : return "char" ;
1472- case "System.Int16" :
1473- case "System.UInt16" : return "short" ;
1474- case "System.Int32" :
1475- case "System.UInt32" : return "int" ;
1476- case "System.Int64" :
1477- case "System.UInt64" : return "long" ;
1478- case "System.Single" : return "float" ;
1479- case "System.Double" : return "double" ;
1480- case "System.Void" : return "void" ;
1481- default :
1482- // For reference types, use java.lang.Object as fallback.
1483- // The exact Java type would require JNI signature resolution.
1484- return "java.lang.Object" ;
1446+ var managedName = index . Reader . GetString ( methodDef . Name ) ;
1447+ var sig = methodDef . DecodeSignature ( SignatureTypeProvider . Instance , genericContext : default ) ;
1448+ var jniSig = BuildJniSignatureFromManaged ( sig ) ;
1449+ var jniReturnType = JniSignatureHelper . ParseReturnTypeString ( jniSig ) ;
1450+ var javaReturnType = JniSignatureHelper . JniTypeToJava ( jniReturnType ) ;
1451+ var access = GetJavaAccess ( methodDef . Attributes & MethodAttributes . MemberAccessMask ) ;
1452+ var isStatic = ( methodDef . Attributes & MethodAttributes . Static ) != 0 ;
1453+
1454+ fields . Add ( new JavaFieldInfo {
1455+ FieldName = fieldName ,
1456+ JavaTypeName = javaReturnType ,
1457+ InitializerMethodName = managedName ,
1458+ Visibility = access ,
1459+ IsStatic = isStatic ,
1460+ } ) ;
14851461 }
14861462 }
14871463}
0 commit comments