Skip to content

Commit

Permalink
[Java.Interop.Tools.*] IMetadataResolver not TypeDefinitionCache (#842)
Browse files Browse the repository at this point in the history
Context: b81cfbb
Context: dotnet/android#5748
Context: dotnet/android#5748 (comment)

Commit b81cfbb introduced `TypeDefinitionCache`, which caches
`TypeReference.Resolve()` invocations so as to speed things up.

Enter dotnet/android#5748: we want to adopt some linker API
changes, and mono/linker's [`LinkContext` API][0] *also* has a
`TypeDefinition` cache construct.

Consequently, to "fully embrace" the new `LinkContext` API changes,
*large portions* of `Java.Interop.Tools.Cecil.dll` are copied so that
`LinkContext`'s caching can be used instead of `TypeDefinitionCache`'s
caching, because mono/linker doesn't use Java.Interop, and thus
can't use `TypeDefinitionCache`.

Clean this up and split the difference: "duplicate" the APIs in
`Java.Interop.Tools.Cecil.dll`,
`Java.Interop.Tools.JavaCallableWrappers.dll`, and
`src/Java.Interop.Tools.TypeNameMappings` so that instead of
optionally using `TypeDefinitionCache`, we instead permit the use
of the [`Mono.Cecil.IMetadataResolver` interface][1], which is a
"superset" of `TypeDefinitionCache` functionality.

Update `TypeDefinitionCache` to implement the `IMetadataResolver`
interface, implementing `IMetadataResolver.Resolve()` so that
previous caching functionality is preserved.

This *should* result in no breakage of existing xamarin-android code,
while allowing for a reasonable integration point between
`Java.Interop.Tools.Cecil.dll` and mono/linker, by way of
`IMetadataResolver`.

[0]: https://github.com/mono/linker/blob/30f2498c2a3de1f7e236d5793f5f1aca6e5ba456/src/linker/Linker/LinkContext.cs
[1]: https://github.com/mono/cecil/blob/e069cd8d25d5b61b0e28fe65e75959c20af7aa80/Mono.Cecil/MetadataResolver.cs#L22-L26
  • Loading branch information
jonpryor committed May 20, 2021
1 parent 412e974 commit 2573dc8
Show file tree
Hide file tree
Showing 7 changed files with 224 additions and 104 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,22 @@ public static class MethodDefinitionRocks
{
[Obsolete ("Use the TypeDefinitionCache overload for better performance.")]
public static MethodDefinition GetBaseDefinition (this MethodDefinition method) =>
GetBaseDefinition (method, cache: null);
GetBaseDefinition (method, resolver: null);

public static MethodDefinition GetBaseDefinition (this MethodDefinition method, TypeDefinitionCache? cache)
public static MethodDefinition GetBaseDefinition (this MethodDefinition method, TypeDefinitionCache? cache) =>
GetBaseDefinition (method, (IMetadataResolver?) cache);

public static MethodDefinition GetBaseDefinition (this MethodDefinition method, IMetadataResolver? resolver)
{
if (method.IsStatic || method.IsNewSlot || !method.IsVirtual)
return method;

foreach (var baseType in method.DeclaringType.GetBaseTypes (cache)) {
foreach (var baseType in method.DeclaringType.GetBaseTypes (resolver)) {
foreach (var m in baseType.Methods) {
if (!m.IsConstructor &&
m.Name == method.Name &&
(m.IsVirtual || m.IsAbstract) &&
AreParametersCompatibleWith (m.Parameters, method.Parameters, cache)) {
AreParametersCompatibleWith (m.Parameters, method.Parameters, resolver)) {
return m;
}
}
Expand All @@ -33,14 +36,17 @@ public static MethodDefinition GetBaseDefinition (this MethodDefinition method,

[Obsolete ("Use the TypeDefinitionCache overload for better performance.")]
public static IEnumerable<MethodDefinition> GetOverriddenMethods (MethodDefinition method, bool inherit) =>
GetOverriddenMethods (method, inherit, cache: null);
GetOverriddenMethods (method, inherit, resolver: null);

public static IEnumerable<MethodDefinition> GetOverriddenMethods (MethodDefinition method, bool inherit, TypeDefinitionCache? cache) =>
GetOverriddenMethods (method, inherit, (IMetadataResolver?) cache);

public static IEnumerable<MethodDefinition> GetOverriddenMethods (MethodDefinition method, bool inherit, TypeDefinitionCache? cache)
public static IEnumerable<MethodDefinition> GetOverriddenMethods (MethodDefinition method, bool inherit, IMetadataResolver? resolver)
{
yield return method;
if (inherit) {
MethodDefinition baseMethod = method;
while ((baseMethod = method.GetBaseDefinition (cache)) != null && baseMethod != method) {
while ((baseMethod = method.GetBaseDefinition (resolver)) != null && baseMethod != method) {
yield return method;
method = baseMethod;
}
Expand All @@ -49,9 +55,12 @@ public static IEnumerable<MethodDefinition> GetOverriddenMethods (MethodDefiniti

[Obsolete ("Use the TypeDefinitionCache overload for better performance.")]
public static bool AreParametersCompatibleWith (this Collection<ParameterDefinition> a, Collection<ParameterDefinition> b) =>
AreParametersCompatibleWith (a, b, cache: null);
AreParametersCompatibleWith (a, b, resolver: null);

public static bool AreParametersCompatibleWith (this Collection<ParameterDefinition> a, Collection<ParameterDefinition> b, TypeDefinitionCache? cache) =>
AreParametersCompatibleWith (a, b, (IMetadataResolver?) cache);

public static bool AreParametersCompatibleWith (this Collection<ParameterDefinition> a, Collection<ParameterDefinition> b, TypeDefinitionCache? cache)
public static bool AreParametersCompatibleWith (this Collection<ParameterDefinition> a, Collection<ParameterDefinition> b, IMetadataResolver? resolver)
{
if (a.Count != b.Count)
return false;
Expand All @@ -60,21 +69,21 @@ public static bool AreParametersCompatibleWith (this Collection<ParameterDefinit
return true;

for (int i = 0; i < a.Count; i++)
if (!IsParameterCompatibleWith (a [i].ParameterType, b [i].ParameterType, cache))
if (!IsParameterCompatibleWith (a [i].ParameterType, b [i].ParameterType, resolver))
return false;

return true;
}

static bool IsParameterCompatibleWith (IModifierType a, IModifierType b, TypeDefinitionCache? cache)
static bool IsParameterCompatibleWith (IModifierType a, IModifierType b, IMetadataResolver? cache)
{
if (!IsParameterCompatibleWith (a.ModifierType, b.ModifierType, cache))
return false;

return IsParameterCompatibleWith (a.ElementType, b.ElementType, cache);
}

static bool IsParameterCompatibleWith (TypeSpecification a, TypeSpecification b, TypeDefinitionCache? cache)
static bool IsParameterCompatibleWith (TypeSpecification a, TypeSpecification b, IMetadataResolver? cache)
{
if (a is GenericInstanceType)
return IsParameterCompatibleWith ((GenericInstanceType) a, (GenericInstanceType) b, cache);
Expand All @@ -85,7 +94,7 @@ static bool IsParameterCompatibleWith (TypeSpecification a, TypeSpecification b,
return IsParameterCompatibleWith (a.ElementType, b.ElementType, cache);
}

static bool IsParameterCompatibleWith (GenericInstanceType a, GenericInstanceType b, TypeDefinitionCache? cache)
static bool IsParameterCompatibleWith (GenericInstanceType a, GenericInstanceType b, IMetadataResolver? cache)
{
if (!IsParameterCompatibleWith (a.ElementType, b.ElementType, cache))
return false;
Expand All @@ -103,7 +112,7 @@ static bool IsParameterCompatibleWith (GenericInstanceType a, GenericInstanceTyp
return true;
}

static bool IsParameterCompatibleWith (TypeReference a, TypeReference b, TypeDefinitionCache? cache)
static bool IsParameterCompatibleWith (TypeReference a, TypeReference b, IMetadataResolver? cache)
{
if (a is TypeSpecification || b is TypeSpecification) {
if (a.GetType () != b.GetType ())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,39 @@
using System.Collections.Generic;
using Mono.Cecil;

using Java.Interop.Tools.Diagnostics;

namespace Java.Interop.Tools.Cecil
{
/// <summary>
/// A class for caching lookups from TypeReference -> TypeDefinition.
/// Generally its lifetime should match an AssemblyResolver instance.
/// </summary>
public class TypeDefinitionCache
public class TypeDefinitionCache : IMetadataResolver
{
readonly Dictionary<TypeReference, TypeDefinition> cache = new Dictionary<TypeReference, TypeDefinition> ();
readonly Dictionary<TypeReference, TypeDefinition?> types = new Dictionary<TypeReference, TypeDefinition?> ();
readonly Dictionary<FieldReference, FieldDefinition?> fields = new Dictionary<FieldReference, FieldDefinition?> ();
readonly Dictionary<MethodReference, MethodDefinition?> methods = new Dictionary<MethodReference, MethodDefinition?> ();

public virtual TypeDefinition Resolve (TypeReference typeReference)
public virtual TypeDefinition? Resolve (TypeReference typeReference)
{
if (cache.TryGetValue (typeReference, out var typeDefinition))
if (types.TryGetValue (typeReference, out var typeDefinition))
return typeDefinition;
return cache [typeReference] = typeReference.Resolve ();
return types [typeReference] = typeReference.Resolve ();
}

public virtual FieldDefinition? Resolve (FieldReference field)
{
if (fields.TryGetValue (field, out var fieldDefinition))
return fieldDefinition;
return fields [field] = field.Resolve ();
}

public virtual MethodDefinition? Resolve (MethodReference method)
{
if (methods.TryGetValue (method, out var methodDefinition))
return methodDefinition;
return methods [method] = method.Resolve ();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,62 +9,74 @@ public static class TypeDefinitionRocks {

[Obsolete ("Use the TypeDefinitionCache overload for better performance.")]
public static TypeDefinition? GetBaseType (this TypeDefinition type) =>
GetBaseType (type, cache: null);
GetBaseType (type, resolver: null);

public static TypeDefinition? GetBaseType (this TypeDefinition type, TypeDefinitionCache? cache)
public static TypeDefinition? GetBaseType (this TypeDefinition type, TypeDefinitionCache? cache) =>
GetBaseType (type, (IMetadataResolver?) cache);

public static TypeDefinition? GetBaseType (this TypeDefinition type, IMetadataResolver? resolver)
{
var bt = type.BaseType;
if (bt == null)
return null;
if (cache != null)
return cache.Resolve (bt);
if (resolver != null)
return resolver.Resolve (bt);
return bt.Resolve ();
}

[Obsolete ("Use the TypeDefinitionCache overload for better performance.")]
public static IEnumerable<TypeDefinition> GetTypeAndBaseTypes (this TypeDefinition type) =>
GetTypeAndBaseTypes (type, cache: null);
GetTypeAndBaseTypes (type, resolver: null);

public static IEnumerable<TypeDefinition> GetTypeAndBaseTypes (this TypeDefinition type, TypeDefinitionCache? cache) =>
GetTypeAndBaseTypes (type, (IMetadataResolver?) cache);

public static IEnumerable<TypeDefinition> GetTypeAndBaseTypes (this TypeDefinition type, TypeDefinitionCache? cache)
public static IEnumerable<TypeDefinition> GetTypeAndBaseTypes (this TypeDefinition type, IMetadataResolver? resolver)
{
TypeDefinition? t = type;

while (t != null) {
yield return t;
t = t.GetBaseType (cache);
t = t.GetBaseType (resolver);
}
}

[Obsolete ("Use the TypeDefinitionCache overload for better performance.")]
public static IEnumerable<TypeDefinition> GetBaseTypes (this TypeDefinition type) =>
GetBaseTypes (type, cache: null);
GetBaseTypes (type, resolver: null);

public static IEnumerable<TypeDefinition> GetBaseTypes (this TypeDefinition type, TypeDefinitionCache? cache)
public static IEnumerable<TypeDefinition> GetBaseTypes (this TypeDefinition type, TypeDefinitionCache? cache) =>
GetBaseTypes (type, (IMetadataResolver?) cache);

public static IEnumerable<TypeDefinition> GetBaseTypes (this TypeDefinition type, IMetadataResolver? resolver)
{
TypeDefinition? t = type;

while ((t = t.GetBaseType (cache)) != null) {
while ((t = t.GetBaseType (resolver)) != null) {
yield return t;
}
}

[Obsolete ("Use the TypeDefinitionCache overload for better performance.")]
public static bool IsAssignableFrom (this TypeReference type, TypeReference c) =>
IsAssignableFrom (type, c, cache: null);
IsAssignableFrom (type, c, resolver: null);

public static bool IsAssignableFrom (this TypeReference type, TypeReference c, TypeDefinitionCache? cache) =>
IsAssignableFrom (type, c, (IMetadataResolver?) cache);

public static bool IsAssignableFrom (this TypeReference type, TypeReference c, TypeDefinitionCache? cache)
public static bool IsAssignableFrom (this TypeReference type, TypeReference c, IMetadataResolver? resolver)
{
if (type.FullName == c.FullName)
return true;
var d = c.Resolve ();
var d = (resolver?.Resolve (c)) ?? c.Resolve ();
if (d == null)
return false;
foreach (var t in d.GetTypeAndBaseTypes (cache)) {
foreach (var t in d.GetTypeAndBaseTypes (resolver)) {
if (type.FullName == t.FullName)
return true;
foreach (var ifaceImpl in t.Interfaces) {
var i = ifaceImpl.InterfaceType;
if (IsAssignableFrom (type, i, cache))
if (IsAssignableFrom (type, i, resolver))
return true;
}
}
Expand All @@ -73,11 +85,13 @@ public static bool IsAssignableFrom (this TypeReference type, TypeReference c, T

[Obsolete ("Use the TypeDefinitionCache overload for better performance.")]
public static bool IsSubclassOf (this TypeDefinition type, string typeName) =>
IsSubclassOf (type, typeName, cache: null);
IsSubclassOf (type, typeName, resolver: null);

public static bool IsSubclassOf (this TypeDefinition type, string typeName, TypeDefinitionCache? cache)
public static bool IsSubclassOf (this TypeDefinition type, string typeName, TypeDefinitionCache? cache) =>
IsSubclassOf (type, typeName, (IMetadataResolver?) cache);
public static bool IsSubclassOf (this TypeDefinition type, string typeName, IMetadataResolver? resolver)
{
foreach (var t in type.GetTypeAndBaseTypes (cache)) {
foreach (var t in type.GetTypeAndBaseTypes (resolver)) {
if (t.FullName == typeName) {
return true;
}
Expand All @@ -87,11 +101,14 @@ public static bool IsSubclassOf (this TypeDefinition type, string typeName, Type

[Obsolete ("Use the TypeDefinitionCache overload for better performance.")]
public static bool ImplementsInterface (this TypeDefinition type, string interfaceName) =>
ImplementsInterface (type, interfaceName, cache: null);
ImplementsInterface (type, interfaceName, resolver: null);

public static bool ImplementsInterface (this TypeDefinition type, string interfaceName, TypeDefinitionCache? cache)
public static bool ImplementsInterface (this TypeDefinition type, string interfaceName, TypeDefinitionCache? cache) =>
ImplementsInterface (type, interfaceName, (IMetadataResolver?) cache);

public static bool ImplementsInterface (this TypeDefinition type, string interfaceName, IMetadataResolver? resolver)
{
foreach (var t in type.GetTypeAndBaseTypes (cache)) {
foreach (var t in type.GetTypeAndBaseTypes (resolver)) {
foreach (var i in t.Interfaces) {
if (i.InterfaceType.FullName == interfaceName) {
return true;
Expand All @@ -103,34 +120,43 @@ public static bool ImplementsInterface (this TypeDefinition type, string interfa

[Obsolete ("Use the TypeDefinitionCache overload for better performance.")]
public static string GetPartialAssemblyName (this TypeReference type) =>
GetPartialAssemblyName (type, cache: null);
GetPartialAssemblyName (type, resolver: null);

public static string GetPartialAssemblyName (this TypeReference type, TypeDefinitionCache? cache) =>
GetPartialAssemblyName (type, (IMetadataResolver?) cache);

public static string GetPartialAssemblyName (this TypeReference type, TypeDefinitionCache? cache)
public static string GetPartialAssemblyName (this TypeReference type, IMetadataResolver? resolver)
{
TypeDefinition def = cache != null ? cache.Resolve (type) : type.Resolve ();
TypeDefinition? def = (resolver?.Resolve (type)) ?? type.Resolve ();
return (def ?? type).Module.Assembly.Name.Name;
}

[Obsolete ("Use the TypeDefinitionCache overload for better performance.")]
public static string GetPartialAssemblyQualifiedName (this TypeReference type) =>
GetPartialAssemblyQualifiedName (type, cache: null);
GetPartialAssemblyQualifiedName (type, resolver: null);

public static string GetPartialAssemblyQualifiedName (this TypeReference type, TypeDefinitionCache? cache)
public static string GetPartialAssemblyQualifiedName (this TypeReference type, TypeDefinitionCache? cache) =>
GetPartialAssemblyQualifiedName (type, (IMetadataResolver?) cache);

public static string GetPartialAssemblyQualifiedName (this TypeReference type, IMetadataResolver? resolver)
{
return string.Format ("{0}, {1}",
// Cecil likes to use '/' as the nested type separator, while
// Reflection uses '+' as the nested type separator. Use Reflection.
type.FullName.Replace ('/', '+'),
type.GetPartialAssemblyName (cache));
type.GetPartialAssemblyName (resolver));
}

[Obsolete ("Use the TypeDefinitionCache overload for better performance.")]
public static string GetAssemblyQualifiedName (this TypeReference type) =>
GetAssemblyQualifiedName (type, cache: null);
GetAssemblyQualifiedName (type, resolver: null);

public static string GetAssemblyQualifiedName (this TypeReference type, TypeDefinitionCache? cache) =>
GetAssemblyQualifiedName (type, (IMetadataResolver?) cache);

public static string GetAssemblyQualifiedName (this TypeReference type, TypeDefinitionCache? cache)
public static string GetAssemblyQualifiedName (this TypeReference type, IMetadataResolver? resolver)
{
TypeDefinition def = cache != null ? cache.Resolve (type) : type.Resolve ();
TypeDefinition? def = (resolver?.Resolve (type)) ?? type.Resolve ();
return string.Format ("{0}, {1}",
// Cecil likes to use '/' as the nested type separator, while
// Reflection uses '+' as the nested type separator. Use Reflection.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ namespace Java.Interop.Tools.JavaCallableWrappers {
public class JavaCallableWrapperGenerator {

class JavaFieldInfo {
public JavaFieldInfo (MethodDefinition method, string fieldName, TypeDefinitionCache cache)
public JavaFieldInfo (MethodDefinition method, string fieldName, IMetadataResolver resolver)
{
this.FieldName = fieldName;
InitializerName = method.Name;
TypeName = JavaNativeTypeManager.ReturnTypeFromSignature (GetJniSignature (method, cache)).Type;
TypeName = JavaNativeTypeManager.ReturnTypeFromSignature (GetJniSignature (method, resolver)).Type;
IsStatic = method.IsStatic;
Access = method.Attributes & MethodAttributes.MemberAccessMask;
Annotations = GetAnnotationsString ("\t", method.CustomAttributes);
Expand All @@ -54,15 +54,20 @@ public string GetJavaAccess ()
List<Signature> methods = new List<Signature> ();
List<Signature> ctors = new List<Signature> ();
List<JavaCallableWrapperGenerator> children;
readonly TypeDefinitionCache cache;
readonly IMetadataResolver cache;

[Obsolete ("Use the TypeDefinitionCache overload for better performance.")]
public JavaCallableWrapperGenerator (TypeDefinition type, Action<string, object []> log)
: this (type, null, log, cache: null)
: this (type, null, log, resolver: null)
{ }

public JavaCallableWrapperGenerator (TypeDefinition type, Action<string, object[]> log, TypeDefinitionCache cache)
: this (type, null, log, cache)
: this (type, log, (IMetadataResolver) cache)
{
}

public JavaCallableWrapperGenerator (TypeDefinition type, Action<string, object[]> log, IMetadataResolver resolver)
: this (type, null, log, resolver)
{
if (type.HasNestedTypes) {
children = new List<JavaCallableWrapperGenerator> ();
Expand Down Expand Up @@ -103,11 +108,11 @@ void AddNestedTypes (TypeDefinition type)
HasExport |= children.Any (t => t.HasExport);
}

JavaCallableWrapperGenerator (TypeDefinition type, string outerType, Action<string, object[]> log, TypeDefinitionCache cache)
JavaCallableWrapperGenerator (TypeDefinition type, string outerType, Action<string, object[]> log, IMetadataResolver resolver)
{
this.type = type;
this.log = log;
this.cache = cache ?? new TypeDefinitionCache ();
this.cache = resolver ?? new TypeDefinitionCache ();

if (type.IsEnum || type.IsInterface || type.IsValueType)
Diagnostic.Error (4200, LookupSource (type), Localization.Resources.JavaCallableWrappers_XA4200, type.FullName);
Expand Down Expand Up @@ -655,7 +660,7 @@ public Signature (MethodDefinition method, RegisterAttribute register, string ma
Annotations = JavaCallableWrapperGenerator.GetAnnotationsString ("\t", method.CustomAttributes);
}

public Signature (MethodDefinition method, ExportAttribute export, TypeDefinitionCache cache)
public Signature (MethodDefinition method, ExportAttribute export, IMetadataResolver cache)
: this (method.Name, GetJniSignature (method, cache), "__export__", null, null, export.SuperArgumentsString)
{
IsExport = true;
Expand All @@ -666,7 +671,7 @@ public Signature (MethodDefinition method, ExportAttribute export, TypeDefinitio
Annotations = JavaCallableWrapperGenerator.GetAnnotationsString ("\t", method.CustomAttributes);
}

public Signature (MethodDefinition method, ExportFieldAttribute exportField, TypeDefinitionCache cache)
public Signature (MethodDefinition method, ExportFieldAttribute exportField, IMetadataResolver cache)
: this (method.Name, GetJniSignature (method, cache), "__export__", null, null, null)
{
if (method.HasParameters)
Expand Down
Loading

0 comments on commit 2573dc8

Please sign in to comment.