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
2 changes: 1 addition & 1 deletion external/Java.Interop
Submodule Java.Interop updated 143 files
Original file line number Diff line number Diff line change
Expand Up @@ -208,18 +208,15 @@ void ScanAssembly (AssemblyIndex index, Dictionary<(string ManagedName, string A
continue;
}

// [JniAddNativeMethodRegistrationAttribute] is not supported by the trimmable typemap
// by design (see XA4251). Detect the attribute *before* any per-type filters below
// (array type, no JNI name, etc.) so the diagnostic fires uniformly regardless of
// whether the type would otherwise have ended up in the typemap.
//
// Skip the per-method walk entirely for the overwhelmingly common case where
// the assembly doesn't even reference the attribute type — the per-assembly
// flag was computed cheaply in AssemblyIndex.Build.
if (index.MayUseJniAddNativeMethodRegistrationAttribute &&
HasJniAddNativeMethodRegistrationAttribute (typeDef, index)) {
logger?.LogJniAddNativeMethodRegistrationAttributeError (MetadataTypeNameResolver.GetFullName (typeDef, index.Reader));
}
var fullName = MetadataTypeNameResolver.GetFullName (typeDef, index.Reader);

// Temporarily allow [JniAddNativeMethodRegistrationAttribute] while we investigate
// which scenarios fail later in the trimmable typemap pipeline.
// if (index.MayUseJniAddNativeMethodRegistrationAttribute &&
// !IsBuiltInJniAddNativeMethodRegistrationType (fullName, index) &&
// HasJniAddNativeMethodRegistrationAttribute (typeDef, index)) {
// logger?.LogJniAddNativeMethodRegistrationAttributeError (fullName);
// }

// Determine the JNI name and whether this is a known Java peer.
// Priority:
Expand Down Expand Up @@ -261,8 +258,6 @@ void ScanAssembly (AssemblyIndex index, Dictionary<(string ManagedName, string A
}
}

var fullName = MetadataTypeNameResolver.GetFullName (typeDef, index.Reader);

var isInterface = (typeDef.Attributes & TypeAttributes.Interface) != 0;
var isAbstract = (typeDef.Attributes & TypeAttributes.Abstract) != 0;
var isGenericDefinition = typeDef.GetGenericParameters ().Count > 0;
Expand Down Expand Up @@ -418,6 +413,12 @@ static bool HasJniAddNativeMethodRegistrationAttribute (TypeDefinition typeDef,
return false;
}

static bool IsBuiltInJniAddNativeMethodRegistrationType (string fullName, AssemblyIndex index)
{
return string.Equals (index.AssemblyName, "Java.Interop", StringComparison.Ordinal) &&
string.Equals (fullName, "Java.Interop.JavaProxyObject", StringComparison.Ordinal);
}

/// <summary>
/// For each virtual override method on <paramref name="typeDef"/> that wasn't already
/// collected (no direct [Register]), walks up the base type hierarchy to find a
Expand Down
28 changes: 23 additions & 5 deletions src/Mono.Android/Android.Runtime/AndroidRuntime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -310,12 +310,14 @@ public override void DeleteWeakGlobalReference (ref JniObjectReference value)
}
}

class AndroidTypeManager : JniRuntime.JniTypeManager {
[UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Temporary suppression for Java.Interop reflection manager base.")]
class AndroidTypeManager : JniRuntime.ReflectionJniTypeManager {
bool jniAddNativeMethodRegistrationAttributePresent;

const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
const DynamicallyAccessedMemberTypes Methods = DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods;
const DynamicallyAccessedMemberTypes MethodsAndPrivateNested = Methods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes;
const DynamicallyAccessedMemberTypes MethodsConstructors = MethodsAndPrivateNested | Constructors;

public AndroidTypeManager (bool jniAddNativeMethodRegistrationAttributePresent)
{
Expand All @@ -332,13 +334,25 @@ protected override IEnumerable<Type> GetTypesForSimpleReference (string jniSimpl
yield return t;
}

[UnconditionalSuppressMessage ("Trimming", "IL2073", Justification = "Temporary suppression until legacy typemap entries carry DAM annotations.")]
[return: DynamicallyAccessedMembers (MethodsConstructors)]
protected override Type? GetTypeForSimpleReference (string jniSimpleReference)
{
var type = base.GetTypeForSimpleReference (jniSimpleReference);
if (type != null) {
return type;
}

return Java.Interop.TypeManager.GetJavaToManagedType (jniSimpleReference);
}

protected override string? GetSimpleReference (Type type)
{
string? j = JNIEnv.TypemapManagedToJava (type);
if (j != null) {
return GetReplacementTypeCore (j) ?? j;
}
return null;
return base.GetSimpleReference (type);
}

protected override IEnumerable<string> GetSimpleReferences (Type type)
Expand All @@ -347,9 +361,12 @@ protected override IEnumerable<string> GetSimpleReferences (Type type)
j = GetReplacementTypeCore (j) ?? j;

if (j != null) {
return new[]{j};
yield return j;
yield break;
}
foreach (var r in base.GetSimpleReferences (type)) {
yield return r;
}
return Array.Empty<string> ();
}

protected override IReadOnlyList<string>? GetStaticMethodFallbackTypesCore (string jniSimpleReference)
Expand Down Expand Up @@ -623,7 +640,8 @@ static void SplitMethodLine (
}
}

class AndroidValueManager : JniRuntime.JniValueManager {
[UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Temporary suppression for Java.Interop reflection manager base.")]
class AndroidValueManager : JniRuntime.ReflectionJniValueManager {

Dictionary<IntPtr, IdentityHashTargets> instances = new Dictionary<IntPtr, IdentityHashTargets> ();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Reflection;
using System.Runtime.CompilerServices;
using Java.Interop;

namespace Microsoft.Android.Runtime;

[UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Temporary suppression for Java.Interop reflection manager base.")]
abstract class AndroidReflectionJniValueManager : JniRuntime.ReflectionJniValueManager
{
const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;

public override IJavaPeerable? CreatePeer (
ref JniObjectReference reference,
JniObjectReferenceOptions transfer,
[DynamicallyAccessedMembers (Constructors)]
Type? targetType)
{
EnsureNotDisposed ();

if (!reference.IsValid) {
return null;
}

targetType = targetType ?? typeof (global::Java.Interop.JavaObject);
targetType = GetPeerType (targetType);

if (!typeof (IJavaPeerable).IsAssignableFrom (targetType)) {
throw new ArgumentException ($"targetType `{targetType.AssemblyQualifiedName}` must implement IJavaPeerable!", nameof (targetType));
}

var targetSig = Runtime.TypeManager.GetTypeSignature (targetType);
if (!targetSig.IsValid || targetSig.SimpleReference == null) {
throw new ArgumentException ($"Could not determine Java type corresponding to `{targetType.AssemblyQualifiedName}`.", nameof (targetType));
}

var refClass = JniEnvironment.Types.GetObjectClass (reference);
JniObjectReference targetClass;
try {
targetClass = JniEnvironment.Types.FindClass (targetSig.SimpleReference);
} catch (Exception e) {
JniObjectReference.Dispose (ref refClass);
throw new ArgumentException ($"Could not find Java class `{targetSig.SimpleReference}`.",
nameof (targetType),
e);
}

if (!JniEnvironment.Types.IsAssignableFrom (refClass, targetClass)) {
JniObjectReference.Dispose (ref refClass);
JniObjectReference.Dispose (ref targetClass);
return null;
}

JniObjectReference.Dispose (ref targetClass);

var peer = CreatePeerInstance (ref refClass, targetType, ref reference, transfer);
if (peer == null) {
throw new NotSupportedException (string.Format (CultureInfo.InvariantCulture, "Could not find an appropriate constructable wrapper type for Java type '{0}', targetType='{1}'.",
JniEnvironment.Types.GetJniTypeNameFromInstance (reference), targetType));
}
peer.SetJniManagedPeerState (peer.JniManagedPeerState | JniManagedPeerStates.Replaceable);
return peer;
}

[return: DynamicallyAccessedMembers (Constructors)]
static Type GetPeerType ([DynamicallyAccessedMembers (Constructors)] Type type)
{
if (type == typeof (object)) {
return typeof (global::Java.Interop.JavaObject);
}
if (type == typeof (IJavaPeerable)) {
return typeof (global::Java.Interop.JavaObject);
}
if (type == typeof (Exception)) {
return typeof (JavaException);
}
return type;
}

IJavaPeerable? CreatePeerInstance (
ref JniObjectReference klass,
[DynamicallyAccessedMembers (Constructors)]
Type targetType,
ref JniObjectReference reference,
JniObjectReferenceOptions transfer)
{
var jniTypeName = JniEnvironment.Types.GetJniTypeNameFromClass (klass);

while (jniTypeName != null) {
JniTypeSignature sig;
if (!JniTypeSignature.TryParse (jniTypeName, out sig)) {
return null;
}

Type? type = GetTypeAssignableTo (sig, targetType);
if (type != null) {
var peer = TryCreatePeerInstance (ref reference, transfer, type);

if (peer != null) {
JniObjectReference.Dispose (ref klass);
return peer;
}
}

var super = JniEnvironment.Types.GetSuperclass (klass);
jniTypeName = super.IsValid
? JniEnvironment.Types.GetJniTypeNameFromClass (super)
: null;

JniObjectReference.Dispose (ref klass, JniObjectReferenceOptions.CopyAndDispose);
klass = super;
}
JniObjectReference.Dispose (ref klass, JniObjectReferenceOptions.CopyAndDispose);

return TryCreatePeerInstance (ref reference, transfer, targetType);

[return: DynamicallyAccessedMembers (Constructors)]
Type? GetTypeAssignableTo (JniTypeSignature sig, Type targetType)
{
foreach (var t in Runtime.TypeManager.GetReflectionConstructibleTypes (sig)) {
if (targetType.IsAssignableFrom (t.Type)) {
return t.Type;
}
}
return null;
}
}

IJavaPeerable? TryCreatePeerInstance (
ref JniObjectReference reference,
JniObjectReferenceOptions options,
[DynamicallyAccessedMembers (Constructors)]
Type type)
{
type = Runtime.TypeManager.GetInvokerType (type) ?? type;

var self = (IJavaPeerable) RuntimeHelpers.GetUninitializedObject (type);
self.SetJniManagedPeerState (JniManagedPeerStates.Replaceable | JniManagedPeerStates.Activatable);

var constructed = false;
try {
constructed = TryConstructPeer (self, ref reference, options, type);
} finally {
if (!constructed) {
GC.SuppressFinalize (self);
self = null;
}
}
return self;
}

static readonly Type ByRefJniObjectReference = typeof (JniObjectReference).MakeByRefType ();
static readonly Type[] JIConstructorSignature = new Type [] { ByRefJniObjectReference, typeof (JniObjectReferenceOptions) };

protected virtual bool TryConstructPeer (
IJavaPeerable self,
ref JniObjectReference reference,
JniObjectReferenceOptions options,
[DynamicallyAccessedMembers (Constructors)]
Type type)
{
var c = type.GetConstructor (BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance, null, JIConstructorSignature, null);
if (c != null) {
var args = new object[] {
reference,
options,
};
c.Invoke (self, args);
reference = (JniObjectReference) args [0];
return true;
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@

namespace Microsoft.Android.Runtime;

class JavaMarshalValueManager : JniRuntime.JniValueManager
class JavaMarshalValueManager : AndroidReflectionJniValueManager
{
const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@

namespace Microsoft.Android.Runtime;

class ManagedTypeManager : JniRuntime.JniTypeManager {
[UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Temporary suppression for Java.Interop reflection manager base.")]
class ManagedTypeManager : JniRuntime.ReflectionJniTypeManager {

const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
internal const DynamicallyAccessedMemberTypes Methods = DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods;
internal const DynamicallyAccessedMemberTypes MethodsAndPrivateNested = Methods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes;
internal const DynamicallyAccessedMemberTypes MethodsConstructors = MethodsAndPrivateNested | Constructors;

public ManagedTypeManager ()
{
Expand Down Expand Up @@ -141,6 +143,17 @@ protected override IEnumerable<Type> GetTypesForSimpleReference (string jniSimpl
}
}

[return: DynamicallyAccessedMembers (MethodsConstructors)]
protected override Type? GetTypeForSimpleReference (string jniSimpleReference)
{
var type = base.GetTypeForSimpleReference (jniSimpleReference);
if (type != null) {
return type;
}

return ManagedTypeMapping.TryGetType (jniSimpleReference, out var target) ? target : null;
}

protected override IEnumerable<string> GetSimpleReferences (Type type)
{
foreach (var r in base.GetSimpleReferences (type)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

namespace Microsoft.Android.Runtime;

class SimpleValueManager : JniRuntime.JniValueManager
class SimpleValueManager : AndroidReflectionJniValueManager
{
const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,14 @@ namespace Microsoft.Android.Runtime;
/// Type manager for the trimmable typemap path. Delegates type lookups
/// to <see cref="TrimmableTypeMap"/>.
/// </summary>
class TrimmableTypeMapTypeManager : JniRuntime.JniTypeManager
[UnconditionalSuppressMessage ("Trimming", "IL2026", Justification = "Temporary suppression for Java.Interop reflection manager base.")]
class TrimmableTypeMapTypeManager : JniRuntime.ReflectionJniTypeManager
{
const string NoSimpleReference = "\0";
const DynamicallyAccessedMemberTypes Constructors = DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors;
const DynamicallyAccessedMemberTypes Methods = DynamicallyAccessedMemberTypes.PublicMethods | DynamicallyAccessedMemberTypes.NonPublicMethods;
const DynamicallyAccessedMemberTypes MethodsAndPrivateNested = Methods | DynamicallyAccessedMemberTypes.NonPublicNestedTypes;
const DynamicallyAccessedMemberTypes MethodsConstructors = MethodsAndPrivateNested | Constructors;
readonly ConcurrentDictionary<Type, string> _simpleReferenceCache = new ();

protected override IEnumerable<Type> GetTypesForSimpleReference (string jniSimpleReference)
Expand All @@ -32,6 +37,21 @@ protected override IEnumerable<Type> GetTypesForSimpleReference (string jniSimpl
}
}

[UnconditionalSuppressMessage ("Trimming", "IL2063", Justification = "Temporary suppression until trimmable typemap type entries carry DAM annotations.")]
[return: DynamicallyAccessedMembers (MethodsConstructors)]
protected override Type? GetTypeForSimpleReference (string jniSimpleReference)
{
var type = base.GetTypeForSimpleReference (jniSimpleReference);
if (type != null) {
return type;
}

if (TrimmableTypeMap.Instance.TryGetTargetTypes (jniSimpleReference, out var types)) {
return types [0];
}
return null;
}

protected override string? GetSimpleReference (Type type)
{
var simpleReference = _simpleReferenceCache.GetOrAdd (type, GetSimpleReferenceUncached);
Expand Down
Loading
Loading