-
Notifications
You must be signed in to change notification settings - Fork 56
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[generator] Only use [JniTypeSignatureAttribute]
for JavaInterop1
.
#1266
Conversation
fc97dd7
to
c7cdf36
Compare
@@ -249,11 +249,17 @@ static CustomAttribute GetObsoleteAttribute (Collection<CustomAttribute> attribu | |||
static string GetObsoleteComment (CustomAttribute attribute) => | |||
attribute?.ConstructorArguments.Any () == true ? (string) attribute.ConstructorArguments [0].Value : null; | |||
|
|||
static CustomAttribute GetRegisterAttribute (Collection<CustomAttribute> attributes) => | |||
static CustomAttribute GetRegisterAttribute (Collection<CustomAttribute> attributes, CodeGenerationOptions opt) => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't this overload instead take ApiImporterOptions
so that it can check ApiImporterOptions. SupportedRegisterAttributes
, a'la GetRegisterAttribute()
within ManagedApiImporter.cs
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The issue is that we're working in 2 different assemblies with both of these seemingly identical changes.
In generator
, we have plumbed CodeGenerationOptions
through most of the assembly, including the main public entry points to CecilApiImporter
: CreateClass
and CreateInterface
. In this case it makes the most sense to continue using our exist "options" class and plumb it a little deeper.
ApiImporterOptions
is in Java.Interop.Tools.JavaTypeSystem
, which did not previously have an "options" class so one had to be added. As JavaTypeSystem
is a more general assembly it should not have the concept of JavaInterop1
/XAJavaInterop1
, so the shape of the "options" class is a little different.
|
||
public class ApiImporterOptions | ||
{ | ||
public Collection<string> SupportedRegisterAttributes { get; } = ["Android.Runtime.RegisterAttribute"]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be SupportedTypeMapAttributes
? So that we could have a SupportedMethodMapAttributes
(should we need one)?
@jpobst: This should also add a unit test of some form which demonstrates the problem we're trying to avoid here, to reduce the chance of future AndroidX breakage. |
This would be ideal, but I'm not sure exactly what we can test. The issue is I suspect we're going to end up revisiting this later. We have fixed this for |
The #1266 summary says: > Only use `[JniTypeSignatureAttribute]` for JavaInterop1 *Actually* do so: for XAJavaInterop1, don't allow `[JniTypeSignatureAttribute]`. For JavaInterop1, don't allow [Register]. Eventually we'll want XAJavaInterop1 to import JniTypeSignatureAttribute (maybe?), but not now.
Does It Build™?
Does It Build™?
@jpobst: one thing confuses me with the described scenario: 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);
} Yes, if someone had |
@jpobst: another point of confusion: the error message:
implies it's trying to emit a property. However, there is no When I
which makes a bit more sense -- this isn't dealing with "method properties", so there doesn't need to be a Which leads me to believe that I don't understand what "in method SetId in managed type Java.Interop.JavaSByteArray" is actually saying, which makes me feel like we don't fully understand this scenario. |
Draft commit message: Context: https://github.com/dotnet/java-interop/issues/1268
Context: 78d59371a9e27aa601bc32a07a529d09e2f7c796
Commit 78d59371 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 78d59371, 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? dotnet/java-interop#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) |
No, that part is still wrong (yay generics!) and this API likely won't bind because it won't find a
Referencing This leads to the question "why is The answer surprises me, as it appears we only import fields that do not have a java-interop/tools/generator/Java.Interop.Tools.Generator.Importers/CecilApiImporter.cs Lines 44 to 46 in b656f7f
This feels wrong, and I assumed I must have broken something in my past java-interop/tools/generator/ClassGen.cs Lines 45 to 47 in 4e364ba
So either it is correct, or it's wrong but doesn't really matter, as we don't really need to know about inherited fields since they don't play a part in binding a subclass. 🤷 |
@jpobst: then re-upping a previous comment: can we unit test this? I thought (wrongly?) that a generic type named diff --git a/tests/generator-Tests/expected.ji/GenericArguments/GenericArguments.xml b/tests/generator-Tests/expected.ji/GenericArguments/GenericArguments.xml
index f9387a08..04cf4838 100644
--- a/tests/generator-Tests/expected.ji/GenericArguments/GenericArguments.xml
+++ b/tests/generator-Tests/expected.ji/GenericArguments/GenericArguments.xml
@@ -4,6 +4,18 @@
<class abstract="false" deprecated="not deprecated" final="false" name="Object" static="false" visibility="public">
</class>
</package>
+ <package name="my.java.util">
+ <interface abstract="true" deprecated="not deprecated" final="false" name="Iterator" static="false" visibility="public">
+ <typeParameters>
+ <typeParameter name="B">
+ <genericConstraints>
+ <genericConstraint type="java.lang.Object" />
+ </genericConstraints>
+ </typeParameter>
+ </typeParameters>
+ <method abstract="true" deprecated="not deprecated" final="false" name="next" native="false" return="B" jni-return="TB;" static="false" synchronized="false" visibility="public" />
+ </interface>
+ </package>
<package name="com.google.android.exoplayer.drm">
<interface abstract="true" deprecated="not deprecated" final="false" name="ExoMediaCrypto" static="false" visibility="public">
<method abstract="true" deprecated="not deprecated" final="false" name="requiresSecureDecoderComponent" native="false" return="boolean" static="false" synchronized="false" visibility="public">
diff --git a/tests/generator-Tests/expected.xaji/GenericArguments/GenericArguments.xml b/tests/generator-Tests/expected.xaji/GenericArguments/GenericArguments.xml
index f9387a08..04cf4838 100644
--- a/tests/generator-Tests/expected.xaji/GenericArguments/GenericArguments.xml
+++ b/tests/generator-Tests/expected.xaji/GenericArguments/GenericArguments.xml
@@ -4,6 +4,18 @@
<class abstract="false" deprecated="not deprecated" final="false" name="Object" static="false" visibility="public">
</class>
</package>
+ <package name="my.java.util">
+ <interface abstract="true" deprecated="not deprecated" final="false" name="Iterator" static="false" visibility="public">
+ <typeParameters>
+ <typeParameter name="B">
+ <genericConstraints>
+ <genericConstraint type="java.lang.Object" />
+ </genericConstraints>
+ </typeParameter>
+ </typeParameters>
+ <method abstract="true" deprecated="not deprecated" final="false" name="next" native="false" return="B" jni-return="TB;" static="false" synchronized="false" visibility="public" />
+ </interface>
+ </package>
<package name="com.google.android.exoplayer.drm">
<interface abstract="true" deprecated="not deprecated" final="false" name="ExoMediaCrypto" static="false" visibility="public">
<method abstract="true" deprecated="not deprecated" final="false" name="requiresSecureDecoderComponent" native="false" return="boolean" static="false" synchronized="false" visibility="public"> This didn't trigger/generate I think the problem is that Do we have any existing tests which involve It doesn't look like we do until #1261… |
Context: #1266 Context: #1268 While the issue described in #1266 is definitely an issue, that example was merely setting up the scenario needed to expose another bug: When a `Java.Lang.Object` or `Java.Interop.JavaObject` subclass has a `public` or `internal` field whose type is a nested type: // C# namespace Com.Mypackage; [Register ("com/mypackage/FieldClass")] public class FieldClass : Java.Lang.Object { public NestedFieldClass field; public class NestedFieldClass : Java.Lang.Object { } } We correctly import the `Field.TypeName` as `Com.Mypackage.FieldClass.NestedFieldClass`, however we also create `Field.SetterParameter` with the "same" type, but we do not replace the `/` nested type separator with a period, resulting in `Com.Mypackage.FieldClass/NestedFieldClass`. Later, when validating the `Field`, we successfully find `Field.TypeName` in the symbol table, but fail to find `Field.SetterParameter` as the symbol table expects a period nested type separator. (Note this only happens because `NestedFieldClass` does not have a `[Register]` attribute, thus it gets added to the symbol table with its managed name rather than its Java name.) This causes `generator` to crash with: System.NotSupportedException: Unable to generate setter parameter list in managed type Com.Mypackage.FieldClass at MonoDroid.Generation.Field.Validate(CodeGenerationOptions opt, GenericParameterDefinitionList type_params, CodeGeneratorContext context) at MonoDroid.Generation.GenBase.OnValidate(CodeGenerationOptions opt, GenericParameterDefinitionList type_params, CodeGeneratorContext context) at MonoDroid.Generation.ClassGen.OnValidate(CodeGenerationOptions opt, GenericParameterDefinitionList type_params, CodeGeneratorContext context) at MonoDroid.Generation.GenBase.Validate(CodeGenerationOptions opt, GenericParameterDefinitionList type_params, CodeGeneratorContext context) Fix this by applying the same `FullNameCorrected()` logic that is applied to `Field.TypeName`.
Changes: dotnet/java-interop@9d99723...2a1e180 * dotnet/java-interop@2a1e1800: [generator] Fix StackOverflow when copying DIM via private interfaces (dotnet/java-interop#1261) * dotnet/java-interop@f863351e: [generator] Only use `[JniTypeSignatureAttribute]` for `JavaInterop1` (dotnet/java-interop#1266) * dotnet/java-interop@56cfab93: [generator] Fix exception caused by incorrect nested type name. (dotnet/java-interop#1267) * dotnet/java-interop@b656f7f5: [generator] Add support for `skipInterfaceMethods` (dotnet/java-interop#1265)
Changes: dotnet/java-interop@9d99723...2a1e180 * dotnet/java-interop@2a1e1800: [generator] Fix StackOverflow when copying DIM via private interfaces (dotnet/java-interop#1261) * dotnet/java-interop@f863351e: [generator] Only use `[JniTypeSignatureAttribute]` for `JavaInterop1` (dotnet/java-interop#1266) * dotnet/java-interop@56cfab93: [generator] Fix exception caused by incorrect nested type name. (dotnet/java-interop#1267) * dotnet/java-interop@b656f7f5: [generator] Add support for `skipInterfaceMethods` (dotnet/java-interop#1265)
Registering types that are annotated with
[JniTypeSignatureAttribute]
to add to thegenerator
type symbol table causes issues, as theJava*Array
types fromJava.Interop
get added:This type is added to the type symbol table with a key of
B
for the JNIByte
type, but the symbol table doesn't deal in JNI types, thus this type will get chosen when a method parameter or return type has a generic type named"B"
(or a global package type"B"
).When binding AndroidX with .NET 9, this causes the error:
Java Documentation
A workaround is to only use
[JniTypeSignatureAttribute]
for theJavaInterop1
generator target.This commit plumbs
CodeGenerationOptions
deeper intogenerator
and a newApiImporterOptions
intoJava.Interop.Tools.JavaTypeSystem
that is used to determine whether we should be importing[JniTypeSignatureAttribute]
types.dotnet/android
test PR: dotnet/android#9406