@@ -48,8 +48,10 @@ namespace Microsoft.Android.Sdk.TrimmableTypeMap;
4848/// // Registers JNI native methods (ACWs only):
4949/// public void RegisterNatives(JniType jniType)
5050/// {
51- /// TrimmableNativeRegistration.RegisterMethod(jniType, "n_OnCreate", "(Landroid/os/Bundle;)V", &n_OnCreate_uco_0);
52- /// TrimmableNativeRegistration.RegisterMethod(jniType, "nctor_0", "()V", &nctor_0_uco);
51+ /// JniNativeMethod* methods = stackalloc JniNativeMethod[2];
52+ /// methods[0] = new JniNativeMethod(&__utf8_0, &__utf8_1, &n_OnCreate_uco_0);
53+ /// methods[1] = new JniNativeMethod(&__utf8_2, &__utf8_3, &nctor_0_uco);
54+ /// JniEnvironment.Types.RegisterNatives(jniType.PeerReference, new ReadOnlySpan<JniNativeMethod>(methods, 2));
5355/// }
5456/// }
5557///
@@ -86,13 +88,23 @@ sealed class TypeMapAssemblyEmitter
8688 MemberReferenceHandle _jniObjectReferenceCtorRef ;
8789 MemberReferenceHandle _jniEnvDeleteRefRef ;
8890 MemberReferenceHandle _activateInstanceRef ;
89- MemberReferenceHandle _registerMethodRef ;
9091 MemberReferenceHandle _ucoAttrCtorRef ;
9192 BlobHandle _ucoAttrBlobHandle ;
9293 MemberReferenceHandle _typeMapAttrCtorRef2Arg ;
9394 MemberReferenceHandle _typeMapAttrCtorRef3Arg ;
9495 MemberReferenceHandle _typeMapAssociationAttrCtorRef ;
9596
97+ // RegisterNatives with JniNativeMethod
98+ TypeReferenceHandle _jniNativeMethodRef ;
99+ TypeReferenceHandle _jniEnvironmentRef ;
100+ TypeReferenceHandle _jniEnvironmentTypesRef ;
101+ TypeReferenceHandle _readOnlySpanOpenRef ;
102+ TypeSpecificationHandle _readOnlySpanOfJniNativeMethodSpec ;
103+ MemberReferenceHandle _jniNativeMethodCtorRef ;
104+ MemberReferenceHandle _jniTypePeerReferenceRef ;
105+ MemberReferenceHandle _jniEnvTypesRegisterNativesRef ;
106+ MemberReferenceHandle _readOnlySpanOfJniNativeMethodCtorRef ;
107+
96108 /// <summary>
97109 /// Creates a new emitter.
98110 /// </summary>
@@ -194,6 +206,18 @@ void EmitTypeReferences ()
194206 metadata . GetOrAddString ( "System" ) , metadata . GetOrAddString ( "NotSupportedException" ) ) ;
195207 _runtimeHelpersRef = metadata . AddTypeReference ( _pe . SystemRuntimeRef ,
196208 metadata . GetOrAddString ( "System.Runtime.CompilerServices" ) , metadata . GetOrAddString ( "RuntimeHelpers" ) ) ;
209+
210+ _jniNativeMethodRef = metadata . AddTypeReference ( _javaInteropRef ,
211+ metadata . GetOrAddString ( "Java.Interop" ) , metadata . GetOrAddString ( "JniNativeMethod" ) ) ;
212+ _jniEnvironmentRef = metadata . AddTypeReference ( _javaInteropRef ,
213+ metadata . GetOrAddString ( "Java.Interop" ) , metadata . GetOrAddString ( "JniEnvironment" ) ) ;
214+ _jniEnvironmentTypesRef = metadata . AddTypeReference ( _jniEnvironmentRef ,
215+ default , metadata . GetOrAddString ( "Types" ) ) ;
216+
217+ // ReadOnlySpan<JniNativeMethod> — TypeSpec for generic instantiation
218+ _readOnlySpanOpenRef = metadata . AddTypeReference ( _pe . SystemRuntimeRef ,
219+ metadata . GetOrAddString ( "System" ) , metadata . GetOrAddString ( "ReadOnlySpan`1" ) ) ;
220+ _readOnlySpanOfJniNativeMethodSpec = MakeGenericTypeSpec_ValueType ( _readOnlySpanOpenRef , _jniNativeMethodRef ) ;
197221 }
198222
199223 void EmitMemberReferences ( )
@@ -240,16 +264,41 @@ void EmitMemberReferences ()
240264 p . AddParameter ( ) . Type ( ) . Type ( _systemTypeRef , false ) ;
241265 } ) ) ;
242266
243- _registerMethodRef = _pe . AddMemberRef ( _trimmableNativeRegistrationRef , "RegisterMethod" ,
244- sig => sig . MethodSignature ( ) . Parameters ( 4 ,
267+ // JniNativeMethod..ctor(byte*, byte*, IntPtr)
268+ _jniNativeMethodCtorRef = _pe . AddMemberRef ( _jniNativeMethodRef , ".ctor" ,
269+ sig => sig . MethodSignature ( isInstanceMethod : true ) . Parameters ( 3 ,
245270 rt => rt . Void ( ) ,
246271 p => {
247- p . AddParameter ( ) . Type ( ) . Type ( _jniTypeRef , false ) ;
248- p . AddParameter ( ) . Type ( ) . String ( ) ;
249- p . AddParameter ( ) . Type ( ) . String ( ) ;
272+ p . AddParameter ( ) . Type ( ) . Pointer ( ) . Byte ( ) ;
273+ p . AddParameter ( ) . Type ( ) . Pointer ( ) . Byte ( ) ;
250274 p . AddParameter ( ) . Type ( ) . IntPtr ( ) ;
251275 } ) ) ;
252276
277+ // JniType.get_PeerReference() -> JniObjectReference
278+ _jniTypePeerReferenceRef = _pe . AddMemberRef ( _jniTypeRef , "get_PeerReference" ,
279+ sig => sig . MethodSignature ( isInstanceMethod : true ) . Parameters ( 0 ,
280+ rt => rt . Type ( ) . Type ( _jniObjectReferenceRef , true ) ,
281+ p => { } ) ) ;
282+
283+ // JniEnvironment.Types.RegisterNatives(JniObjectReference, ReadOnlySpan<JniNativeMethod>)
284+ _jniEnvTypesRegisterNativesRef = _pe . AddMemberRef ( _jniEnvironmentTypesRef , "RegisterNatives" ,
285+ sig => sig . MethodSignature ( ) . Parameters ( 2 ,
286+ rt => rt . Void ( ) ,
287+ p => {
288+ p . AddParameter ( ) . Type ( ) . Type ( _jniObjectReferenceRef , true ) ;
289+ // ReadOnlySpan<JniNativeMethod> — must encode as GENERICINST manually
290+ EncodeReadOnlySpanOfJniNativeMethod ( p . AddParameter ( ) . Type ( ) ) ;
291+ } ) ) ;
292+
293+ // ReadOnlySpan<JniNativeMethod>..ctor(void*, int)
294+ _readOnlySpanOfJniNativeMethodCtorRef = _pe . AddMemberRef ( _readOnlySpanOfJniNativeMethodSpec , ".ctor" ,
295+ sig => sig . MethodSignature ( isInstanceMethod : true ) . Parameters ( 2 ,
296+ rt => rt . Void ( ) ,
297+ p => {
298+ p . AddParameter ( ) . Type ( ) . VoidPointer ( ) ;
299+ p . AddParameter ( ) . Type ( ) . Int32 ( ) ;
300+ } ) ) ;
301+
253302 var ucoAttrTypeRef = _pe . Metadata . AddTypeReference ( _pe . SystemRuntimeInteropServicesRef ,
254303 _pe . Metadata . GetOrAddString ( "System.Runtime.InteropServices" ) ,
255304 _pe . Metadata . GetOrAddString ( "UnmanagedCallersOnlyAttribute" ) ) ;
@@ -702,25 +751,116 @@ MethodDefinitionHandle EmitUcoConstructor (UcoConstructorData uco)
702751 void EmitRegisterNatives ( List < NativeRegistrationData > registrations ,
703752 Dictionary < string , MethodDefinitionHandle > wrapperHandles )
704753 {
754+ // Filter to only registrations that have corresponding wrapper methods
755+ var validRegs = new List < ( NativeRegistrationData Reg , MethodDefinitionHandle Wrapper ) > ( registrations . Count ) ;
756+ foreach ( var reg in registrations ) {
757+ if ( wrapperHandles . TryGetValue ( reg . WrapperMethodName , out var wrapperHandle ) ) {
758+ validRegs . Add ( ( reg , wrapperHandle ) ) ;
759+ }
760+ }
761+
762+ if ( validRegs . Count == 0 ) {
763+ _pe . EmitBody ( "RegisterNatives" ,
764+ MethodAttributes . Public | MethodAttributes . Virtual | MethodAttributes . HideBySig |
765+ MethodAttributes . NewSlot | MethodAttributes . Final ,
766+ sig => sig . MethodSignature ( isInstanceMethod : true ) . Parameters ( 1 ,
767+ rt => rt . Void ( ) ,
768+ p => p . AddParameter ( ) . Type ( ) . Type ( _jniTypeRef , false ) ) ,
769+ encoder => encoder . OpCode ( ILOpCode . Ret ) ) ;
770+ return ;
771+ }
772+
773+ // Get or create deduplicated RVA fields for each unique name/signature string.
774+ var nameFields = new FieldDefinitionHandle [ validRegs . Count ] ;
775+ var sigFields = new FieldDefinitionHandle [ validRegs . Count ] ;
776+ for ( int i = 0 ; i < validRegs . Count ; i ++ ) {
777+ nameFields [ i ] = _pe . GetOrAddUtf8Field ( validRegs [ i ] . Reg . JniMethodName ) ;
778+ sigFields [ i ] = _pe . GetOrAddUtf8Field ( validRegs [ i ] . Reg . JniSignature ) ;
779+ }
780+
781+ int methodCount = validRegs . Count ;
782+
705783 _pe . EmitBody ( "RegisterNatives" ,
706784 MethodAttributes . Public | MethodAttributes . Virtual | MethodAttributes . HideBySig |
707785 MethodAttributes . NewSlot | MethodAttributes . Final ,
708786 sig => sig . MethodSignature ( isInstanceMethod : true ) . Parameters ( 1 ,
709787 rt => rt . Void ( ) ,
710788 p => p . AddParameter ( ) . Type ( ) . Type ( _jniTypeRef , false ) ) ,
711789 encoder => {
712- foreach ( var reg in registrations ) {
713- if ( ! wrapperHandles . TryGetValue ( reg . WrapperMethodName , out var wrapperHandle ) ) {
714- continue ;
790+ // stackalloc JniNativeMethod[N]
791+ encoder . LoadConstantI4 ( methodCount ) ;
792+ encoder . OpCode ( ILOpCode . Sizeof ) ;
793+ encoder . Token ( _jniNativeMethodRef ) ;
794+ encoder . OpCode ( ILOpCode . Mul ) ;
795+ encoder . OpCode ( ILOpCode . Localloc ) ;
796+ encoder . StoreLocal ( 0 ) ;
797+
798+ for ( int i = 0 ; i < methodCount ; i ++ ) {
799+ // &methods[i] — destination address for stobj
800+ encoder . LoadLocal ( 0 ) ;
801+ if ( i > 0 ) {
802+ encoder . LoadConstantI4 ( i ) ;
803+ encoder . OpCode ( ILOpCode . Sizeof ) ;
804+ encoder . Token ( _jniNativeMethodRef ) ;
805+ encoder . OpCode ( ILOpCode . Mul ) ;
806+ encoder . OpCode ( ILOpCode . Add ) ;
715807 }
716- encoder . LoadArgument ( 1 ) ;
717- encoder . LoadString ( _pe . Metadata . GetOrAddUserString ( reg . JniMethodName ) ) ;
718- encoder . LoadString ( _pe . Metadata . GetOrAddUserString ( reg . JniSignature ) ) ;
808+
809+ // byte* name — ldsflda of deduplicated field
810+ encoder . OpCode ( ILOpCode . Ldsflda ) ;
811+ encoder . Token ( nameFields [ i ] ) ;
812+
813+ // byte* signature
814+ encoder . OpCode ( ILOpCode . Ldsflda ) ;
815+ encoder . Token ( sigFields [ i ] ) ;
816+
817+ // IntPtr functionPointer
719818 encoder . OpCode ( ILOpCode . Ldftn ) ;
720- encoder . Token ( wrapperHandle ) ;
721- encoder . Call ( _registerMethodRef ) ;
819+ encoder . Token ( validRegs [ i ] . Wrapper ) ;
820+
821+ // Construct the struct on the evaluation stack and store it
822+ // at the destination address. This matches the Roslyn pattern:
823+ // newobj JniNativeMethod::.ctor(byte*, byte*, IntPtr)
824+ // stobj JniNativeMethod
825+ encoder . OpCode ( ILOpCode . Newobj ) ;
826+ encoder . Token ( _jniNativeMethodCtorRef ) ;
827+ encoder . OpCode ( ILOpCode . Stobj ) ;
828+ encoder . Token ( _jniNativeMethodRef ) ;
722829 }
830+
831+ // JniObjectReference peerRef = jniType.PeerReference
832+ // JniType is a sealed reference type, so use ldarg + callvirt
833+ encoder . LoadArgument ( 1 ) ;
834+ encoder . OpCode ( ILOpCode . Callvirt ) ;
835+ encoder . Token ( _jniTypePeerReferenceRef ) ;
836+ encoder . StoreLocal ( 1 ) ;
837+
838+ // new ReadOnlySpan<JniNativeMethod>(methods, count)
839+ encoder . LoadLocalAddress ( 2 ) ;
840+ encoder . LoadLocal ( 0 ) ;
841+ encoder . LoadConstantI4 ( methodCount ) ;
842+ encoder . Call ( _readOnlySpanOfJniNativeMethodCtorRef ) ;
843+
844+ // JniEnvironment.Types.RegisterNatives(peerRef, span)
845+ encoder . LoadLocal ( 1 ) ;
846+ encoder . LoadLocal ( 2 ) ;
847+ encoder . Call ( _jniEnvTypesRegisterNativesRef ) ;
848+
723849 encoder . OpCode ( ILOpCode . Ret ) ;
850+ } ,
851+ encodeLocals : localSig => {
852+ localSig . WriteByte ( 0x07 ) ; // IMAGE_CEE_CS_CALLCONV_LOCAL_SIG
853+ localSig . WriteCompressedInteger ( 3 ) ;
854+
855+ // local 0: native int (stackalloc pointer)
856+ localSig . WriteByte ( 0x18 ) ; // ELEMENT_TYPE_I
857+
858+ // local 1: JniObjectReference
859+ localSig . WriteByte ( 0x11 ) ; // ELEMENT_TYPE_VALUETYPE
860+ localSig . WriteCompressedInteger ( CodedIndex . TypeDefOrRefOrSpec ( _jniObjectReferenceRef ) ) ;
861+
862+ // local 2: ReadOnlySpan<JniNativeMethod>
863+ EncodeGenericValueTypeInst ( localSig , _readOnlySpanOpenRef , _jniNativeMethodRef ) ;
724864 } ) ;
725865 }
726866
@@ -753,4 +893,38 @@ void EmitTypeMapAssociationAttribute (TypeMapAssociationData assoc)
753893 } ) ;
754894 _pe . Metadata . AddCustomAttribute ( EntityHandle . AssemblyDefinition , _typeMapAssociationAttrCtorRef , blob ) ;
755895 }
896+
897+ /// <summary>
898+ /// Writes the ECMA-335 blob for a closed generic value type with a single value-type argument.
899+ /// E.g., <c>ReadOnlySpan<JniNativeMethod></c>.
900+ /// </summary>
901+ static void EncodeGenericValueTypeInst ( BlobBuilder builder , EntityHandle openType , EntityHandle valueTypeArg )
902+ {
903+ builder . WriteByte ( 0x15 ) ; // ELEMENT_TYPE_GENERICINST
904+ builder . WriteByte ( 0x11 ) ; // ELEMENT_TYPE_VALUETYPE
905+ builder . WriteCompressedInteger ( CodedIndex . TypeDefOrRefOrSpec ( openType ) ) ;
906+ builder . WriteCompressedInteger ( 1 ) ; // generic arity = 1
907+ builder . WriteByte ( 0x11 ) ; // ELEMENT_TYPE_VALUETYPE
908+ builder . WriteCompressedInteger ( CodedIndex . TypeDefOrRefOrSpec ( valueTypeArg ) ) ;
909+ }
910+
911+ /// <summary>
912+ /// Builds a <c>TypeSpec</c> for a closed generic type with a single value-type argument.
913+ /// E.g., <c>ReadOnlySpan<JniNativeMethod></c>.
914+ /// </summary>
915+ TypeSpecificationHandle MakeGenericTypeSpec_ValueType ( EntityHandle openType , EntityHandle valueTypeArg )
916+ {
917+ var sigBlob = new BlobBuilder ( 32 ) ;
918+ EncodeGenericValueTypeInst ( sigBlob , openType , valueTypeArg ) ;
919+ return _pe . Metadata . AddTypeSpecification ( _pe . Metadata . GetOrAddBlob ( sigBlob ) ) ;
920+ }
921+
922+ /// <summary>
923+ /// Encodes <c>ReadOnlySpan<JniNativeMethod></c> directly into a signature type encoder.
924+ /// Required because <see cref="SignatureTypeEncoder.Type"/> doesn't accept TypeSpec handles.
925+ /// </summary>
926+ void EncodeReadOnlySpanOfJniNativeMethod ( SignatureTypeEncoder encoder )
927+ {
928+ EncodeGenericValueTypeInst ( encoder . Builder , _readOnlySpanOpenRef , _jniNativeMethodRef ) ;
929+ }
756930}
0 commit comments