Skip to content

Commit f863351

Browse files
authored
[generator] Only use [JniTypeSignatureAttribute] for JavaInterop1 (#1266)
Context: #1268 Context: 78d5937 Commit 78d5937 included this snippet: > Update `generator` to support using `Java.Base.dll` as a referenced > assembly for binding purposes, in particular by supporting the use > of `[JniTypeSignatureAttribute]` on already bound types. Aside: the symbol table used by `generator` uses the "full Java name" as the key. Which means that after 78d5937, types with `JniTypeSignatureAttribute.SimpleReference` values are now added to the symbol table, which in turn means that when given: // C# [JniTypeSignature ("B", ArrayRank=1, IsKeyword=true, GenerateJavaPeer=false)] public sealed partial class JavaSByteArray : JavaPrimitiveArray<SByte> { } when `generator` needs to find type information corresponding to the Java name of `B`, it will find `JavaSByteArray`! This apparently breaks binding the [`androidx.work:work-runtime`][0] Maven package, as [`androidx.work.WorkRequest.Builder.setId(UUID)`][1] has a return type of `B`, a generic type parameter: // Java public abstract /* partial */ class androidx.work.WorkRequest.Builder< B extends androidx.work.WorkRequest$Builder<B, ?>, W extends androidx.work.WorkRequest> { public final B setId(java.util.UUID); } As a workaround, only use `[JniTypeSignature]` when using `generator --codegen-target=JavaInterop1`. `generator --codegen-target=XAJavaInterop1` will only look for `[RegisterAttribute]` when constructing the symbol table. This commit plumbs `CodeGenerationOptions` deeper into `generator` and a new `ApiImporterOptions` into `Java.Interop.Tools.JavaTypeSystem` that is used to determine whether we should be importing `[JniTypeSignatureAttribute]` types. TODO? #1268 [0]: https://maven.google.com/web/index.html#androidx.work:work-runtime [1]: https://developer.android.com/reference/androidx/work/WorkRequest.Builder?hl=en#setId(java.util.UUID)
1 parent 56cfab9 commit f863351

File tree

7 files changed

+90
-68
lines changed

7 files changed

+90
-68
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using System;
2+
using System.Collections.ObjectModel;
3+
4+
namespace Java.Interop.Tools.JavaTypeSystem;
5+
6+
public class ApiImporterOptions
7+
{
8+
public Collection<string> SupportedTypeMapAttributes { get; } = ["Android.Runtime.RegisterAttribute"];
9+
}

src/Java.Interop.Tools.JavaTypeSystem/Adapters/ManagedApiImporter.cs

+34-34
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@ public static class ManagedApiImporter
1313
[Obsolete ("Use the TypeDefinitionCache overload for better performance.", error: true)]
1414
public static JavaTypeCollection Parse (AssemblyDefinition assembly, JavaTypeCollection collection) => throw new NotSupportedException ();
1515

16-
public static JavaTypeCollection Parse (AssemblyDefinition assembly, JavaTypeCollection collection, TypeDefinitionCache resolver)
16+
public static JavaTypeCollection Parse (AssemblyDefinition assembly, JavaTypeCollection collection, TypeDefinitionCache resolver, ApiImporterOptions options)
1717
{
1818
var types_to_add = new List<JavaTypeModel> ();
1919

2020
foreach (var md in assembly.Modules)
2121
foreach (var td in md.Types) {
22-
if (!ShouldSkipType (td, resolver) && ParseType (td, collection) is JavaTypeModel type)
22+
if (!ShouldSkipType (td, resolver, options) && ParseType (td, collection, options) is JavaTypeModel type)
2323
types_to_add.Add (type);
2424
}
2525

@@ -33,21 +33,21 @@ public static JavaTypeCollection Parse (AssemblyDefinition assembly, JavaTypeCol
3333
return collection;
3434
}
3535

36-
public static JavaTypeModel? ParseType (TypeDefinition type, JavaTypeCollection collection)
36+
public static JavaTypeModel? ParseType (TypeDefinition type, JavaTypeCollection collection, ApiImporterOptions options)
3737
{
3838
if (!type.IsPublic && !type.IsNested)
3939
return null;
4040

4141
if (!ShouldImport (type))
4242
return null;
4343

44-
var model = type.IsInterface ? (JavaTypeModel?) ParseInterface (type, collection) : ParseClass (type, collection);
44+
var model = type.IsInterface ? (JavaTypeModel?) ParseInterface (type, collection, options) : ParseClass (type, collection, options);
4545

4646
if (model is null)
4747
return null;
4848

4949
foreach (var nested in type.NestedTypes)
50-
if (ParseType (nested, collection) is JavaTypeModel nested_model)
50+
if (ParseType (nested, collection, options) is JavaTypeModel nested_model)
5151
model.NestedTypes.Add (nested_model);
5252

5353
return model;
@@ -89,19 +89,19 @@ static bool ShouldImport (TypeDefinition td)
8989
return true;
9090
}
9191

92-
public static JavaClassModel? ParseClass (TypeDefinition type, JavaTypeCollection collection)
92+
public static JavaClassModel? ParseClass (TypeDefinition type, JavaTypeCollection collection, ApiImporterOptions options)
9393
{
9494
// TODO: type parameters?
9595
var obs_attr = GetObsoleteAttribute (type.CustomAttributes);
96-
var reg_attr = GetRegisterAttribute (type.CustomAttributes);
96+
var reg_attr = GetRegisterAttribute (type.CustomAttributes, options);
9797

9898
if (reg_attr is null)
9999
return null;
100100

101101
var encoded_fullname = ((string) reg_attr.ConstructorArguments [0].Value).Replace ('/', '.');
102102
var (package, nested_name) = DecodeRegisterJavaFullName (encoded_fullname);
103103

104-
var base_jni = GetBaseTypeJni (type);
104+
var base_jni = GetBaseTypeJni (type, options);
105105

106106
var model = new JavaClassModel (
107107
javaPackage: GetOrCreatePackage (collection, package, type.Namespace),
@@ -118,20 +118,20 @@ static bool ShouldImport (TypeDefinition td)
118118
annotatedVisibility: string.Empty
119119
); ;
120120

121-
ParseImplementedInterfaces (type, model);
121+
ParseImplementedInterfaces (type, model, options);
122122

123123
foreach (var method in type.Methods.Where (m => !m.IsConstructor))
124-
if (ParseMethod (method, model) is JavaMethodModel m)
124+
if (ParseMethod (method, model, options) is JavaMethodModel m)
125125
model.Methods.Add (m);
126126

127127
return model;
128128
}
129129

130-
public static JavaInterfaceModel? ParseInterface (TypeDefinition type, JavaTypeCollection collection)
130+
public static JavaInterfaceModel? ParseInterface (TypeDefinition type, JavaTypeCollection collection, ApiImporterOptions options)
131131
{
132132
// TODO: type paramters?
133133
var obs_attr = GetObsoleteAttribute (type.CustomAttributes);
134-
var reg_attr = GetRegisterAttribute (type.CustomAttributes);
134+
var reg_attr = GetRegisterAttribute (type.CustomAttributes, options);
135135

136136
if (reg_attr is null)
137137
return null;
@@ -149,22 +149,22 @@ static bool ShouldImport (TypeDefinition td)
149149
annotatedVisibility: ""
150150
);
151151

152-
ParseImplementedInterfaces (type, model);
152+
ParseImplementedInterfaces (type, model, options);
153153

154154
foreach (var method in type.Methods)
155-
if (ParseMethod (method, model) is JavaMethodModel m)
155+
if (ParseMethod (method, model, options) is JavaMethodModel m)
156156
model.Methods.Add (m);
157157

158158
return model;
159159
}
160160

161-
public static JavaMethodModel? ParseMethod (MethodDefinition method, JavaTypeModel declaringType)
161+
public static JavaMethodModel? ParseMethod (MethodDefinition method, JavaTypeModel declaringType, ApiImporterOptions options)
162162
{
163163
if (method.IsPrivate || method.IsAssembly)
164164
return null;
165165

166166
var obs_attr = GetObsoleteAttribute (method.CustomAttributes);
167-
var reg_attr = GetRegisterAttribute (method.CustomAttributes);
167+
var reg_attr = GetRegisterAttribute (method.CustomAttributes, options);
168168

169169
if (reg_attr is null)
170170
return null;
@@ -225,7 +225,7 @@ static void AddReferenceTypeRecursive (JavaTypeModel type, JavaTypeCollection co
225225
AddReferenceTypeRecursive (nested, collection);
226226
}
227227

228-
static bool ShouldSkipType (TypeDefinition type, TypeDefinitionCache cache)
228+
static bool ShouldSkipType (TypeDefinition type, TypeDefinitionCache cache, ApiImporterOptions options)
229229
{
230230
// We want to use Java's collection types instead of our managed adapter.
231231
// eg: 'Java.Util.ArrayList' over 'Android.Runtime.JavaList'
@@ -246,28 +246,28 @@ static bool ShouldSkipType (TypeDefinition type, TypeDefinitionCache cache)
246246
? type.Module.GetType (type.FullName.Substring (0, type.FullName.IndexOf ('`')))
247247
: null;
248248

249-
if (ShouldSkipGeneric (type, non_generic_type, cache))
249+
if (ShouldSkipGeneric (type, non_generic_type, cache, options))
250250
return true;
251251

252252
return false;
253253
}
254254

255-
static bool ShouldSkipGeneric (TypeDefinition? a, TypeDefinition? b, TypeDefinitionCache cache)
255+
static bool ShouldSkipGeneric (TypeDefinition? a, TypeDefinition? b, TypeDefinitionCache cache, ApiImporterOptions options)
256256
{
257257
if (a == null || b == null)
258258
return false;
259259
if (!a.ImplementsInterface ("Android.Runtime.IJavaObject", cache) || !b.ImplementsInterface ("Android.Runtime.IJavaObject", cache))
260260
return false;
261261

262-
return GetRegisteredJavaTypeName (a) == GetRegisteredJavaTypeName (b);
262+
return GetRegisteredJavaTypeName (a, options) == GetRegisteredJavaTypeName (b, options);
263263
}
264264

265-
static string? TypeReferenceToJavaType (TypeReference type)
265+
static string? TypeReferenceToJavaType (TypeReference type, ApiImporterOptions options)
266266
{
267-
var retval = GetRegisteredJavaName (type);
267+
var retval = GetRegisteredJavaName (type, options);
268268

269269
if (retval != null && type is GenericInstanceType generic) {
270-
var parameters = generic.GenericArguments.Select (ga => GetRegisteredJavaName (ga.Resolve ())).ToArray ();
270+
var parameters = generic.GenericArguments.Select (ga => GetRegisteredJavaName (ga.Resolve (), options)).ToArray ();
271271

272272
if (parameters.WhereNotNull ().Any ())
273273
retval += $"<{string.Join (", ", parameters.WhereNotNull ())}>";
@@ -276,14 +276,14 @@ static bool ShouldSkipGeneric (TypeDefinition? a, TypeDefinition? b, TypeDefinit
276276
return retval;
277277
}
278278

279-
static string? GetRegisteredJavaName (TypeReference type)
279+
static string? GetRegisteredJavaName (TypeReference type, ApiImporterOptions options)
280280
{
281281
var td = type.Resolve ();
282282

283-
return GetRegisteredJavaTypeName (td);
283+
return GetRegisteredJavaTypeName (td, options);
284284
}
285285

286-
static void ParseImplementedInterfaces (TypeDefinition type, JavaTypeModel model)
286+
static void ParseImplementedInterfaces (TypeDefinition type, JavaTypeModel model, ApiImporterOptions options)
287287
{
288288
foreach (var iface_impl in type.Interfaces) {
289289
var iface = iface_impl.InterfaceType;
@@ -292,7 +292,7 @@ static void ParseImplementedInterfaces (TypeDefinition type, JavaTypeModel model
292292
if (iface_def is null || iface_def.IsNotPublic)
293293
continue;
294294

295-
if (GetRegisterAttribute (iface_def.CustomAttributes) is CustomAttribute reg_attr) {
295+
if (GetRegisterAttribute (iface_def.CustomAttributes, options) is CustomAttribute reg_attr) {
296296
var jni = (string) reg_attr.ConstructorArguments [0].Value;
297297
var name = jni.Replace ('/', '.').Replace ('$', '.');
298298

@@ -301,7 +301,7 @@ static void ParseImplementedInterfaces (TypeDefinition type, JavaTypeModel model
301301
}
302302
}
303303

304-
static string GetBaseTypeJni (TypeDefinition type)
304+
static string GetBaseTypeJni (TypeDefinition type, ApiImporterOptions options)
305305
{
306306
// Find a Java base type, ignoring generic types, if nothing else it will be Java.Lang.Object
307307
TypeDefinition? base_type = type;
@@ -319,7 +319,7 @@ static string GetBaseTypeJni (TypeDefinition type)
319319
if (base_type.HasGenericParameters || base_type.IsGenericInstance)
320320
continue;
321321

322-
if (GetRegisterAttribute (base_type.CustomAttributes) is CustomAttribute reg_attr)
322+
if (GetRegisterAttribute (base_type.CustomAttributes, options) is CustomAttribute reg_attr)
323323
return (string) reg_attr.ConstructorArguments [0].Value;
324324
}
325325

@@ -329,19 +329,19 @@ static string GetBaseTypeJni (TypeDefinition type)
329329
static CustomAttribute? GetObsoleteAttribute (Collection<CustomAttribute> attributes) =>
330330
attributes.FirstOrDefault (a => a.AttributeType.FullNameCorrected () == "System.ObsoleteAttribute");
331331

332-
static CustomAttribute? GetRegisterAttribute (Collection<CustomAttribute> attributes) =>
332+
static CustomAttribute? GetRegisterAttribute (Collection<CustomAttribute> attributes, ApiImporterOptions options) =>
333333
attributes.FirstOrDefault (a => {
334334
var attrType = a.AttributeType.FullNameCorrected ();
335-
return attrType == "Android.Runtime.RegisterAttribute" ||
336-
attrType == "Java.Interop.JniTypeSignatureAttribute";
335+
336+
return options.SupportedTypeMapAttributes.Contains (attrType);
337337
});
338338

339-
static string? GetRegisteredJavaTypeName (TypeDefinition type)
339+
static string? GetRegisteredJavaTypeName (TypeDefinition type, ApiImporterOptions options)
340340
{
341341
if (GetSpecialCase (type) is string s)
342342
return s;
343343

344-
if (GetRegisterAttribute (type.CustomAttributes) is CustomAttribute reg_attr)
344+
if (GetRegisterAttribute (type.CustomAttributes, options) is CustomAttribute reg_attr)
345345
return ((string) reg_attr.ConstructorArguments [0].Value).Replace ('/', '.');
346346

347347
return null;

src/Java.Interop.Tools.JavaTypeSystem/Java.Interop.Tools.JavaTypeSystem.csproj

-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
<PropertyGroup>
44
<TargetFramework>$(DotNetTargetFramework)</TargetFramework>
55
<Nullable>enable</Nullable>
6-
<LangVersion>8.0</LangVersion>
76
</PropertyGroup>
87

98
<Import Project="..\..\TargetFrameworkDependentValues.props" />

tests/generator-Tests/Unit-Tests/ManagedTests.cs

+13-13
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ public void Method ()
162162
{
163163
var type = module.GetType ("Com.Mypackage.Foo");
164164
var @class = CecilApiImporter.CreateClass (type, options);
165-
var method = CecilApiImporter.CreateMethod (@class, type.Methods.First (m => m.Name == "Bar"));
165+
var method = CecilApiImporter.CreateMethod (@class, type.Methods.First (m => m.Name == "Bar"), options);
166166
Assert.IsTrue (method.Validate (new CodeGenerationOptions (), new GenericParameterDefinitionList (), new CodeGeneratorContext ()), "method.Validate failed!");
167167

168168
Assert.AreEqual ("public", method.Visibility);
@@ -183,8 +183,8 @@ public void Method_Matches_True ()
183183
var type = module.GetType ("Com.Mypackage.Foo");
184184
var @class = CecilApiImporter.CreateClass (type, options);
185185
var unknownTypes = type.Methods.First (m => m.Name == "UnknownTypes");
186-
var methodA = CecilApiImporter.CreateMethod (@class, unknownTypes);
187-
var methodB = CecilApiImporter.CreateMethod (@class, unknownTypes);
186+
var methodA = CecilApiImporter.CreateMethod (@class, unknownTypes, options);
187+
var methodB = CecilApiImporter.CreateMethod (@class, unknownTypes, options);
188188
Assert.IsTrue (methodA.Matches (methodB), "Methods should match!");
189189
}
190190

@@ -196,8 +196,8 @@ public void Method_Matches_False ()
196196
var unknownTypesA = type.Methods.First (m => m.Name == "UnknownTypes");
197197
var unknownTypesB = type.Methods.First (m => m.Name == "UnknownTypesReturn");
198198
unknownTypesB.Name = "UnknownTypes";
199-
var methodA = CecilApiImporter.CreateMethod (@class, unknownTypesA);
200-
var methodB = CecilApiImporter.CreateMethod (@class, unknownTypesB);
199+
var methodA = CecilApiImporter.CreateMethod (@class, unknownTypesA, options);
200+
var methodB = CecilApiImporter.CreateMethod (@class, unknownTypesB, options);
201201
//Everything the same besides return type
202202
Assert.IsFalse (methodA.Matches (methodB), "Methods should not match!");
203203
}
@@ -207,7 +207,7 @@ public void MethodWithParameters ()
207207
{
208208
var type = module.GetType ("Com.Mypackage.Foo");
209209
var @class = CecilApiImporter.CreateClass (type, options);
210-
var method = CecilApiImporter.CreateMethod (@class, type.Methods.First (m => m.Name == "BarWithParams"));
210+
var method = CecilApiImporter.CreateMethod (@class, type.Methods.First (m => m.Name == "BarWithParams"), options);
211211
Assert.IsTrue (method.Validate (new CodeGenerationOptions (), new GenericParameterDefinitionList (), new CodeGeneratorContext ()), "method.Validate failed!");
212212
Assert.AreEqual ("(ZID)Ljava/lang/String;", method.JniSignature);
213213
Assert.AreEqual ("java.lang.String", method.Return);
@@ -237,7 +237,7 @@ public void Ctor ()
237237
{
238238
var type = module.GetType ("Com.Mypackage.Foo");
239239
var @class = CecilApiImporter.CreateClass (type, options);
240-
var ctor = CecilApiImporter.CreateCtor (@class, type.Methods.First (m => m.IsConstructor && !m.IsStatic));
240+
var ctor = CecilApiImporter.CreateCtor (@class, type.Methods.First (m => m.IsConstructor && !m.IsStatic), options);
241241
Assert.IsTrue (ctor.Validate (new CodeGenerationOptions (), new GenericParameterDefinitionList (), new CodeGeneratorContext ()), "ctor.Validate failed!");
242242

243243
Assert.AreEqual ("public", ctor.Visibility);
@@ -251,7 +251,7 @@ public void Field ()
251251
{
252252
var type = module.GetType ("Com.Mypackage.Foo");
253253
var @class = CecilApiImporter.CreateClass (type, options);
254-
var field = CecilApiImporter.CreateField (type.Fields.First (f => f.Name == "Value"));
254+
var field = CecilApiImporter.CreateField (type.Fields.First (f => f.Name == "Value"), options);
255255
Assert.IsTrue (field.Validate (new CodeGenerationOptions (), new GenericParameterDefinitionList (), new CodeGeneratorContext ()), "field.Validate failed!");
256256

257257
Assert.AreEqual ("Value", field.Name);
@@ -303,23 +303,23 @@ public void TypeNullability ()
303303
var type = module.GetType ("NullableTestTypes.NullableClass");
304304
var gen = CecilApiImporter.CreateClass (module.GetType ("NullableTestTypes.NullableClass"), options);
305305

306-
var not_null_field = CecilApiImporter.CreateField (type.Fields.First (f => f.Name == "not_null_field"));
306+
var not_null_field = CecilApiImporter.CreateField (type.Fields.First (f => f.Name == "not_null_field"), options);
307307
Assert.AreEqual (true, not_null_field.NotNull);
308308

309-
var null_field = CecilApiImporter.CreateField (type.Fields.First (f => f.Name == "null_field"));
309+
var null_field = CecilApiImporter.CreateField (type.Fields.First (f => f.Name == "null_field"), options);
310310
Assert.AreEqual (false, null_field.NotNull);
311311

312-
var null_method = CecilApiImporter.CreateMethod (gen, type.Methods.First (f => f.Name == "NullableReturnMethod"));
312+
var null_method = CecilApiImporter.CreateMethod (gen, type.Methods.First (f => f.Name == "NullableReturnMethod"), options);
313313
Assert.AreEqual (false, null_method.ReturnNotNull);
314314
Assert.AreEqual (true, null_method.Parameters.First (f => f.Name == "notnull").NotNull);
315315
Assert.AreEqual (false, null_method.Parameters.First (f => f.Name == "nullable").NotNull);
316316

317-
var not_null_method = CecilApiImporter.CreateMethod (gen, type.Methods.First (f => f.Name == "NotNullReturnMethod"));
317+
var not_null_method = CecilApiImporter.CreateMethod (gen, type.Methods.First (f => f.Name == "NotNullReturnMethod"), options);
318318
Assert.AreEqual (true, not_null_method.ReturnNotNull);
319319
Assert.AreEqual (true, not_null_method.Parameters.First (f => f.Name == "notnull").NotNull);
320320
Assert.AreEqual (false, not_null_method.Parameters.First (f => f.Name == "nullable").NotNull);
321321

322-
var ctor = CecilApiImporter.CreateCtor (gen, type.Methods.First (f => f.Name == ".ctor"));
322+
var ctor = CecilApiImporter.CreateCtor (gen, type.Methods.First (f => f.Name == ".ctor"), options);
323323
Assert.AreEqual (true, ctor.Parameters.First (f => f.Name == "notnull").NotNull);
324324
Assert.AreEqual (false, ctor.Parameters.First (f => f.Name == "nullable").NotNull);
325325
}

tools/generator/CodeGenerator.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ static void Run (CodeGeneratorOptions options, DirectoryAssemblyResolver resolve
113113
// Resolve types using Java.Interop.Tools.JavaTypeSystem
114114
if (is_classparse && !options.UseLegacyJavaResolver) {
115115
var output_xml = api_xml_adjuster_output ?? Path.Combine (Path.GetDirectoryName (filename), Path.GetFileName (filename) + ".adjusted");
116-
JavaTypeResolutionFixups.Fixup (filename, output_xml, resolver, references.Distinct ().ToArray (), resolverCache);
116+
JavaTypeResolutionFixups.Fixup (filename, output_xml, resolver, references.Distinct ().ToArray (), resolverCache, options);
117117

118118
if (only_xml_adjuster)
119119
return;

0 commit comments

Comments
 (0)