Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 9 additions & 18 deletions samples/Hello-NativeAOTFromAndroid/NativeAotTypeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,19 @@ partial class NativeAotTypeManager : JniRuntime.JniTypeManager {
["my/MainActivity"] = typeof (MainActivity),
};

public override void RegisterNativeMembers (
JniType nativeClass,
[DynamicallyAccessedMembers (MethodsAndPrivateNested)]
Type type,
ReadOnlySpan<char> methods)
{
Console.WriteLine ($"# jonp: RegisterNativeMembers: nativeClass={nativeClass} type=`{type}`");
base.RegisterNativeMembers (nativeClass, type, methods);
}


protected override IEnumerable<Type> GetTypesForSimpleReference (string jniSimpleReference)
{
Console.WriteLine ($"# jonp: GetTypesForSimpleReference: jniSimpleReference=`{jniSimpleReference}`");
if (typeMappings.TryGetValue (jniSimpleReference, out var target)) {
Console.WriteLine ($"# jonp: GetTypesForSimpleReference: jniSimpleReference=`{jniSimpleReference}` -> `{target}`");
if (typeMappings.TryGetValue (jniSimpleReference, out var target))
yield return target;
}
foreach (var t in base.GetTypesForSimpleReference (jniSimpleReference)) {
Console.WriteLine ($"# jonp: GetTypesForSimpleReference: jniSimpleReference=`{jniSimpleReference}` -> `{t}`");
foreach (var t in base.GetTypesForSimpleReference (jniSimpleReference))
yield return t;
}
}

protected override Type? GetTypeForSimpleReference (string jniSimpleReference)
{
if (typeMappings.TryGetValue (jniSimpleReference, out var target))
return target;
return base.GetTypeForSimpleReference (jniSimpleReference);
}

protected override IEnumerable<string> GetSimpleReferences (Type type)
Expand Down
7 changes: 7 additions & 0 deletions samples/Hello-NativeAOTFromJNI/NativeAotTypeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,13 @@ protected override IEnumerable<Type> GetTypesForSimpleReference (string jniSimpl
yield return t;
}

protected override Type? GetTypeForSimpleReference (string jniSimpleReference)
{
if (typeMappings.TryGetValue (jniSimpleReference, out var target))
return target;
return base.GetTypeForSimpleReference (jniSimpleReference);
}

protected override IEnumerable<string> GetSimpleReferences (Type type)
{
return base.GetSimpleReferences (type)
Expand Down
160 changes: 129 additions & 31 deletions src/Java.Interop/Java.Interop/JniRuntime.JniTypeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -261,28 +261,128 @@ protected virtual IEnumerable<string> GetSimpleReferences (Type type)

static readonly string[] EmptyStringArray = Array.Empty<string> ();
static readonly Type[] EmptyTypeArray = Array.Empty<Type> ();
const string NotUsedInAndroid = "This code path is not used in Android projects.";

// FIXME: https://github.com/dotnet/java-interop/issues/1192
[UnconditionalSuppressMessage ("Trimming", "IL3050", Justification = NotUsedInAndroid)]
static Type MakeArrayType (Type type) =>
type.MakeArrayType ();
readonly struct KnownArrayTypesInfo
{
public readonly Dictionary<Type, Type> ArrayTypes;
public readonly Dictionary<Type, Type> JavaObjectArrayTypes;

public KnownArrayTypesInfo (Dictionary<Type, Type> arrayTypes, Dictionary<Type, Type> javaObjectArrayTypes)
{
ArrayTypes = arrayTypes;
JavaObjectArrayTypes = javaObjectArrayTypes;
}
}

static readonly Lazy<KnownArrayTypesInfo> KnownArrayTypes = new Lazy<KnownArrayTypesInfo> (InitKnownArrayTypes);

static KnownArrayTypesInfo InitKnownArrayTypes ()
{
var arrayTypes = new Dictionary<Type, Type> ();
var javaObjectArrayTypes = new Dictionary<Type, Type> ();

AddKnownArrayTypes<string> (arrayTypes, javaObjectArrayTypes);

AddKnownPrimitiveArrayTypes<Boolean, JavaBooleanArray> (arrayTypes, javaObjectArrayTypes);
AddKnownPrimitiveArrayTypes<SByte, JavaSByteArray> (arrayTypes, javaObjectArrayTypes);
AddKnownPrimitiveArrayTypes<Char, JavaCharArray> (arrayTypes, javaObjectArrayTypes);
AddKnownPrimitiveArrayTypes<Int16, JavaInt16Array> (arrayTypes, javaObjectArrayTypes);
AddKnownPrimitiveArrayTypes<Int32, JavaInt32Array> (arrayTypes, javaObjectArrayTypes);
AddKnownPrimitiveArrayTypes<Int64, JavaInt64Array> (arrayTypes, javaObjectArrayTypes);
AddKnownPrimitiveArrayTypes<Single, JavaSingleArray> (arrayTypes, javaObjectArrayTypes);
AddKnownPrimitiveArrayTypes<Double, JavaDoubleArray> (arrayTypes, javaObjectArrayTypes);

return new KnownArrayTypesInfo (arrayTypes, javaObjectArrayTypes);
Comment on lines +279 to +295
}

static void AddKnownPrimitiveArrayTypes<
[DynamicallyAccessedMembers (Constructors)]
T,
[DynamicallyAccessedMembers (Constructors)]
TArray> (Dictionary<Type, Type> arrayTypes, Dictionary<Type, Type> javaObjectArrayTypes)
{
AddKnownArrayTypes<T> (arrayTypes, javaObjectArrayTypes);
AddKnownArrayTypes<JavaArray<T>> (arrayTypes, javaObjectArrayTypes);
AddKnownArrayTypes<JavaPrimitiveArray<T>> (arrayTypes, javaObjectArrayTypes);
AddKnownArrayTypes<TArray> (arrayTypes, javaObjectArrayTypes);
}

static void AddKnownArrayTypes<
[DynamicallyAccessedMembers (Constructors)]
T> (Dictionary<Type, Type> arrayTypes, Dictionary<Type, Type> javaObjectArrayTypes)
{
arrayTypes [typeof (T)] = typeof (T[]);
arrayTypes [typeof (T[])] = typeof (T[][]);
arrayTypes [typeof (T[][])] = typeof (T[][][]);
javaObjectArrayTypes [typeof (T)] = typeof (JavaObjectArray<T>);
javaObjectArrayTypes [typeof (JavaObjectArray<T>)] = typeof (JavaObjectArray<JavaObjectArray<T>>);
}

static bool TryMakeArrayType (Type type, out Type? arrayType) =>
KnownArrayTypes.Value.ArrayTypes.TryGetValue (type, out arrayType);

static bool TryMakeJavaObjectArrayType (Type type, out Type? arrayType) =>
KnownArrayTypes.Value.JavaObjectArrayTypes.TryGetValue (type, out arrayType);
Comment on lines +321 to +325

// FIXME: https://github.com/dotnet/java-interop/issues/1192
[UnconditionalSuppressMessage ("Trimming", "IL2055", Justification = NotUsedInAndroid)]
[UnconditionalSuppressMessage ("Trimming", "IL3050", Justification = NotUsedInAndroid)]
static Type MakeGenericType (Type type, Type arrayType) =>
type.MakeGenericType (arrayType);
static Type GetUnsupportedArrayType (Type type) =>
throw new NotSupportedException ($"Array type construction for `{type}` is not supported.");

static Type GetUnsupportedJavaObjectArrayType (Type type) =>
throw new NotSupportedException ($"Generic Java array wrapper type construction for `{type}` is not supported.");

[UnconditionalSuppressMessage ("Trimming", "IL2073", Justification = "Types returned here should be preserved via other means.")]
[return: DynamicallyAccessedMembers (MethodsConstructors)]
public Type? GetType (JniTypeSignature typeSignature)
{
AssertValid ();

return GetTypes (typeSignature).FirstOrDefault ();
if (!typeSignature.IsValid || typeSignature.SimpleReference == null)
return null;

var type = GetTypeForSimpleReference (typeSignature.SimpleReference);
if (type == null)
return null;
if (typeSignature.ArrayRank == 0)
return type;
throw new NotSupportedException ($"DAM-annotated type lookup for array signature `{typeSignature}` is not supported. Use {nameof (GetTypes)} instead.");
}

[return: DynamicallyAccessedMembers (MethodsConstructors)]
protected virtual Type? GetTypeForSimpleReference (string jniSimpleReference)
{
AssertValid ();
AssertSimpleReference (jniSimpleReference);

return jniSimpleReference switch {
"java/lang/String" => TypeOf<string> (),
"V" => TypeOfVoid (),
"Z" => TypeOf<Boolean> (),
"java/lang/Boolean" => TypeOf<Boolean?> (),
"B" => TypeOf<SByte> (),
"java/lang/Byte" => TypeOf<SByte?> (),
"C" => TypeOf<Char> (),
"java/lang/Character" => TypeOf<Char?> (),
"S" => TypeOf<Int16> (),
"java/lang/Short" => TypeOf<Int16?> (),
"I" => TypeOf<Int32> (),
"java/lang/Integer" => TypeOf<Int32?> (),
"J" => TypeOf<Int64> (),
"java/lang/Long" => TypeOf<Int64?> (),
"F" => TypeOf<Single> (),
"java/lang/Float" => TypeOf<Single?> (),
"D" => TypeOf<Double> (),
"java/lang/Double" => TypeOf<Double?> (),
_ => null,
};
}

[return: DynamicallyAccessedMembers (MethodsConstructors)]
static Type TypeOf<
[DynamicallyAccessedMembers (MethodsConstructors)]
T> () => typeof (T);

[return: DynamicallyAccessedMembers (MethodsConstructors)]
static Type TypeOfVoid () => typeof (void);

public virtual IEnumerable<Type> GetTypes (JniTypeSignature typeSignature)
{
AssertValid ();
Expand Down Expand Up @@ -313,7 +413,9 @@ IEnumerable<Type> CreateGetTypesEnumerator (JniTypeSignature typeSignature)
var rank = typeSignature.ArrayRank;
var arrayType = type;
while (rank-- > 0) {
arrayType = MakeGenericType (typeof (JavaObjectArray<>), arrayType);
arrayType = TryMakeJavaObjectArrayType (arrayType, out var nextArrayType)
? nextArrayType ?? throw new InvalidOperationException ("Should not be reached")
: GetUnsupportedJavaObjectArrayType (arrayType);
}
yield return arrayType;
}
Expand All @@ -322,7 +424,9 @@ IEnumerable<Type> CreateGetTypesEnumerator (JniTypeSignature typeSignature)
var rank = typeSignature.ArrayRank;
var arrayType = type;
while (rank-- > 0) {
arrayType = MakeArrayType (arrayType);
arrayType = TryMakeArrayType (arrayType, out var nextArrayType)
? nextArrayType ?? throw new InvalidOperationException ("Should not be reached")
: GetUnsupportedArrayType (arrayType);
}
yield return arrayType;
}
Expand All @@ -345,14 +449,18 @@ IEnumerable<Type> GetPrimitiveArrayTypesForSimpleReference (JniTypeSignature typ
var rank = typeSignature.ArrayRank-1;
var arrayType = t;
while (rank-- > 0) {
arrayType = MakeGenericType (typeof (JavaObjectArray<>), arrayType);
arrayType = TryMakeJavaObjectArrayType (arrayType, out var nextArrayType)
? nextArrayType ?? throw new InvalidOperationException ("Should not be reached")
: GetUnsupportedJavaObjectArrayType (arrayType);
}
yield return arrayType;

rank = typeSignature.ArrayRank-1;
arrayType = t;
while (rank-- > 0) {
arrayType = MakeArrayType (arrayType);
arrayType = TryMakeArrayType (arrayType, out var nextArrayType)
? nextArrayType ?? throw new InvalidOperationException ("Should not be reached")
: GetUnsupportedArrayType (arrayType);
}
yield return arrayType;
}
Expand Down Expand Up @@ -392,19 +500,6 @@ IEnumerable<Type> CreateGetTypesForSimpleReferenceEnumerator (string jniSimpleRe
[DynamicallyAccessedMembers (Constructors)]
Type type)
{
// https://github.com/xamarin/xamarin-android/blob/5472eec991cc075e4b0c09cd98a2331fb93aa0f3/src/Microsoft.Android.Sdk.ILLink/MarkJavaObjects.cs#L176-L186
const string makeGenericTypeMessage = "Generic 'Invoker' types are preserved by the MarkJavaObjects trimmer step.";

// FIXME: https://github.com/dotnet/java-interop/issues/1192
[UnconditionalSuppressMessage ("Trimming", "IL2055", Justification = makeGenericTypeMessage)]
[UnconditionalSuppressMessage ("Trimming", "IL3050", Justification = makeGenericTypeMessage)]
[return: DynamicallyAccessedMembers (Constructors)]
static Type MakeGenericType (
[DynamicallyAccessedMembers (Constructors)]
Type type,
Type [] arguments) =>
type.MakeGenericType (arguments);

var signature = type.GetCustomAttribute<JniTypeSignatureAttribute> ();
if (signature == null || signature.InvokerType == null) {
return null;
Expand All @@ -414,7 +509,11 @@ static Type MakeGenericType (
if (arguments.Length == 0)
return signature.InvokerType;

return MakeGenericType (signature.InvokerType, arguments);
#if NET
throw new NotSupportedException ($"Generic invoker type construction for `{type}` is not supported.");
#else // NET
return signature.InvokerType.MakeGenericType (arguments);
#endif // NET
}

#if NET
Expand Down Expand Up @@ -605,4 +704,3 @@ bool FindAndCallRegisterMethod (
}
}
}

1 change: 1 addition & 0 deletions src/Java.Interop/PublicAPI.Unshipped.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ static Java.Interop.JniEnvironment.EndMarshalMethod(ref Java.Interop.JniTransiti
virtual Java.Interop.JniRuntime.OnEnterMarshalMethod() -> void
virtual Java.Interop.JniRuntime.OnUserUnhandledException(ref Java.Interop.JniTransition transition, System.Exception! e) -> void
virtual Java.Interop.JniRuntime.JniTypeManager.GetInvokerTypeCore(System.Type! type) -> System.Type?
virtual Java.Interop.JniRuntime.JniTypeManager.GetTypeForSimpleReference(string! jniSimpleReference) -> System.Type?
virtual Java.Interop.JniRuntime.JniValueManager.TryConstructPeer(Java.Interop.IJavaPeerable! self, ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions options, System.Type! type) -> bool
Java.Interop.JavaException.JavaException(ref Java.Interop.JniObjectReference reference, Java.Interop.JniObjectReferenceOptions transfer, Java.Interop.JniObjectReference throwableOverride) -> void
Java.Interop.JavaException.SetJavaStackTrace(Java.Interop.JniObjectReference peerReferenceOverride = default(Java.Interop.JniObjectReference)) -> void
Expand Down
10 changes: 10 additions & 0 deletions src/Java.Runtime.Environment/Java.Interop/JreTypeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,16 @@ protected override IEnumerable<Type> GetTypesForSimpleReference (string jniSimpl
yield return target;
}

protected override Type? GetTypeForSimpleReference (string jniSimpleReference)
{
var type = base.GetTypeForSimpleReference (jniSimpleReference);
if (type != null)
return type;
if (typeMappings == null)
return null;
return typeMappings.TryGetValue (jniSimpleReference, out var target) ? target : null;
}

protected override IEnumerable<string> GetSimpleReferences (Type type)
{
return base.GetSimpleReferences (type)
Expand Down
9 changes: 6 additions & 3 deletions tests/Java.Interop-Tests/Java.Interop/JavaVMFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Diagnostics;
using System.Linq;

using Java.Interop;
Expand Down Expand Up @@ -65,6 +64,12 @@ protected override IEnumerable<Type> GetTypesForSimpleReference (string jniSimpl
#pragma warning restore CS8600
}

protected override Type? GetTypeForSimpleReference (string jniSimpleReference)
{
return base.GetTypeForSimpleReference (jniSimpleReference) ??
(TypeMappings.TryGetValue (jniSimpleReference, out var target) ? target : null);
}

protected override IEnumerable<string> GetSimpleReferences (Type type)
{
return base.GetSimpleReferences (type)
Expand All @@ -91,7 +96,6 @@ IEnumerable<string> CreateSimpleReferencesEnumerator (Type type)
protected override IReadOnlyList<string>? GetStaticMethodFallbackTypesCore (string jniSimpleReference)
{
RequestedFallbackTypesForSimpleReference = jniSimpleReference;
Debug.WriteLine ($"# GetStaticMethodFallbackTypes (jniSimpleReference={jniSimpleReference})");

var slash = jniSimpleReference.LastIndexOf ('/');
var desugarType = slash <= 0
Expand Down Expand Up @@ -167,4 +171,3 @@ string GetAlternateMethodSignature ()
#endif // NET
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,30 @@ public void GetInvokerType ()
}
}

#if NET
[Test]
public void GetInvokerType_GenericType_Throws ()
{
using (var vm = new MyTypeManager ()) {
Assert.Throws<NotSupportedException> (() => vm.GetInvokerType (typeof (IGenericJavaInterface<string>)));
}
}
#endif // NET

class MyTypeManager : JniRuntime.JniTypeManager {
}
}
}

[JniTypeSignature ("example/GenericInterface", GenerateJavaPeer=false, InvokerType=typeof (IGenericJavaInterfaceInvoker<>))]
interface IGenericJavaInterface<T> : IJavaPeerable {
}

[JniTypeSignature ("example/GenericInterface", GenerateJavaPeer=false)]
class IGenericJavaInterfaceInvoker<T> : JavaObject, IGenericJavaInterface<T> {

public IGenericJavaInterfaceInvoker (ref JniObjectReference reference, JniObjectReferenceOptions options)
: base (ref reference, options)
{
}
}
}
}
6 changes: 5 additions & 1 deletion tests/Java.Interop-Tests/Java.Interop/JniTypeManagerTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,11 @@ static void AssertGetJniTypeInfoForType (Type type, string jniType, bool isKeywo
Assert.AreEqual (typeof (double), GetType ("D"));
Assert.AreEqual (typeof (string), GetType ("java/lang/String"));
Assert.AreEqual (typeof (float?), GetType ("java/lang/Float"));
#if !__ANDROID__
Assert.AreEqual (typeof (GenericHolder<>), GetType (GenericHolder<int>.JniTypeName));
#endif // !__ANDROID__
Assert.Throws<NotSupportedException> (() => GetType ("[I"));
Assert.Throws<NotSupportedException> (() => GetType ("[Ljava/lang/String;"));
Assert.AreEqual (null, GetType ("com/example/does/not/exist"));
Assert.AreEqual (null, GetType ("Lcom/example/does/not/exist;"));
Assert.AreEqual (null, GetType ("[Lcom/example/does/not/exist;"));
Expand Down Expand Up @@ -246,4 +251,3 @@ class GenericHolder<T> : JavaObject {
public T Value {get; set;}
}
}