@@ -17,6 +17,8 @@ namespace NativeAOT;
17
17
18
18
internal class NativeAotValueManager : JniRuntime . JniValueManager
19
19
{
20
+ const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes . PublicConstructors | DynamicallyAccessedMemberTypes . NonPublicConstructors ;
21
+
20
22
readonly NativeAotTypeManager TypeManager ;
21
23
Dictionary < int , List < IJavaPeerable > > ? RegisteredInstances = new Dictionary < int , List < IJavaPeerable > > ( ) ;
22
24
@@ -253,105 +255,180 @@ public override List<JniSurfacedPeerInfo> GetSurfacedPeers ()
253
255
254
256
public override IJavaPeerable ? CreatePeer (
255
257
ref JniObjectReference reference ,
256
- JniObjectReferenceOptions options ,
257
- [ DynamicallyAccessedMembers ( DynamicallyAccessedMemberTypes . PublicConstructors | DynamicallyAccessedMemberTypes . NonPublicConstructors ) ]
258
+ JniObjectReferenceOptions transfer ,
259
+ [ DynamicallyAccessedMembers ( Constructors ) ]
258
260
Type ? targetType )
259
261
{
260
- if ( ! reference . IsValid )
262
+ if ( ! reference . IsValid ) {
261
263
return null ;
264
+ }
262
265
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 ) ;
267
268
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 ) ) ;
281
271
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 ) {
284
274
throw new ArgumentException ( $ "Could not determine Java type corresponding to `{ targetType . AssemblyQualifiedName } `.", nameof ( targetType ) ) ;
285
275
}
286
276
287
- JniObjectReference typeClass = default ;
288
- JniObjectReference handleClass = default ;
277
+ var refClass = JniEnvironment . Types . GetObjectClass ( reference ) ;
278
+ JniObjectReference targetClass ;
289
279
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 ) ;
297
329
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 ) )
300
334
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
+ }
301
343
}
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 ;
305
352
}
353
+ JniObjectReference . Dispose ( ref klass , JniObjectReferenceOptions . CopyAndDispose ) ;
306
354
307
- IJavaPeerable ? result = null ;
355
+ return TryCreatePeerProxy ( fallbackType , ref reference , options ) ;
356
+ }
308
357
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 ;
319
364
}
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 ;
321
373
}
322
374
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
+
323
407
static readonly Type [ ] XAConstructorSignature = new Type [ ] { typeof ( IntPtr ) , typeof ( JniHandleOwnership ) } ;
324
408
static readonly Type [ ] JIConstructorSignature = new Type [ ] { typeof ( JniObjectReference ) . MakeByRefType ( ) , typeof ( JniObjectReferenceOptions ) } ;
325
409
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 )
331
411
{
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 ) ;
336
413
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 ;
338
421
}
339
- c = type . GetConstructor ( flags , null , JIConstructorSignature , null ) ;
422
+ c = type . GetConstructor ( ActivationConstructorBindingFlags , null , JIConstructorSignature , null ) ;
340
423
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 ;
346
431
}
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 ;
356
433
}
357
434
}
0 commit comments