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

Commit cf80deb

Browse files
[Java.Interop.Tools.JavaCallableWrappers] use IMetadataResolver more (#1069)
Context: b81cfbb Reviewing code in `JavaCallableWrapperGenerator`, I found codepaths where we weren't caching `TypeReference.Resolve()` calls *at all*, e.g. `JavaNativeTypeManager.GetPrimitiveClass(TypeDefinition)`. We thus appear to be calling `TypeReference.Resolve()` potentially on the same types. For example, `dotnet trace` output of an incremental build of a `dotnet new maui` project: 41.65ms xamarin.android.cecil!Mono.Cecil.TypeReference.Resolve() It appears that, in trying to make our maintenance lives easier by preserving backward API compatibility with callers that couldn't provide a `TypeDefinitionCache` or `IMetadataResolver` instance -- by providing method overloads which took e.g. `IMetadataResolver? resolver` parameters which could be `null` -- we made our optimization and performance lives *harder*, because not all codepaths which needed to use caching *were* using caching, as they were overlooked. As the `Java.Interop.Tools.*` assemblies are internal API, introduce an *API break* while preserving *ABI*: `IMetadataResolver` is now required. Thus, previous/current "compatibility methods" which allow *not* providing an `IMetadataResolver` instance such as: // old and busted partial class TypeDefinitionRocks { [Obsolete ("Use the TypeDefinitionCache overload for better performance.")] public static TypeDefinition? GetBaseType (this TypeDefinition type) => GetBaseType (type, resolver: null); } will now become *errors* to use: // new hawtness partial class TypeDefinitionRocks { [Obsolete ("Use the TypeDefinitionCache overload for better performance.", error:true)] public static TypeDefinition? GetBaseType (this TypeDefinition type) => throw new NotSupportedException (); } This allows the C# compiler to help us audit our codebase, ensuring that *all* codepaths which call `TypeReference.Resolve()` instead use `IMetadataResolver.Resolve()`, including: * `JavaCallableWrapperGenerator.GetAnnotationsString()` * `JavaCallableWrapperGenerator.WriteAnnotations()` * `JavaNativeTypeManager.GetPrimitiveClass()` Requiring caching results in: 23.89ms xamarin.android.cecil!Mono.Cecil.TypeReference.Resolve() Additionally, I updated two places to use plain `foreach` loops instead of using System.Linq. * Before: 1.03s xamarin.android.build.tasks!Xamarin.Android.Tasks.GenerateJavaStubs.RunTask() * After: 944.48ms xamarin.android.build.tasks!Xamarin.Android.Tasks.GenerateJavaStubs.RunTask() I think this likely saves about ~50ms off incremental builds of a `dotnet new maui` project.
1 parent 5c5dc08 commit cf80deb

8 files changed

Lines changed: 185 additions & 207 deletions

File tree

‎src/Java.Interop.Tools.Cecil/Java.Interop.Tools.Cecil/MethodDefinitionRocks.cs‎

Lines changed: 19 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,13 @@ namespace Java.Interop.Tools.Cecil {
99

1010
publicstaticclassMethodDefinitionRocks
1111
{
12-
[Obsolete("Use the TypeDefinitionCache overload for better performance.")]
13-
publicstaticMethodDefinitionGetBaseDefinition(thisMethodDefinitionmethod)=>
14-
GetBaseDefinition(method,resolver:null);
12+
[Obsolete("Use the TypeDefinitionCache overload for better performance.",error:true)]
13+
publicstaticMethodDefinitionGetBaseDefinition(thisMethodDefinitionmethod)=>thrownewNotSupportedException();
1514

16-
publicstaticMethodDefinitionGetBaseDefinition(thisMethodDefinitionmethod,TypeDefinitionCache?cache)=>
17-
GetBaseDefinition(method,(IMetadataResolver?)cache);
15+
publicstaticMethodDefinitionGetBaseDefinition(thisMethodDefinitionmethod,TypeDefinitionCachecache)=>
16+
GetBaseDefinition(method,(IMetadataResolver)cache);
1817

19-
publicstaticMethodDefinitionGetBaseDefinition(thisMethodDefinitionmethod,IMetadataResolver?resolver)
18+
publicstaticMethodDefinitionGetBaseDefinition(thisMethodDefinitionmethod,IMetadataResolverresolver)
2019
{
2120
if(method.IsStatic||method.IsNewSlot||!method.IsVirtual)
2221
returnmethod;
@@ -34,14 +33,13 @@ public static MethodDefinition GetBaseDefinition (this MethodDefinition method,
3433
returnmethod;
3534
}
3635

37-
[Obsolete("Use the TypeDefinitionCache overload for better performance.")]
38-
publicstaticIEnumerable<MethodDefinition>GetOverriddenMethods(MethodDefinitionmethod,boolinherit)=>
39-
GetOverriddenMethods(method,inherit,resolver:null);
36+
[Obsolete("Use the TypeDefinitionCache overload for better performance.",error:true)]
37+
publicstaticIEnumerable<MethodDefinition>GetOverriddenMethods(MethodDefinitionmethod,boolinherit)=>thrownewNotSupportedException();
4038

41-
publicstaticIEnumerable<MethodDefinition>GetOverriddenMethods(MethodDefinitionmethod,boolinherit,TypeDefinitionCache?cache)=>
42-
GetOverriddenMethods(method,inherit,(IMetadataResolver?)cache);
39+
publicstaticIEnumerable<MethodDefinition>GetOverriddenMethods(MethodDefinitionmethod,boolinherit,TypeDefinitionCachecache)=>
40+
GetOverriddenMethods(method,inherit,(IMetadataResolver)cache);
4341

44-
publicstaticIEnumerable<MethodDefinition>GetOverriddenMethods(MethodDefinitionmethod,boolinherit,IMetadataResolver?resolver)
42+
publicstaticIEnumerable<MethodDefinition>GetOverriddenMethods(MethodDefinitionmethod,boolinherit,IMetadataResolverresolver)
4543
{
4644
yieldreturnmethod;
4745
if(inherit){
@@ -53,14 +51,13 @@ public static IEnumerable<MethodDefinition> GetOverriddenMethods (MethodDefiniti
5351
}
5452
}
5553

56-
[Obsolete("Use the TypeDefinitionCache overload for better performance.")]
57-
publicstaticboolAreParametersCompatibleWith(thisCollection<ParameterDefinition>a,Collection<ParameterDefinition>b)=>
58-
AreParametersCompatibleWith(a,b,resolver:null);
54+
[Obsolete("Use the TypeDefinitionCache overload for better performance.",error:true)]
55+
publicstaticboolAreParametersCompatibleWith(thisCollection<ParameterDefinition>a,Collection<ParameterDefinition>b)=>thrownewNotSupportedException();
5956

60-
publicstaticboolAreParametersCompatibleWith(thisCollection<ParameterDefinition>a,Collection<ParameterDefinition>b,TypeDefinitionCache?cache)=>
61-
AreParametersCompatibleWith(a,b,(IMetadataResolver?)cache);
57+
publicstaticboolAreParametersCompatibleWith(thisCollection<ParameterDefinition>a,Collection<ParameterDefinition>b,TypeDefinitionCachecache)=>
58+
AreParametersCompatibleWith(a,b,(IMetadataResolver)cache);
6259

63-
publicstaticboolAreParametersCompatibleWith(thisCollection<ParameterDefinition>a,Collection<ParameterDefinition>b,IMetadataResolver?resolver)
60+
publicstaticboolAreParametersCompatibleWith(thisCollection<ParameterDefinition>a,Collection<ParameterDefinition>b,IMetadataResolverresolver)
6461
{
6562
if(a.Count!=b.Count)
6663
returnfalse;
@@ -75,15 +72,15 @@ public static bool AreParametersCompatibleWith (this Collection<ParameterDefinit
7572
returntrue;
7673
}
7774

78-
staticboolIsParameterCompatibleWith(IModifierTypea,IModifierTypeb,IMetadataResolver?cache)
75+
staticboolIsParameterCompatibleWith(IModifierTypea,IModifierTypeb,IMetadataResolvercache)
7976
{
8077
if(!IsParameterCompatibleWith(a.ModifierType,b.ModifierType,cache))
8178
returnfalse;
8279

8380
returnIsParameterCompatibleWith(a.ElementType,b.ElementType,cache);
8481
}
8582

86-
staticboolIsParameterCompatibleWith(TypeSpecificationa,TypeSpecificationb,IMetadataResolver?cache)
83+
staticboolIsParameterCompatibleWith(TypeSpecificationa,TypeSpecificationb,IMetadataResolvercache)
8784
{
8885
if(aisGenericInstanceType)
8986
returnIsParameterCompatibleWith((GenericInstanceType)a,(GenericInstanceType)b,cache);
@@ -94,7 +91,7 @@ static bool IsParameterCompatibleWith (TypeSpecification a, TypeSpecification b,
9491
returnIsParameterCompatibleWith(a.ElementType,b.ElementType,cache);
9592
}
9693

97-
staticboolIsParameterCompatibleWith(GenericInstanceTypea,GenericInstanceTypeb,IMetadataResolver?cache)
94+
staticboolIsParameterCompatibleWith(GenericInstanceTypea,GenericInstanceTypeb,IMetadataResolvercache)
9895
{
9996
if(!IsParameterCompatibleWith(a.ElementType,b.ElementType,cache))
10097
returnfalse;
@@ -112,7 +109,7 @@ static bool IsParameterCompatibleWith (GenericInstanceType a, GenericInstanceTyp
112109
returntrue;
113110
}
114111

115-
staticboolIsParameterCompatibleWith(TypeReferencea,TypeReferenceb,IMetadataResolver?cache)
112+
staticboolIsParameterCompatibleWith(TypeReferencea,TypeReferenceb,IMetadataResolvercache)
116113
{
117114
if(aisTypeSpecification||bisTypeSpecification){
118115
if(a.GetType()!=b.GetType())

‎src/Java.Interop.Tools.Cecil/Java.Interop.Tools.Cecil/TypeDefinitionRocks.cs‎

Lines changed: 49 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -7,31 +7,27 @@ namespace Java.Interop.Tools.Cecil {
77

88
publicstaticclassTypeDefinitionRocks{
99

10-
[Obsolete("Use the TypeDefinitionCache overload for better performance.")]
11-
publicstaticTypeDefinition?GetBaseType(thisTypeDefinitiontype)=>
12-
GetBaseType(type,resolver:null);
10+
[Obsolete("Use the TypeDefinitionCache overload for better performance.",error:true)]
11+
publicstaticTypeDefinition?GetBaseType(thisTypeDefinitiontype)=>thrownewNotSupportedException();
1312

14-
publicstaticTypeDefinition?GetBaseType(thisTypeDefinitiontype,TypeDefinitionCache?cache)=>
15-
GetBaseType(type,(IMetadataResolver?)cache);
13+
publicstaticTypeDefinition?GetBaseType(thisTypeDefinitiontype,TypeDefinitionCachecache)=>
14+
GetBaseType(type,(IMetadataResolver)cache);
1615

17-
publicstaticTypeDefinition?GetBaseType(thisTypeDefinitiontype,IMetadataResolver?resolver)
16+
publicstaticTypeDefinition?GetBaseType(thisTypeDefinitiontype,IMetadataResolverresolver)
1817
{
1918
varbt=type.BaseType;
2019
if(bt==null)
2120
returnnull;
22-
if(resolver!=null)
23-
returnresolver.Resolve(bt);
24-
returnbt.Resolve();
21+
returnresolver.Resolve(bt);
2522
}
2623

27-
[Obsolete("Use the TypeDefinitionCache overload for better performance.")]
28-
publicstaticIEnumerable<TypeDefinition>GetTypeAndBaseTypes(thisTypeDefinitiontype)=>
29-
GetTypeAndBaseTypes(type,resolver:null);
24+
[Obsolete("Use the TypeDefinitionCache overload for better performance.",error:true)]
25+
publicstaticIEnumerable<TypeDefinition>GetTypeAndBaseTypes(thisTypeDefinitiontype)=>thrownewNotSupportedException();
3026

31-
publicstaticIEnumerable<TypeDefinition>GetTypeAndBaseTypes(thisTypeDefinitiontype,TypeDefinitionCache?cache)=>
32-
GetTypeAndBaseTypes(type,(IMetadataResolver?)cache);
27+
publicstaticIEnumerable<TypeDefinition>GetTypeAndBaseTypes(thisTypeDefinitiontype,TypeDefinitionCachecache)=>
28+
GetTypeAndBaseTypes(type,(IMetadataResolver)cache);
3329

34-
publicstaticIEnumerable<TypeDefinition>GetTypeAndBaseTypes(thisTypeDefinitiontype,IMetadataResolver?resolver)
30+
publicstaticIEnumerable<TypeDefinition>GetTypeAndBaseTypes(thisTypeDefinitiontype,IMetadataResolverresolver)
3531
{
3632
TypeDefinition?t=type;
3733

@@ -41,14 +37,13 @@ public static IEnumerable<TypeDefinition> GetTypeAndBaseTypes (this TypeDefiniti
4137
}
4238
}
4339

44-
[Obsolete("Use the TypeDefinitionCache overload for better performance.")]
45-
publicstaticIEnumerable<TypeDefinition>GetBaseTypes(thisTypeDefinitiontype)=>
46-
GetBaseTypes(type,resolver:null);
40+
[Obsolete("Use the TypeDefinitionCache overload for better performance.",error:true)]
41+
publicstaticIEnumerable<TypeDefinition>GetBaseTypes(thisTypeDefinitiontype)=>thrownewNotSupportedException();
4742

48-
publicstaticIEnumerable<TypeDefinition>GetBaseTypes(thisTypeDefinitiontype,TypeDefinitionCache?cache)=>
49-
GetBaseTypes(type,(IMetadataResolver?)cache);
43+
publicstaticIEnumerable<TypeDefinition>GetBaseTypes(thisTypeDefinitiontype,TypeDefinitionCachecache)=>
44+
GetBaseTypes(type,(IMetadataResolver)cache);
5045

51-
publicstaticIEnumerable<TypeDefinition>GetBaseTypes(thisTypeDefinitiontype,IMetadataResolver?resolver)
46+
publicstaticIEnumerable<TypeDefinition>GetBaseTypes(thisTypeDefinitiontype,IMetadataResolverresolver)
5247
{
5348
TypeDefinition?t=type;
5449

@@ -57,18 +52,17 @@ public static IEnumerable<TypeDefinition> GetBaseTypes (this TypeDefinition type
5752
}
5853
}
5954

60-
[Obsolete("Use the TypeDefinitionCache overload for better performance.")]
61-
publicstaticboolIsAssignableFrom(thisTypeReferencetype,TypeReferencec)=>
62-
IsAssignableFrom(type,c,resolver:null);
55+
[Obsolete("Use the TypeDefinitionCache overload for better performance.",error:true)]
56+
publicstaticboolIsAssignableFrom(thisTypeReferencetype,TypeReferencec)=>thrownewNotSupportedException();
6357

64-
publicstaticboolIsAssignableFrom(thisTypeReferencetype,TypeReferencec,TypeDefinitionCache?cache)=>
65-
IsAssignableFrom(type,c,(IMetadataResolver?)cache);
58+
publicstaticboolIsAssignableFrom(thisTypeReferencetype,TypeReferencec,TypeDefinitionCachecache)=>
59+
IsAssignableFrom(type,c,(IMetadataResolver)cache);
6660

67-
publicstaticboolIsAssignableFrom(thisTypeReferencetype,TypeReferencec,IMetadataResolver?resolver)
61+
publicstaticboolIsAssignableFrom(thisTypeReferencetype,TypeReferencec,IMetadataResolverresolver)
6862
{
6963
if(type.FullName==c.FullName)
7064
returntrue;
71-
vard=(resolver?.Resolve(c))??c.Resolve();
65+
vard=resolver.Resolve(c);
7266
if(d==null)
7367
returnfalse;
7468
foreach(vartind.GetTypeAndBaseTypes(resolver)){
@@ -83,13 +77,12 @@ public static bool IsAssignableFrom (this TypeReference type, TypeReference c, I
8377
returnfalse;
8478
}
8579

86-
[Obsolete("Use the TypeDefinitionCache overload for better performance.")]
87-
publicstaticboolIsSubclassOf(thisTypeDefinitiontype,stringtypeName)=>
88-
IsSubclassOf(type,typeName,resolver:null);
80+
[Obsolete("Use the TypeDefinitionCache overload for better performance.",error:true)]
81+
publicstaticboolIsSubclassOf(thisTypeDefinitiontype,stringtypeName)=>thrownewNotSupportedException();
8982

90-
publicstaticboolIsSubclassOf(thisTypeDefinitiontype,stringtypeName,TypeDefinitionCache?cache)=>
91-
IsSubclassOf(type,typeName,(IMetadataResolver?)cache);
92-
publicstaticboolIsSubclassOf(thisTypeDefinitiontype,stringtypeName,IMetadataResolver?resolver)
83+
publicstaticboolIsSubclassOf(thisTypeDefinitiontype,stringtypeName,TypeDefinitionCachecache)=>
84+
IsSubclassOf(type,typeName,(IMetadataResolver)cache);
85+
publicstaticboolIsSubclassOf(thisTypeDefinitiontype,stringtypeName,IMetadataResolverresolver)
9386
{
9487
foreach(vartintype.GetTypeAndBaseTypes(resolver)){
9588
if(t.FullName==typeName){
@@ -99,14 +92,13 @@ public static bool IsSubclassOf (this TypeDefinition type, string typeName, IMet
9992
returnfalse;
10093
}
10194

102-
[Obsolete("Use the TypeDefinitionCache overload for better performance.")]
103-
publicstaticboolImplementsInterface(thisTypeDefinitiontype,stringinterfaceName)=>
104-
ImplementsInterface(type,interfaceName,resolver:null);
95+
[Obsolete("Use the TypeDefinitionCache overload for better performance.",error:true)]
96+
publicstaticboolImplementsInterface(thisTypeDefinitiontype,stringinterfaceName)=>thrownewNotSupportedException();
10597

106-
publicstaticboolImplementsInterface(thisTypeDefinitiontype,stringinterfaceName,TypeDefinitionCache?cache)=>
107-
ImplementsInterface(type,interfaceName,(IMetadataResolver?)cache);
98+
publicstaticboolImplementsInterface(thisTypeDefinitiontype,stringinterfaceName,TypeDefinitionCachecache)=>
99+
ImplementsInterface(type,interfaceName,(IMetadataResolver)cache);
108100

109-
publicstaticboolImplementsInterface(thisTypeDefinitiontype,stringinterfaceName,IMetadataResolver?resolver)
101+
publicstaticboolImplementsInterface(thisTypeDefinitiontype,stringinterfaceName,IMetadataResolverresolver)
110102
{
111103
foreach(vartintype.GetTypeAndBaseTypes(resolver)){
112104
foreach(variint.Interfaces){
@@ -118,27 +110,25 @@ public static bool ImplementsInterface (this TypeDefinition type, string interfa
118110
returnfalse;
119111
}
120112

121-
[Obsolete("Use the TypeDefinitionCache overload for better performance.")]
122-
publicstaticstringGetPartialAssemblyName(thisTypeReferencetype)=>
123-
GetPartialAssemblyName(type,resolver:null);
113+
[Obsolete("Use the TypeDefinitionCache overload for better performance.",error:true)]
114+
publicstaticstringGetPartialAssemblyName(thisTypeReferencetype)=>thrownewNotSupportedException();
124115

125-
publicstaticstringGetPartialAssemblyName(thisTypeReferencetype,TypeDefinitionCache?cache)=>
126-
GetPartialAssemblyName(type,(IMetadataResolver?)cache);
116+
publicstaticstringGetPartialAssemblyName(thisTypeReferencetype,TypeDefinitionCachecache)=>
117+
GetPartialAssemblyName(type,(IMetadataResolver)cache);
127118

128-
publicstaticstringGetPartialAssemblyName(thisTypeReferencetype,IMetadataResolver?resolver)
119+
publicstaticstringGetPartialAssemblyName(thisTypeReferencetype,IMetadataResolverresolver)
129120
{
130-
TypeDefinition?def=(resolver?.Resolve(type))??type.Resolve();
121+
TypeDefinition?def=resolver.Resolve(type);
131122
return(def??type).Module.Assembly.Name.Name;
132123
}
133124

134-
[Obsolete("Use the TypeDefinitionCache overload for better performance.")]
135-
publicstaticstringGetPartialAssemblyQualifiedName(thisTypeReferencetype)=>
136-
GetPartialAssemblyQualifiedName(type,resolver:null);
125+
[Obsolete("Use the TypeDefinitionCache overload for better performance.",error:true)]
126+
publicstaticstringGetPartialAssemblyQualifiedName(thisTypeReferencetype)=>thrownewNotSupportedException();
137127

138-
publicstaticstringGetPartialAssemblyQualifiedName(thisTypeReferencetype,TypeDefinitionCache?cache)=>
139-
GetPartialAssemblyQualifiedName(type,(IMetadataResolver?)cache);
128+
publicstaticstringGetPartialAssemblyQualifiedName(thisTypeReferencetype,TypeDefinitionCachecache)=>
129+
GetPartialAssemblyQualifiedName(type,(IMetadataResolver)cache);
140130

141-
publicstaticstringGetPartialAssemblyQualifiedName(thisTypeReferencetype,IMetadataResolver?resolver)
131+
publicstaticstringGetPartialAssemblyQualifiedName(thisTypeReferencetype,IMetadataResolverresolver)
142132
{
143133
returnstring.Format("{0}, {1}",
144134
// Cecil likes to use '/' as the nested type separator, while
@@ -147,16 +137,15 @@ public static string GetPartialAssemblyQualifiedName (this TypeReference type, I
147137
type.GetPartialAssemblyName(resolver));
148138
}
149139

150-
[Obsolete("Use the TypeDefinitionCache overload for better performance.")]
151-
publicstaticstringGetAssemblyQualifiedName(thisTypeReferencetype)=>
152-
GetAssemblyQualifiedName(type,resolver:null);
140+
[Obsolete("Use the TypeDefinitionCache overload for better performance.",error:true)]
141+
publicstaticstringGetAssemblyQualifiedName(thisTypeReferencetype)=>thrownewNotSupportedException();
153142

154-
publicstaticstringGetAssemblyQualifiedName(thisTypeReferencetype,TypeDefinitionCache?cache)=>
155-
GetAssemblyQualifiedName(type,(IMetadataResolver?)cache);
143+
publicstaticstringGetAssemblyQualifiedName(thisTypeReferencetype,TypeDefinitionCachecache)=>
144+
GetAssemblyQualifiedName(type,(IMetadataResolver)cache);
156145

157-
publicstaticstringGetAssemblyQualifiedName(thisTypeReferencetype,IMetadataResolver?resolver)
146+
publicstaticstringGetAssemblyQualifiedName(thisTypeReferencetype,IMetadataResolverresolver)
158147
{
159-
TypeDefinition?def=(resolver?.Resolve(type))??type.Resolve();
148+
TypeDefinition?def=resolver.Resolve(type);
160149
returnstring.Format("{0}, {1}",
161150
// Cecil likes to use '/' as the nested type separator, while
162151
// Reflection uses '+' as the nested type separator. Use Reflection.

0 commit comments

Comments
 (0)