Skip to content
This repository was archived by the owner on Jul 6, 2026. It is now read-only.

Commit 7a058c0

Browse files
authored
[Java.Interop] Add IJavaPeerable.JavaAs() extension method (#1234)
Fixes: #10Fixes: dotnet/android#9038 Context: 1adb796 Imagine the following Java type hierarchy: // Java public abstract class Drawable { public static Drawable createFromStream(IntputStream is, String srcName) {…} // … } public interface Animatable { public void start(); // … } /* package */ class SomeAnimatableDrawable extends Drawable implements Animatable { // … } Further imagine that a call to `Drawable.createFromStream()` returns an instance of `SomeAnimatableDrawable`. What does the *binding* `Drawable.CreateFromStream()` return? // C# var drawable = Drawable.CreateFromStream(input, name); // What is the runtime type of `drawable`? The binding `Drawable.CreateFromStream()` look at the runtime type of the value returned, sees that it's of type `SomeAnimatableDrawable`, and looks for an existing binding of that type. If no such binding is found -- which will be the case here, as `SomeAnimatableDrawable` is package-private -- then we check the value's base class, ad infinitum, until we hit a type that we *do* have a binding for (or fail catastrophically if we can't find a binding for `java.lang.Object`). See also [`TypeManager.CreateInstance()`][0], which is similar to the code within `JniRuntime.JniValueManager.GetPeerConstructor()`. Any interfaces implemented by Java value are not consulted, only the base class hierarchy is consulted. Consequently, the runtime type of `drawable` would be the `Drawable` binding; however, as `Drawable` is an `abstract` type, the runtime type will *actually* be `DrawableInvoker` (see e.g. 1adb796), akin to: // emitted by `generator`… internal class DrawableInvoker : Drawable { // … } Further imagine that we want to invoke `Animatable` methods on `drawable`. How do we do this? This is where the [`.JavaCast<TResult>()` extension method][1] comes in: we can use `.JavaCast<TResult>()` to perform a Java-side type check for the desired type, which returns a value which can be used to invoke methods on the specified type: var animatable = drawable.JavaCast<IAnimatable>(); animatable.Start(); The problem with `.JavaCast<TResult>()` is that it always throws on failure: var someOtherIface = drawable.JavaCast<ISomethingElse>(); // throws some exception… @mattleibow requests an "exception-free JavaCast overload" so that he can *easily* use type-specific functionality *optionally*. Add the following extension methods to `IJavaPeerable`: static partial class JavaPeerableExtensions { public static TResult? JavaAs<TResult>( this IJavaPeerable self); public static bool TryJavaCast<TResult>( this IJavaPeerable self, out TResult? result); } The `.JavaAs<TResult>()` extension method mirrors the C# `as` operator, returning `null` if the the runtime type of `self` is not implicitly convertible to the Java type corresponding to `TResult`. This makes it useful for one-off invocations: drawable.JavaAs<IAnimatable>()?.Start(); The `.TryJavaCast<TResult>()` extension method follows the [`TryParse()` pattern][2], returning true if the type coercion succeeds and the output `result` parameter is non-null, and false otherwise. This allows "nicely scoping" things within an `if`: if (drawable.TryJavaCast<IAnimatable>(out var animatable)) { animatable.Start(); // … animatable.Stop(); } [0]: https://github.com/dotnet/android/blob/06bb1dc6a292ef5618a3bb6ecca3ca869253ff2e/src/Mono.Android/Java.Interop/TypeManager.cs#L276-L291 [1]: https://github.com/dotnet/android/blob/06bb1dc6a292ef5618a3bb6ecca3ca869253ff2e/src/Mono.Android/Android.Runtime/Extensions.cs#L9-L17 [2]: https://learn.microsoft.com/dotnet/standard/design-guidelines/exceptions-and-performance#try-parse-pattern
1 parent 6f9defa commit 7a058c0

12 files changed

Lines changed: 343 additions & 7 deletions

File tree

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
<?xml version="1.0"?>
2+
<docs>
3+
<membername="T:JavaPeerableExtensions">
4+
<summary>
5+
Extension methods on <seecref="T:Java.Interop.IJavaPeerable" />.
6+
</summary>
7+
<remarks />
8+
</member>
9+
<membername="M:GetJniTypeName">
10+
<summary>Gets the JNI name of the type of the instance <paramrefname="self" />.</summary>
11+
<paramname="self">
12+
The <seecref="T:Java.Interop.IJavaPeerable" /> instance
13+
to get the JNI type name of.
14+
</param>
15+
<remarks>
16+
<para>
17+
The JNI type name is the name of the Java type, as it would be
18+
used in Java Native Interface (JNI) API calls. For example,
19+
instead of the Java name <c>java.lang.Object</c>, the JNI name
20+
is <c>java/lang/Object</c>.
21+
</para>
22+
</remarks>
23+
</member>
24+
<membername="M:TryJavaCast">
25+
<typeparamname="TResult">
26+
The type to coerce <paramrefname="self" /> to.
27+
</typeparam>
28+
<paramname="self">
29+
A <seecref="T:Java.Interop.IJavaPeerable" /> instance
30+
to coerce to type <typeparamrefname="TResult" />.
31+
</param>
32+
<paramname="result">
33+
When this method returns, contains a value of type
34+
<typeparamrefname="TResult" /> if <paramrefname="self" /> can be
35+
coerced to the Java type corresponding to <typeparamrefname="TResult" />,
36+
or <c>null</c> if the coercion is not valid.
37+
</param>
38+
<summary>
39+
Try to coerce <paramrefname="self" /> to type <typeparamrefname="TResult" />,
40+
checking that the coercion is valid on the Java side.
41+
</summary>
42+
<returns>
43+
<seelangword="true" /> if <pramrefname="self" /> was converted successfully;
44+
otherwise, <seelangword="false" />.
45+
</returns>
46+
<remarks>
47+
<blocksubset="none"type="note">
48+
Implementations of <seecref="T:Java.Interop.IJavaPeerable" /> consist
49+
of two halves: a <i>Java peer</i> and a <i>managed peer</i>.
50+
The <seecref="P:Java.Interop.IJavaPeerable.PeerReference" /> property
51+
associates the managed peer to the Java peer.
52+
</block>
53+
<blocksubset="none"type="note">
54+
The <seecref="T:Java.Interop.JniTypeSignatureAttribute" /> or
55+
<seecref="T:Android.Runtime.RegisterAttribute" /> custom attributes are
56+
used to associated a managed type to a Java type.
57+
</block>
58+
</remarks>
59+
<exceptioncref="T:System.ArgumentException">
60+
<para>
61+
The Java peer type for <typeparamrefname="TResult" /> could not be found.
62+
</para>
63+
</exception>
64+
<exceptioncref="T:System.NotSupportedException">
65+
<para>
66+
The type <typeparamrefname="TResult" /> or a <i>Invoker type</i> for
67+
<typeparamrefname="TResult" /> does not provide an
68+
<i>activation constructor</i>, a constructor with a singature of
69+
<c>(ref JniObjectReference, JniObjectReferenceOptions)</c> or
70+
<c>(IntPtr, JniHandleOwnership)</c>.
71+
</para>
72+
</exception>
73+
<seealsocref="M:Java.Interop.JavaPeerableExtensions.JavaAs``1(Java.Interop.IJavaPeerable)" />
74+
</member>
75+
<membername="M:JavaAs">
76+
<typeparamname="TResult">
77+
The type to coerce <paramrefname="self" /> to.
78+
</typeparam>
79+
<paramname="self">
80+
A <seecref="T:Java.Interop.IJavaPeerable" /> instance
81+
to coerce to type <typeparamrefname="TResult" />.
82+
</param>
83+
<summary>
84+
Try to coerce <paramrefname="self" /> to type <typeparamrefname="TResult" />,
85+
checking that the coercion is valid on the Java side.
86+
</summary>
87+
<returns>
88+
A value of type <typeparamrefname="TResult" /> if the Java peer to
89+
<paramrefname="self" /> can be coerced to the Java type corresponding
90+
to <typeparamrefname="TResult" />; otherwise, <c>null</c>.
91+
</returns>
92+
<remarks>
93+
<blocksubset="none"type="note">
94+
Implementations of <seecref="T:Java.Interop.IJavaPeerable" /> consist
95+
of two halves: a <i>Java peer</i> and a <i>managed peer</i>.
96+
The <seecref="P:Java.Interop.IJavaPeerable.PeerReference" /> property
97+
associates the managed peer to the Java peer.
98+
</block>
99+
<blocksubset="none"type="note">
100+
The <seecref="T:Java.Interop.JniTypeSignatureAttribute" /> or
101+
<seecref="T:Android.Runtime.RegisterAttribute" /> custom attributes are
102+
used to associated a managed type to a Java type.
103+
</block>
104+
</remarks>
105+
<exceptioncref="T:System.ArgumentException">
106+
<para>
107+
The Java peer type for <typeparamrefname="TResult" /> could not be found.
108+
</para>
109+
</exception>
110+
<exceptioncref="T:System.NotSupportedException">
111+
<para>
112+
The type <typeparamrefname="TResult" /> or a <i>Invoker type</i> for
113+
<typeparamrefname="TResult" /> does not provide an
114+
<i>activation constructor</i>, a constructor with a singature of
115+
<c>(ref JniObjectReference, JniObjectReferenceOptions)</c> or
116+
<c>(IntPtr, JniHandleOwnership)</c>.
117+
</para>
118+
</exception>
119+
<seealsocref="P:Java.Interop.JavaPeerableExtensions.TryJavaCast``1(Java.Interop.IJavaPeerable)" />
120+
</member>
121+
</docs>

‎src/Java.Interop/Java.Interop/JavaObject.cs‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ namespace Java.Interop
88
[JniTypeSignature("java/lang/Object",GenerateJavaPeer=false)]
99
unsafepublicclassJavaObject:IJavaPeerable
1010
{
11-
internalconstDynamicallyAccessedMemberTypesConstructorsAndInterfaces=DynamicallyAccessedMemberTypes.PublicConstructors|DynamicallyAccessedMemberTypes.NonPublicConstructors|DynamicallyAccessedMemberTypes.Interfaces;
11+
internalconstDynamicallyAccessedMemberTypesConstructors=DynamicallyAccessedMemberTypes.PublicConstructors|DynamicallyAccessedMemberTypes.NonPublicConstructors;
12+
internalconstDynamicallyAccessedMemberTypesConstructorsAndInterfaces=Constructors|DynamicallyAccessedMemberTypes.Interfaces;
1213

1314
readonlystaticJniPeerMembers_members=newJniPeerMembers("java/lang/Object",typeof(JavaObject));
1415

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,51 @@
11
#nullable enable
22

33
usingSystem;
4+
usingSystem.Diagnostics.CodeAnalysis;
45

56
namespaceJava.Interop{
67

8+
/// <include file="../Documentation/Java.Interop/JavaPeerableExtensions.xml" path="/docs/member[@name='T:JavaPeerableExtensions']/*" />
79
publicstaticclassJavaPeerableExtensions{
810

11+
/// <include file="../Documentation/Java.Interop/JavaPeerableExtensions.xml" path="/docs/member[@name='M:GetJniTypeName']/*" />
912
publicstaticstring?GetJniTypeName(thisIJavaPeerableself)
1013
{
1114
JniPeerMembers.AssertSelf(self);
1215
returnJniEnvironment.Types.GetJniTypeNameFromInstance(self.PeerReference);
1316
}
17+
18+
/// <include file="../Documentation/Java.Interop/JavaPeerableExtensions.xml" path="/docs/member[@name='M:TryJavaCast']/*" />
19+
publicstaticboolTryJavaCast<
20+
[DynamicallyAccessedMembers(JavaObject.Constructors)]
21+
TResult
22+
>(thisIJavaPeerable?self,[NotNullWhen(true)]outTResult?result)
23+
whereTResult:class,IJavaPeerable
24+
{
25+
result=JavaAs<TResult>(self);
26+
returnresult!=null;
27+
}
28+
29+
/// <include file="../Documentation/Java.Interop/JavaPeerableExtensions.xml" path="/docs/member[@name='M:JavaAs']/*" />
30+
publicstaticTResult?JavaAs<
31+
[DynamicallyAccessedMembers(JavaObject.Constructors)]
32+
TResult
33+
>(thisIJavaPeerable?self)
34+
whereTResult:class,IJavaPeerable
35+
{
36+
if(self==null||!self.PeerReference.IsValid){
37+
returnnull;
38+
}
39+
40+
if(selfisTResultresult){
41+
returnresult;
42+
}
43+
44+
varr=self.PeerReference;
45+
returnJniEnvironment.Runtime.ValueManager.CreatePeer(
46+
refr,JniObjectReferenceOptions.Copy,
47+
targetType:typeof(TResult))
48+
asTResult;
49+
}
1450
}
1551
}

‎src/Java.Interop/Java.Interop/JniRuntime.JniValueManager.cs‎

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -276,16 +276,45 @@ static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type)
276276
if(disposed)
277277
thrownewObjectDisposedException(GetType().Name);
278278

279+
if(!reference.IsValid){
280+
returnnull;
281+
}
282+
279283
targetType=targetType??typeof(JavaObject);
280284
targetType=GetPeerType(targetType);
281285

282286
if(!typeof(IJavaPeerable).IsAssignableFrom(targetType))
283287
thrownewArgumentException($"targetType `{targetType.AssemblyQualifiedName}` must implement IJavaPeerable!",nameof(targetType));
284288

285-
varctor=GetPeerConstructor(reference,targetType);
286-
if(ctor==null)
289+
vartargetSig=Runtime.TypeManager.GetTypeSignature(targetType);
290+
if(!targetSig.IsValid||targetSig.SimpleReference==null){
291+
thrownewArgumentException($"Could not determine Java type corresponding to `{targetType.AssemblyQualifiedName}`.",nameof(targetType));
292+
}
293+
294+
varrefClass=JniEnvironment.Types.GetObjectClass(reference);
295+
JniObjectReferencetargetClass;
296+
try{
297+
targetClass=JniEnvironment.Types.FindClass(targetSig.SimpleReference);
298+
}catch(Exceptione){
299+
JniObjectReference.Dispose(refrefClass);
300+
thrownewArgumentException($"Could not find Java class `{targetSig.SimpleReference}`.",
301+
nameof(targetType),
302+
e);
303+
}
304+
305+
if(!JniEnvironment.Types.IsAssignableFrom(refClass,targetClass)){
306+
JniObjectReference.Dispose(refrefClass);
307+
JniObjectReference.Dispose(reftargetClass);
308+
returnnull;
309+
}
310+
311+
JniObjectReference.Dispose(reftargetClass);
312+
313+
varctor=GetPeerConstructor(refrefClass,targetType);
314+
if(ctor==null){
287315
thrownewNotSupportedException(string.Format("Could not find an appropriate constructable wrapper type for Java type '{0}', targetType='{1}'.",
288316
JniEnvironment.Types.GetJniTypeNameFromInstance(reference),targetType));
317+
}
289318

290319
varacts=newobject[]{
291320
reference,
@@ -303,11 +332,10 @@ static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type)
303332
staticreadonlyTypeByRefJniObjectReference=typeof(JniObjectReference).MakeByRefType();
304333

305334
ConstructorInfo?GetPeerConstructor(
306-
JniObjectReferenceinstance,
335+
refJniObjectReferenceklass,
307336
[DynamicallyAccessedMembers(Constructors)]
308337
TypefallbackType)
309338
{
310-
varklass=JniEnvironment.Types.GetObjectClass(instance);
311339
varjniTypeName=JniEnvironment.Types.GetJniTypeNameFromClass(klass);
312340

313341
Type?type=null;

‎src/Java.Interop/Java.Interop/ManagedPeer.cs‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ namespace Java.Interop {
1818
/* static */sealedclassManagedPeer:JavaObject{
1919

2020
internalconststringJniTypeName="net/dot/jni/ManagedPeer";
21-
internalconstDynamicallyAccessedMemberTypesConstructors=DynamicallyAccessedMemberTypes.PublicConstructors|DynamicallyAccessedMemberTypes.NonPublicConstructors;
2221
internalconstDynamicallyAccessedMemberTypesConstructorsMethodsNestedTypes=Constructors|DynamicallyAccessedMemberTypes.PublicMethods|DynamicallyAccessedMemberTypes.NonPublicMethods|DynamicallyAccessedMemberTypes.NonPublicNestedTypes;
2322

2423

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
11
#nullable enable
2+
static Java.Interop.JavaPeerableExtensions.TryJavaCast<TResult>(this Java.Interop.IJavaPeerable? self, out TResult? result) -> bool
3+
static Java.Interop.JavaPeerableExtensions.JavaAs<TResult>(this Java.Interop.IJavaPeerable? self) -> TResult?

‎tests/Java.Interop-Tests/Java.Interop-Tests.csproj‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@
4141
<JavaInteropTestJarInclude="$(MSBuildThisFileDirectory)java\net\dot\jni\test\CallVirtualFromConstructorBase.java" />
4242
<JavaInteropTestJarInclude="$(MSBuildThisFileDirectory)java\net\dot\jni\test\CallVirtualFromConstructorDerived.java" />
4343
<JavaInteropTestJarInclude="$(MSBuildThisFileDirectory)java\net\dot\jni\test\GetThis.java" />
44+
<JavaInteropTestJarInclude="$(MSBuildThisFileDirectory)java\net\dot\jni\test\JavaInterface.java" />
45+
<JavaInteropTestJarInclude="$(MSBuildThisFileDirectory)java\net\dot\jni\test\MyJavaInterfaceImpl.java" />
4446
<JavaInteropTestJarInclude="$(MSBuildThisFileDirectory)java\net\dot\jni\test\ObjectHelper.java" />
4547
<JavaInteropTestJarInclude="$(MSBuildThisFileDirectory)java\net\dot\jni\test\RenameClassBase1.java" />
4648
<JavaInteropTestJarInclude="$(MSBuildThisFileDirectory)java\net\dot\jni\test\RenameClassBase2.java" />
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
usingSystem;
2+
usingSystem.Collections.Generic;
3+
usingSystem.Linq;
4+
5+
usingJava.Interop;
6+
7+
usingNUnit.Framework;
8+
9+
namespaceJava.InteropTests;
10+
11+
[TestFixture]
12+
publicclassJavaPeerableExtensionsTests{
13+
14+
[Test]
15+
publicvoidJavaAs_Exceptions()
16+
{
17+
usingvarv=newMyJavaInterfaceImpl();
18+
19+
// The Java type corresponding to JavaObjectWithMissingJavaPeer doesn't exist
20+
Assert.Throws<ArgumentException>(()=>v.JavaAs<JavaObjectWithMissingJavaPeer>());
21+
22+
varr=v.PeerReference;
23+
usingvaro=newJavaObject(refr,JniObjectReferenceOptions.Copy);
24+
// MyJavaInterfaceImpl doesn't provide an activation constructor
25+
Assert.Throws<NotSupportedException>(()=>o.JavaAs<MyJavaInterfaceImpl>());
26+
#if !__ANDROID__
27+
// JavaObjectWithNoJavaPeer has no Java peer
28+
Assert.Throws<ArgumentException>(()=>v.JavaAs<JavaObjectWithNoJavaPeer>());
29+
#endif // !__ANDROID__
30+
}
31+
32+
[Test]
33+
publicvoidJavaAs_NullSelfReturnsNull()
34+
{
35+
Assert.AreEqual(null,JavaPeerableExtensions.JavaAs<IAndroidInterface>(null));
36+
}
37+
38+
publicvoidJavaAs_InvalidPeerRefReturnsNull()
39+
{
40+
varv=newMyJavaInterfaceImpl();
41+
v.Dispose();
42+
Assert.AreEqual(null,JavaPeerableExtensions.JavaAs<IJavaInterface>(v));
43+
}
44+
45+
[Test]
46+
publicvoidJavaAs_InstanceThatDoesNotImplementInterfaceReturnsNull()
47+
{
48+
usingvarv=newMyJavaInterfaceImpl();
49+
Assert.AreEqual(null,JavaPeerableExtensions.JavaAs<IAndroidInterface>(v));
50+
}
51+
52+
[Test]
53+
publicvoidJavaAs()
54+
{
55+
usingvarimpl=newMyJavaInterfaceImpl();
56+
usingvariface=impl.JavaAs<IJavaInterface>();
57+
Assert.IsNotNull(iface);
58+
Assert.AreEqual("Hello from Java!",iface.Value);
59+
}
60+
}
61+
62+
// Note: Java side implements JavaInterface, while managed binding DOES NOT.
63+
[JniTypeSignature(JniTypeName,GenerateJavaPeer=false)]
64+
publicclassMyJavaInterfaceImpl:JavaObject{
65+
internalconststringJniTypeName="net/dot/jni/test/MyJavaInterfaceImpl";
66+
67+
internalstaticreadonlyJniPeerMembers_members=newJniPeerMembers(JniTypeName,typeof(MyJavaInterfaceImpl));
68+
69+
publicoverrideJniPeerMembersJniPeerMembers{
70+
get{return_members;}
71+
}
72+
73+
publicunsafeMyJavaInterfaceImpl()
74+
:base(ref*InvalidJniObjectReference,JniObjectReferenceOptions.None)
75+
{
76+
conststringid="()V";
77+
varpeer=_members.InstanceMethods.StartCreateInstance(id,GetType(),null);
78+
Construct(refpeer,JniObjectReferenceOptions.CopyAndDispose);
79+
_members.InstanceMethods.FinishCreateInstance(id,this,null);
80+
}
81+
}
82+
83+
[JniTypeSignature(JniTypeName,GenerateJavaPeer=false)]
84+
interfaceIJavaInterface:IJavaPeerable{
85+
internalconststringJniTypeName="net/dot/jni/test/JavaInterface";
86+
87+
publicstringValue{
88+
[JniMethodSignatureAttribute("getValue","()Ljava/lang/String;")]
89+
get;
90+
}
91+
}
92+
93+
[JniTypeSignature(IJavaInterface.JniTypeName,GenerateJavaPeer=false)]
94+
internalclassIJavaInterfaceInvoker:JavaObject,IJavaInterface{
95+
96+
internalstaticreadonlyJniPeerMembers_members=newJniPeerMembers(IJavaInterface.JniTypeName,typeof(IJavaInterfaceInvoker));
97+
98+
publicoverrideJniPeerMembersJniPeerMembers{
99+
get{return_members;}
100+
}
101+
102+
publicIJavaInterfaceInvoker(refJniObjectReferencereference,JniObjectReferenceOptionsoptions)
103+
:base(refreference,options)
104+
{
105+
}
106+
107+
publicunsafestringValue{
108+
get{
109+
conststringid="getValue.()Ljava/lang/String;";
110+
varr=JniPeerMembers.InstanceMethods.InvokeVirtualObjectMethod(id,this,null);
111+
returnJniEnvironment.Strings.ToString(refr,JniObjectReferenceOptions.CopyAndDispose);
112+
}
113+
}
114+
}

‎tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,12 @@ class JavaVMFixtureTypeManager : JniRuntime.JniTypeManager {
4141
[CallVirtualFromConstructorDerived.JniTypeName]=typeof(CallVirtualFromConstructorDerived),
4242
[CrossReferenceBridge.JniTypeName]=typeof(CrossReferenceBridge),
4343
[GetThis.JniTypeName]=typeof(GetThis),
44+
[IAndroidInterface.JniTypeName]=typeof(IAndroidInterface),
45+
[IJavaInterface.JniTypeName]=typeof(IJavaInterface),
4446
[JavaDisposedObject.JniTypeName]=typeof(JavaDisposedObject),
4547
[JavaObjectWithMissingJavaPeer.JniTypeName]=typeof(JavaObjectWithMissingJavaPeer),
4648
[MyDisposableObject.JniTypeName]=typeof(JavaDisposedObject),
49+
[MyJavaInterfaceImpl.JniTypeName]=typeof(MyJavaInterfaceImpl),
4750
};
4851

4952
publicJavaVMFixtureTypeManager()

0 commit comments

Comments
 (0)