@@ -17,6 +17,8 @@ namespace NativeAOT;
1717
1818internal class NativeAotValueManager : JniRuntime . JniValueManager
1919{
20+ const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes . PublicConstructors | DynamicallyAccessedMemberTypes . NonPublicConstructors ;
21+
2022 readonly NativeAotTypeManager TypeManager ;
2123 Dictionary < int , List < IJavaPeerable > > ? RegisteredInstances = new Dictionary < int , List < IJavaPeerable > > ( ) ;
2224
@@ -253,105 +255,180 @@ public override List<JniSurfacedPeerInfo> GetSurfacedPeers ()
253255
254256 public override IJavaPeerable ? CreatePeer (
255257 ref JniObjectReference reference ,
256- JniObjectReferenceOptions options ,
257- [ DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes . PublicConstructors | DynamicallyAccessedMemberTypes . NonPublicConstructors ) ]
258+ JniObjectReferenceOptions transfer ,
259+ [ DynamicallyAccessedMembers ( Constructors ) ]
258260 Type ? targetType )
259261 {
260- if ( ! reference . IsValid )
262+ if ( ! reference . IsValid ) {
261263 return null ;
264+ }
262265
263- var peer = CreateInstance ( reference . Handle , JniHandleOwnership . DoNotTransfer , targetType ) ;
264- JniObjectReference . Dispose ( ref reference , options ) ;
265- return peer ;
266- }
266+ targetType = targetType ?? typeof ( global ::Java . Interop . JavaObject ) ;
267+ targetType = GetPeerType ( targetType ) ;
267268
268- internal IJavaPeerable ? CreateInstance (
269- IntPtr handle ,
270- JniHandleOwnership transfer ,
271- [ DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes . PublicConstructors | DynamicallyAccessedMemberTypes . NonPublicConstructors ) ]
272- Type ? targetType )
273- {
274- if ( targetType . IsInterface || targetType . IsAbstract ) {
275- var invokerType = JavaObjectExtensions . GetInvokerType ( targetType ) ;
276- if ( invokerType == null )
277- throw new NotSupportedException ( "Unable to find Invoker for type '" + targetType . FullName + "'. Was it linked away?" ,
278- CreateJavaLocationException ( ) ) ;
279- targetType = invokerType ;
280- }
269+ if ( ! typeof ( IJavaPeerable ) . IsAssignableFrom ( targetType ) )
270+ throw new ArgumentException ( $ "targetType `{ targetType . AssemblyQualifiedName } ` must implement IJavaPeerable!", nameof ( targetType ) ) ;
281271
282- var typeSig = TypeManager . GetTypeSignature ( targetType ) ;
283- if ( ! typeSig . IsValid || typeSig . SimpleReference == null ) {
272+ var targetSig = Runtime . TypeManager . GetTypeSignature ( targetType ) ;
273+ if ( ! targetSig . IsValid || targetSig . SimpleReference == null ) {
284274 throw new ArgumentException ( $ "Could not determine Java type corresponding to `{ targetType . AssemblyQualifiedName } `.", nameof ( targetType ) ) ;
285275 }
286276
287- JniObjectReference typeClass = default ;
288- JniObjectReference handleClass = default ;
277+ var refClass = JniEnvironment . Types . GetObjectClass ( reference ) ;
278+ JniObjectReference targetClass ;
289279 try {
290- try {
291- typeClass = JniEnvironment . Types . FindClass ( typeSig . SimpleReference ) ;
292- } catch ( Exception e ) {
293- throw new ArgumentException ( $ "Could not find Java class `{ typeSig . SimpleReference } `.",
294- nameof ( targetType ) ,
295- e ) ;
296- }
280+ targetClass = JniEnvironment . Types . FindClass ( targetSig . SimpleReference ) ;
281+ } catch ( Exception e ) {
282+ JniObjectReference . Dispose ( ref refClass ) ;
283+ throw new ArgumentException ( $ "Could not find Java class `{ targetSig . SimpleReference } `.",
284+ nameof ( targetType ) ,
285+ e ) ;
286+ }
287+
288+ if ( ! JniEnvironment . Types . IsAssignableFrom ( refClass , targetClass ) ) {
289+ JniObjectReference . Dispose ( ref refClass ) ;
290+ JniObjectReference . Dispose ( ref targetClass ) ;
291+ return null ;
292+ }
293+
294+ JniObjectReference . Dispose ( ref targetClass ) ;
295+
296+ var proxy = CreatePeerProxy ( ref refClass , targetType , ref reference , transfer ) ;
297+
298+ if ( proxy == null ) {
299+ throw new NotSupportedException ( string . Format ( "Could not find an appropriate constructable wrapper type for Java type '{0}', targetType='{1}'." ,
300+ JniEnvironment . Types . GetJniTypeNameFromInstance ( reference ) , targetType ) ) ;
301+ }
302+
303+ proxy . SetJniManagedPeerState ( proxy . JniManagedPeerState | JniManagedPeerStates . Replaceable ) ;
304+ return proxy ;
305+ }
306+
307+ [ return : DynamicallyAccessedMembers ( Constructors ) ]
308+ static Type GetPeerType ( [ DynamicallyAccessedMembers ( Constructors ) ] Type type )
309+ {
310+ if ( type == typeof ( object ) )
311+ return typeof ( global ::Java . Interop . JavaObject ) ;
312+ if ( type == typeof ( IJavaPeerable ) )
313+ return typeof ( global ::Java . Interop . JavaObject ) ;
314+ if ( type == typeof ( Exception ) )
315+ return typeof ( global ::Java . Interop . JavaException ) ;
316+ return type ;
317+ }
318+
319+ static readonly Type ByRefJniObjectReference = typeof ( JniObjectReference ) . MakeByRefType ( ) ;
320+
321+ IJavaPeerable ? CreatePeerProxy (
322+ ref JniObjectReference klass ,
323+ [ DynamicallyAccessedMembers ( Constructors ) ]
324+ Type fallbackType ,
325+ ref JniObjectReference reference ,
326+ JniObjectReferenceOptions options )
327+ {
328+ var jniTypeName = JniEnvironment . Types . GetJniTypeNameFromClass ( klass ) ;
297329
298- handleClass = JniEnvironment . Types . GetObjectClass ( new JniObjectReference ( handle ) ) ;
299- if ( ! JniEnvironment . Types . IsAssignableFrom ( handleClass , typeClass ) ) {
330+ Type ? type = null ;
331+ while ( jniTypeName != null ) {
332+ JniTypeSignature sig ;
333+ if ( ! JniTypeSignature . TryParse ( jniTypeName , out sig ) )
300334 return null ;
335+
336+ type = Runtime . TypeManager . GetType ( sig ) ;
337+
338+ if ( type != null ) {
339+ var peer = TryCreatePeerProxy ( type , ref reference , options ) ;
340+ if ( peer != null ) {
341+ return peer ;
342+ }
301343 }
302- } finally {
303- JniObjectReference . Dispose ( ref handleClass ) ;
304- JniObjectReference . Dispose ( ref typeClass ) ;
344+
345+ var super = JniEnvironment . Types . GetSuperclass ( klass ) ;
346+ jniTypeName = super . IsValid
347+ ? JniEnvironment . Types . GetJniTypeNameFromClass ( super )
348+ : null ;
349+
350+ JniObjectReference . Dispose ( ref klass , JniObjectReferenceOptions . CopyAndDispose ) ;
351+ klass = super ;
305352 }
353+ JniObjectReference . Dispose ( ref klass , JniObjectReferenceOptions . CopyAndDispose ) ;
306354
307- IJavaPeerable ? result = null ;
355+ return TryCreatePeerProxy ( fallbackType , ref reference , options ) ;
356+ }
308357
309- try {
310- result = ( IJavaPeerable ) CreateProxy ( targetType , handle , transfer ) ;
311- //if (JNIEnv.IsGCUserPeer (result.PeerReference.Handle)) {
312- result . SetJniManagedPeerState ( JniManagedPeerStates . Replaceable | JniManagedPeerStates . Activatable ) ;
313- //}
314- } catch ( MissingMethodException e ) {
315- var key_handle = JNIEnv . IdentityHash ( handle ) ;
316- JNIEnv . DeleteRef ( handle , transfer ) ;
317- throw new NotSupportedException ( FormattableString . Invariant (
318- $ "Unable to activate instance of type { targetType } from native handle 0x{ handle : x} (key_handle 0x{ key_handle : x} ).") , e ) ;
358+ static ConstructorInfo ? GetActivationConstructor (
359+ [ DynamicallyAccessedMembers ( Constructors ) ]
360+ Type type )
361+ {
362+ if ( type . IsAbstract || type . IsInterface ) {
363+ type = GetInvokerType ( type ) ?? type ;
319364 }
320- return result ;
365+ foreach ( var c in type . GetConstructors ( BindingFlags . Public | BindingFlags . NonPublic | BindingFlags . Instance ) ) {
366+ var p = c . GetParameters ( ) ;
367+ if ( p . Length == 2 && p [ 0 ] . ParameterType == ByRefJniObjectReference && p [ 1 ] . ParameterType == typeof ( JniObjectReferenceOptions ) )
368+ return c ;
369+ if ( p . Length == 2 && p [ 0 ] . ParameterType == typeof ( IntPtr ) && p [ 1 ] . ParameterType == typeof ( JniHandleOwnership ) )
370+ return c ;
371+ }
372+ return null ;
321373 }
322374
375+ [ return : DynamicallyAccessedMembers ( Constructors ) ]
376+ static Type ? GetInvokerType ( Type type )
377+ {
378+ // https://github.com/xamarin/xamarin-android/blob/5472eec991cc075e4b0c09cd98a2331fb93aa0f3/src/Microsoft.Android.Sdk.ILLink/MarkJavaObjects.cs#L176-L186
379+ const string makeGenericTypeMessage = "Generic 'Invoker' types are preserved by the MarkJavaObjects trimmer step." ;
380+
381+ [ UnconditionalSuppressMessage ( "Trimming" , "IL2055" , Justification = makeGenericTypeMessage ) ]
382+ [ return : DynamicallyAccessedMembers ( Constructors ) ]
383+ static Type MakeGenericType (
384+ [ DynamicallyAccessedMembers ( Constructors ) ]
385+ Type type ,
386+ Type [ ] arguments ) =>
387+ // FIXME: https://github.com/dotnet/java-interop/issues/1192
388+ #pragma warning disable IL3050
389+ type . MakeGenericType ( arguments ) ;
390+ #pragma warning restore IL3050
391+
392+ var signature = type . GetCustomAttribute < JniTypeSignatureAttribute > ( ) ;
393+ if ( signature == null || signature . InvokerType == null ) {
394+ return null ;
395+ }
396+
397+ Type [ ] arguments = type . GetGenericArguments ( ) ;
398+ if ( arguments . Length == 0 )
399+ return signature . InvokerType ;
400+
401+ return MakeGenericType ( signature . InvokerType , arguments ) ;
402+ }
403+
404+ const BindingFlags ActivationConstructorBindingFlags = BindingFlags . Public | BindingFlags . NonPublic | BindingFlags . Instance ;
405+
406+
323407 static readonly Type [ ] XAConstructorSignature = new Type [ ] { typeof ( IntPtr ) , typeof ( JniHandleOwnership ) } ;
324408 static readonly Type [ ] JIConstructorSignature = new Type [ ] { typeof ( JniObjectReference ) . MakeByRefType ( ) , typeof ( JniObjectReferenceOptions ) } ;
325409
326- internal static object CreateProxy (
327- [ DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes . PublicConstructors | DynamicallyAccessedMemberTypes . NonPublicConstructors ) ]
328- Type type ,
329- IntPtr handle ,
330- JniHandleOwnership transfer )
410+ protected virtual IJavaPeerable ? TryCreatePeerProxy ( Type type , ref JniObjectReference reference , JniObjectReferenceOptions options )
331411 {
332- // Skip Activator.CreateInstance() as that requires public constructors,
333- // and we want to hide some constructors for sanity reasons.
334- BindingFlags flags = BindingFlags . Public | BindingFlags . NonPublic | BindingFlags . Instance ;
335- var c = type . GetConstructor ( flags , null , XAConstructorSignature , null ) ;
412+ var c = type . GetConstructor ( ActivationConstructorBindingFlags , null , XAConstructorSignature , null ) ;
336413 if ( c != null ) {
337- return c . Invoke ( new object [ ] { handle , transfer } ) ;
414+ var args = new object [ ] {
415+ reference . Handle ,
416+ JniHandleOwnership . DoNotTransfer ,
417+ } ;
418+ var p = ( IJavaPeerable ) c . Invoke ( args ) ;
419+ JniObjectReference . Dispose ( ref reference , options ) ;
420+ return p ;
338421 }
339- c = type . GetConstructor ( flags , null , JIConstructorSignature , null ) ;
422+ c = type . GetConstructor ( ActivationConstructorBindingFlags , null , JIConstructorSignature , null ) ;
340423 if ( c != null ) {
341- JniObjectReference r = new JniObjectReference ( handle ) ;
342- JniObjectReferenceOptions o = JniObjectReferenceOptions . Copy ;
343- var peer = ( IJavaPeerable ) c . Invoke ( new object [ ] { r , o } ) ;
344- JNIEnv . DeleteRef ( handle , transfer ) ;
345- return peer ;
424+ var args = new object [ ] {
425+ reference ,
426+ options ,
427+ } ;
428+ var p = ( IJavaPeerable ) c . Invoke ( args ) ;
429+ reference = ( JniObjectReference ) args [ 0 ] ;
430+ return p ;
346431 }
347- throw new MissingMethodException (
348- "No constructor found for " + type . FullName + "::.ctor(System.IntPtr, Android.Runtime.JniHandleOwnership)" ,
349- CreateJavaLocationException ( ) ) ;
350- }
351-
352- static Exception CreateJavaLocationException ( )
353- {
354- using ( var loc = new Java . Lang . Error ( "Java callstack:" ) )
355- return new JavaLocationException ( loc . ToString ( ) ) ;
432+ return null ;
356433 }
357434}
0 commit comments