Skip to content

Commit

Permalink
Remove GetTypeFromAssemblyQualifiedName()
Browse files Browse the repository at this point in the history
Fixes: #1165
Context: #1157

If we more strongly rely on JNI signatures, we can remove the need
for Java Callable Wrappers to contain assembly-qualified type names,
thus removing the need for `ManagedPeer` to use `Type.GetType()`
entirely, removing the [IL2057][0] warnings.

Furthermore, if we add `[DynamicallyAccessedMembers]` to
`JniRuntime.JniTypeManager.GetType()`, we can fix some [IL2075][1]
warnings which appeared after fixing the IL2057 warnings.

Aside: Excising assembly-qualified type names from Java Callable
Wrappers had some "interesting" knock-on effects in the unit tests,
requiring that more typemap information be explicitly provided.
(This same information was *implicitly* provided before, via the
provision of assembly-qualified type names everywhere…)

[0]: https://learn.microsoft.com/en-us/dotnet/core/deploying/trimming/trim-warnings/IL2057
[1]: https://learn.microsoft.com/en-us/dotnet/core/deploying/trimming/trim-warnings/il2075
  • Loading branch information
jonpryor committed Nov 24, 2023
1 parent aeed449 commit 1e71988
Show file tree
Hide file tree
Showing 21 changed files with 107 additions and 74 deletions.
12 changes: 0 additions & 12 deletions samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,11 @@ class NativeAotTypeManager : JniRuntime.JniTypeManager {

#pragma warning disable IL2026
Dictionary<string, Type> typeMappings = new () {
["com/xamarin/java_interop/internal/JavaProxyThrowable"] = Type.GetType ("Java.Interop.JavaProxyThrowable, Java.Interop", throwOnError: true)!,
["example/ManagedType"] = typeof (Example.ManagedType),
["System.Int32, System.Runtime"] = typeof (int),
["System.Int32, System.Private.CoreLib"] = typeof (int),
["Java.Interop.JavaProxyThrowable, Java.Interop"] = Type.GetType ("Java.Interop.JavaProxyThrowable, Java.Interop", throwOnError: true)!,
["Example.ManagedType, Hello-NativeAOTFromJNI"] = typeof (Example.ManagedType),
};
#pragma warning restore IL2026


public override Type GetTypeFromAssemblyQualifiedName (string assemblyQualifiedTypeName)
{
if (typeMappings.TryGetValue (assemblyQualifiedTypeName, out var type))
return type;
throw new NotSupportedException ($"Unsupported type: \"{assemblyQualifiedTypeName}\"!");
}

protected override IEnumerable<Type> GetTypesForSimpleReference (string jniSimpleReference)
{
if (typeMappings.TryGetValue (jniSimpleReference, out var target))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -825,9 +825,7 @@ void GenerateRegisterType (TextWriter sw, JavaCallableWrapperGenerator self, str
case JavaPeerStyle.JavaInterop1:
sw.Write ("net.dot.jni.ManagedPeer.registerNativeMembers (");
sw.Write (self.name);
sw.Write (".class, \"");
sw.Write (managedTypeName);
sw.Write ("\", ");
sw.Write (".class, ");
sw.Write (field);
sw.WriteLine (");");
break;
Expand Down Expand Up @@ -1025,9 +1023,7 @@ void GenerateConstructor (Signature ctor, TextWriter sw)
switch (CodeGenerationTarget) {
case JavaPeerStyle.JavaInterop1:
sw.Write ("net.dot.jni.ManagedPeer.construct (this, \"");
sw.Write (type.GetPartialAssemblyQualifiedName (cache));
sw.Write ("\", \"");
sw.Write (ctor.ManagedParameters);
sw.Write (ctor.JniSignature);
sw.Write ("\", new java.lang.Object[] { ");
sw.Write (ctor.ActivateCall);
sw.WriteLine (" });");
Expand Down
4 changes: 4 additions & 0 deletions src/Java.Interop/Java.Interop/JavaProxyObject.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;

namespace Java.Interop {

Expand Down Expand Up @@ -72,6 +73,7 @@ public override bool Equals (object? obj)
}

// TODO: Keep in sync with the code generated by ExportedMemberBuilder
[UnmanagedFunctionPointer (CallingConvention.Winapi)]
delegate bool EqualsMarshalMethod (IntPtr jnienv, IntPtr n_self, IntPtr n_value);
static bool Equals (IntPtr jnienv, IntPtr n_self, IntPtr n_value)
{
Expand All @@ -92,6 +94,7 @@ static bool Equals (IntPtr jnienv, IntPtr n_self, IntPtr n_value)
}

// TODO: Keep in sync with the code generated by ExportedMemberBuilder
[UnmanagedFunctionPointer (CallingConvention.Winapi)]
delegate int GetHashCodeMarshalMethod (IntPtr jnienv, IntPtr n_self);
static int GetHashCode (IntPtr jnienv, IntPtr n_self)
{
Expand All @@ -109,6 +112,7 @@ static int GetHashCode (IntPtr jnienv, IntPtr n_self)
}
}

[UnmanagedFunctionPointer (CallingConvention.Winapi)]
delegate IntPtr ToStringMarshalMethod (IntPtr jnienv, IntPtr n_self);
static IntPtr ToString (IntPtr jnienv, IntPtr n_self)
{
Expand Down
4 changes: 4 additions & 0 deletions src/Java.Interop/Java.Interop/JniBuiltinMarshalers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,9 @@ static Dictionary<string, Type> InitJniBuiltinSimpleReferenceToType ()
{
return new Dictionary<string, Type> (StringComparer.Ordinal) {
{"java/lang/String", typeof (string)},
{"net/dot/jni/internal/JavaProxyObject", typeof (JavaProxyObject)},
{"net/dot/jni/internal/JavaProxyThrowable", typeof (JavaProxyThrowable)},
{"net/dot/jni/ManagedPeer", typeof (ManagedPeer)},
{"V", typeof (void)},
{"Z", typeof (Boolean)},
{"java/lang/Boolean", typeof (Boolean?)},
Expand All @@ -156,6 +159,7 @@ static KeyValuePair<Type, JniValueMarshaler>[] InitJniBuiltinMarshalers ()
{
return new []{
new KeyValuePair<Type, JniValueMarshaler>(typeof (string), JniStringValueMarshaler.Instance),
new KeyValuePair<Type, JniValueMarshaler>(typeof (JavaProxyObject), ProxyValueMarshaler.Instance),
new KeyValuePair<Type, JniValueMarshaler>(typeof (Boolean), JniBooleanValueMarshaler.Instance),
new KeyValuePair<Type, JniValueMarshaler>(typeof (Boolean?), JniNullableBooleanValueMarshaler.Instance),
new KeyValuePair<Type, JniValueMarshaler>(typeof (SByte), JniSByteValueMarshaler.Instance),
Expand Down
4 changes: 4 additions & 0 deletions src/Java.Interop/Java.Interop/JniBuiltinMarshalers.tt
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@ namespace Java.Interop {
{
return new Dictionary<string, Type> (StringComparer.Ordinal) {
{"java/lang/String", typeof (string)},
{"net/dot/jni/internal/JavaProxyObject", typeof (JavaProxyObject)},
{"net/dot/jni/internal/JavaProxyThrowable", typeof (JavaProxyThrowable)},
{"net/dot/jni/ManagedPeer", typeof (ManagedPeer)},
{"V", typeof (void)},
<#
foreach (var type in types) {
Expand All @@ -119,6 +122,7 @@ namespace Java.Interop {
{
return new []{
new KeyValuePair<Type, JniValueMarshaler>(typeof (string), JniStringValueMarshaler.Instance),
new KeyValuePair<Type, JniValueMarshaler>(typeof (JavaProxyObject), ProxyValueMarshaler.Instance),
<#
foreach (var type in types) {
#>
Expand Down
16 changes: 16 additions & 0 deletions src/Java.Interop/Java.Interop/JniMemberSignature.cs
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,22 @@ public JniMemberSignature (string memberName, string memberSignature)
this.memberSignature = memberSignature;
}

internal static IEnumerable<JniTypeSignature> GetParameterTypesFromMethodSignature (string jniMethodSignature)
{
if (jniMethodSignature.Length < "()V".Length || jniMethodSignature [0] != '(' ) {
throw new ArgumentException (
$"Member signature `{jniMethodSignature}` is not a method signature. Method signatures must start with `(`.",
nameof (jniMethodSignature));
}
int index = 1;
while (index < jniMethodSignature.Length &&
jniMethodSignature [index] != ')') {
var (start, length) = ExtractType (jniMethodSignature, ref index);
var jniType = jniMethodSignature.Substring (start, length);
yield return JniTypeSignature.Parse (jniType);
}
}

public static int GetParameterCountFromMethodSignature (string jniMethodSignature)
{
if (jniMethodSignature.Length < "()V".Length || jniMethodSignature [0] != '(' ) {
Expand Down
39 changes: 35 additions & 4 deletions src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,42 @@ internal JniInstanceMethods GetConstructorsForType (Type declaringType)
if (declaringType == DeclaringType)
return this;

JniInstanceMethods? methods;

lock (SubclassConstructors) {
if (!SubclassConstructors.TryGetValue (declaringType, out var methods)) {
methods = new JniInstanceMethods (declaringType);
SubclassConstructors.Add (declaringType, methods);
}
if (SubclassConstructors.TryGetValue (declaringType, out methods))
return methods;
}
// Init outside of `lock` in case we have recursive access:
// System.ArgumentException: An item with the same key has already been added. Key: Java.Interop.JavaProxyThrowable
// at System.Collections.Generic.Dictionary`2.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
// at System.Collections.Generic.Dictionary`2.Add(TKey key, TValue value)
// at Java.Interop.JniPeerMembers.JniInstanceMethods.GetConstructorsForType(Type declaringType) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs:line 80
// at Java.Interop.JniPeerMembers.JniInstanceMethods.GetConstructorsForType(Type declaringType) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs:line 80
// at Java.Interop.JniPeerMembers.JniInstanceMethods.StartCreateInstance(String constructorSignature, Type declaringType, JniArgumentValue* parameters) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs:line 146
// at Java.Interop.JavaException..ctor(String message) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JavaException.cs:line 52
// at Java.Interop.JavaProxyThrowable..ctor(Exception exception) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JavaProxyThrowable.cs:line 15
// at Java.Interop.JniEnvironment.Exceptions.Throw(Exception e) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JniEnvironment.Errors.cs:line 39
// at Java.Interop.JniRuntime.RaisePendingException(Exception pendingException) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JniRuntime.cs:line 444
// at Java.Interop.JniTransition.Dispose() in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JniTransition.cs:line 39
// at Java.Interop.ManagedPeer.RegisterNativeMembers(IntPtr jnienv, IntPtr klass, IntPtr n_nativeClass, IntPtr n_methods) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/ManagedPeer.cs:line 195
// at Java.Interop.NativeMethods.java_interop_jnienv_find_class(IntPtr jnienv, IntPtr& thrown, String classname)
// at Java.Interop.NativeMethods.java_interop_jnienv_find_class(IntPtr jnienv, IntPtr& thrown, String classname)
// at Java.Interop.JniEnvironment.Types.TryRawFindClass(IntPtr env, String classname, IntPtr& klass, IntPtr& thrown) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JniEnvironment.Types.cs:line 135
// at Java.Interop.JniEnvironment.Types.TryFindClass(String classname, Boolean throwOnError) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JniEnvironment.Types.cs:line 49
// at Java.Interop.JniEnvironment.Types.FindClass(String classname) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JniEnvironment.Types.cs:line 37
// at Java.Interop.JniType..ctor(String classname) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JniType.cs:line 51
// at Java.Interop.JniPeerMembers.JniInstanceMethods..ctor(Type declaringType) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs:line 27
// at Java.Interop.JniPeerMembers.JniInstanceMethods.GetConstructorsForType(Type declaringType) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs:line 77
// at Java.Interop.JniPeerMembers.JniInstanceMethods.StartCreateInstance(String constructorSignature, Type declaringType, JniArgumentValue* parameters) in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Interop/Java.Interop/JniPeerMembers.JniInstanceMethods.cs:line 146
// at Java.Lang.Object..ctor() in /Users/jon/Developer/src/xamarin/java.interop/src/Java.Base/obj/Debug-net7.0/mcw/Java.Lang.Object.cs:line 32
// at Java.BaseTests.MyIntConsumer..ctor(Action`1 action) in /Users/jon/Developer/src/xamarin/java.interop/tests/Java.Base-Tests/Java.Base/JavaToManagedTests.cs:line 77
// at Java.BaseTests.JavaToManagedTests.InterfaceInvokerMethod() in /Users/jon/Developer/src/xamarin/java.interop/tests/Java.Base-Tests/Java.Base/JavaToManagedTests.cs:line 26
methods = new JniInstanceMethods (declaringType);
lock (SubclassConstructors) {
if (SubclassConstructors.TryGetValue (declaringType, out var m))
return m;
SubclassConstructors.Add (declaringType, methods);
return methods;
}
}
Expand Down
13 changes: 1 addition & 12 deletions src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ protected virtual IEnumerable<string> GetSimpleReferences (Type type)
static readonly Type[] EmptyTypeArray = Array.Empty<Type> ();


[return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)]
public Type? GetType (JniTypeSignature typeSignature)
{
AssertValid ();
Expand Down Expand Up @@ -372,18 +373,6 @@ IEnumerable<Type> CreateGetTypesForSimpleReferenceEnumerator (string jniSimpleRe

protected virtual ReplacementMethodInfo? GetReplacementMethodInfoCore (string jniSimpleReference, string jniMethodName, string jniMethodSignature) => null;

#if FEATURE_NATIVE_AOT
public virtual
#else // FEATURE_NATIVE_AOT
internal
#endif // FEATURE_NATIVE_AOT
Type GetTypeFromAssemblyQualifiedName (string assemblyQualifiedTypeName)
{
AssertValid ();

return Type.GetType (assemblyQualifiedTypeName, throwOnError: true)!;
}

public virtual void RegisterNativeMembers (JniType nativeClass, Type type, ReadOnlySpan<char> methods)
{
TryRegisterNativeMembers (nativeClass, type, methods);
Expand Down
37 changes: 23 additions & 14 deletions src/Java.Interop/Java.Interop/ManagedPeer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
Expand Down Expand Up @@ -48,21 +49,19 @@ public override JniPeerMembers JniPeerMembers {
get {return _members;}
}

const string ConstructSignature = "(Ljava/lang/Object;Ljava/lang/String;Ljava/lang/String;[Ljava/lang/Object;)V";
const string ConstructSignature = "(Ljava/lang/Object;Ljava/lang/String;[Ljava/lang/Object;)V";

// TODO: Keep in sync with the code generated by ExportedMemberBuilder
[UnmanagedFunctionPointer (CallingConvention.Winapi)]
delegate void ConstructMarshalMethod (IntPtr jnienv,
IntPtr klass,
IntPtr n_self,
IntPtr n_assemblyQualifiedName,
IntPtr n_constructorSignature,
IntPtr n_constructorArguments);
static void Construct (
IntPtr jnienv,
IntPtr klass,
IntPtr n_self,
IntPtr n_assemblyQualifiedName,
IntPtr n_constructorSignature,
IntPtr n_constructorArguments)
{
Expand Down Expand Up @@ -93,7 +92,9 @@ static void Construct (
return;
}

var type = typeMgr.GetTypeFromAssemblyQualifiedName (JniEnvironment.Strings.ToString (n_assemblyQualifiedName)!);
var typeSig = new JniTypeSignature (JniEnvironment.Types.GetJniTypeNameFromInstance (r_self));
var type = GetTypeFromSignature (typeMgr, typeSig);

if (type.IsGenericTypeDefinition) {
throw new NotSupportedException (
"Constructing instances of generic types from Java is not supported, as the type parameters cannot be determined.",
Expand Down Expand Up @@ -153,10 +154,11 @@ static Type[] GetParameterTypes (JniRuntime.JniTypeManager typeMgr, string? sign
{
if (string.IsNullOrEmpty (signature))
return Array.Empty<Type> ();
var typeNames = signature!.Split (':');
var ptypes = new Type [typeNames.Length];
for (int i = 0; i < typeNames.Length; i++)
ptypes [i] = typeMgr.GetTypeFromAssemblyQualifiedName (typeNames [i]);
var ptypes = new Type [JniMemberSignature.GetParameterCountFromMethodSignature (signature)];
int i = 0;
foreach (var jniType in JniMemberSignature.GetParameterTypesFromMethodSignature (signature)) {
ptypes [i++] = GetTypeFromSignature (typeMgr, jniType, signature);
}
return ptypes;
}

Expand All @@ -178,32 +180,33 @@ static Type[] GetParameterTypes (JniRuntime.JniTypeManager typeMgr, string? sign
return pvalues;
}

const string RegisterNativeMembersSignature = "(Ljava/lang/Class;Ljava/lang/String;Ljava/lang/String;)V";
const string RegisterNativeMembersSignature = "(Ljava/lang/Class;Ljava/lang/String;)V";

[UnmanagedFunctionPointer (CallingConvention.Winapi)]
delegate void RegisterMarshalMethod (IntPtr jnienv,
IntPtr klass,
IntPtr n_nativeClass,
IntPtr n_assemblyQualifiedName,
IntPtr n_methods);
static unsafe void RegisterNativeMembers (
IntPtr jnienv,
IntPtr klass,
IntPtr n_nativeClass,
IntPtr n_assemblyQualifiedName,
IntPtr n_methods)
{
var envp = new JniTransition (jnienv);
try {
var r_nativeClass = new JniObjectReference (n_nativeClass);
#pragma warning disable CA2000
var nativeClass = new JniType (ref r_nativeClass, JniObjectReferenceOptions.Copy);
#pragma warning restore CA2000

var assemblyQualifiedName = JniEnvironment.Strings.ToString (new JniObjectReference (n_assemblyQualifiedName));
var methodsRef = new JniObjectReference (n_methods);

var typeSig = new JniTypeSignature (nativeClass.Name);
var type = GetTypeFromSignature (JniEnvironment.Runtime.TypeManager, typeSig);

#if NET

var type = JniEnvironment.Runtime.TypeManager.GetTypeFromAssemblyQualifiedName (assemblyQualifiedName!);

int methodsLength = JniEnvironment.Strings.GetStringLength (methodsRef);
var methodsChars = JniEnvironment.Strings.GetStringChars (methodsRef, null);
Expand All @@ -215,7 +218,6 @@ static unsafe void RegisterNativeMembers (
JniEnvironment.Strings.ReleaseStringChars (methodsRef, methodsChars);
}
#else // NET
var type = Type.GetType (assemblyQualifiedName!, throwOnError: true)!;
var methods = JniEnvironment.Strings.ToString (methodsRef);
JniEnvironment.Runtime.TypeManager.RegisterNativeMembers (nativeClass, type, methods);
#endif // NET
Expand All @@ -229,6 +231,13 @@ static unsafe void RegisterNativeMembers (
envp.Dispose ();
}
}

[return: DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)]
static Type GetTypeFromSignature (JniRuntime.JniTypeManager typeManager, JniTypeSignature typeSignature, string? context = null)
{
return typeManager.GetType (typeSignature) ??
throw new NotSupportedException ($"Could not find System.Type corresponding to Java type {typeSignature} {(context == null ? "" : "within `" + context + "`")}.");
}
}

sealed class JniLocationException : Exception {
Expand Down
2 changes: 0 additions & 2 deletions src/Java.Interop/java/net/dot/jni/ManagedPeer.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,10 @@ private ManagedPeer () {

public static native void registerNativeMembers (
java.lang.Class<?> nativeClass,
String assemblyQualifiedName,
String methods);

public static native void construct (
Object self,
String assemblyQualifiedName,
String constructorSignature,
Object... arguments
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,9 @@
extends java.lang.Object
implements GCUserPeerable
{
static final String assemblyQualifiedName = "Java.Interop.JavaProxyObject, Java.Interop";
static {
net.dot.jni.ManagedPeer.registerNativeMembers (
JavaProxyObject.class,
assemblyQualifiedName,
"");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,9 @@
extends java.lang.Error
implements GCUserPeerable
{
static final String assemblyQualifiedName = "Java.Interop.JavaProxyThrowable, Java.Interop";
static {
net.dot.jni.ManagedPeer.registerNativeMembers (
JavaProxyThrowable.class,
assemblyQualifiedName,
"");
}

Expand Down
1 change: 1 addition & 0 deletions tests/Java.Base-Tests/Java.Base/JavaVMFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ static partial void CreateJavaVM ()
var c = new TestJVM (
jars: new[]{ "java.base-tests.jar" },
typeMappings: new Dictionary<string, Type> () {
["example/MyIntConsumer"] = typeof (MyIntConsumer),
["example/MyRunnable"] = typeof (MyRunnable),
[JavaInvoker.JniTypeName] = typeof (JavaInvoker),
}
Expand Down
3 changes: 3 additions & 0 deletions tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ class JavaVMFixtureTypeManager : JniRuntime.JniTypeManager {
[GenericHolder<int>.JniTypeName] = typeof (GenericHolder<>),
[RenameClassBase.JniTypeName] = typeof (RenameClassBase),
[RenameClassDerived.JniTypeName] = typeof (RenameClassDerived),
[CallVirtualFromConstructorBase.JniTypeName] = typeof (CallVirtualFromConstructorBase),
[CallVirtualFromConstructorDerived.JniTypeName] = typeof (CallVirtualFromConstructorDerived),
[GetThis.JniTypeName] = typeof (GetThis),
};

public JavaVMFixtureTypeManager ()
Expand Down
Loading

0 comments on commit 1e71988

Please sign in to comment.