-
Notifications
You must be signed in to change notification settings - Fork 53
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
Emit [DynamicDependency] to preserve Invokers #1263
Comments
[DynamicDependency(DynamicallyAccessedMemberTypes.All, typeof(IMyInterfaceInvoker))]
partial interface IMyInterface : IJavaPeerable {
// …
}
partial class IMyInterfaceInvoker : Java.Lang.Object, IMyInterface {
// …
} This is not valid, as Is there another way to accomplish this? Or do we need to add this attribute to every type member? 🤮 |
How do we get from the interface to the invoker at runtime? My general answer will always be: "Don't use |
@vitek-karas: please see this for a reasonable overview: 1adb796
How it works is via convention and Reflection.
We append "Invoker" to the abstract class or interface name, and resolve it from the same assembly:
We instantiate it:
I'm not sure I understand what that means. My guess is that you're suggesting we e.g. update partial class RegisterAttribute : Attribute {
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)]
public Type? InvokerType {get; set;}
} which would allow: [Register (…, InvokerType=typeof(IRunnableInvoker))]
public partial interface IRunnable {
}
internal partial class IRunnableInvoker {
} |
The |
@jpobst: okay, so this got more complicated. :-( Firstly, we need to update our attributes: namespace Java.Interop {
// In java-interop
public partial class JniTypeSignatureAttribute {
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)]
public Type? InvokerType {get; set;}
}
}
// in dotnet/android
namespace Android.Runtime {
partial class RegisterAttribute : Attribute {
[DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.All)]
public Type? InvokerType {get; set;}
}
} Then we need to update Then we need to update
@jpobst: open question: should we update |
I would also consider not using |
I think that's mostly what we actually want/need. (See also.) For interfaces, all interface members must be preserved, always, because Java might call them, and we can't know whether or not that'll actually happen. Abstract classes are more difficult, as we do want the linker to be able to remove non-overridden methods. However, if user code overrides a Java method, that must be preserved, especially when the only callers of that method are from Java. You're thus immediately right that a new |
Given that we already know there are issues with using If switching to |
Context: #1263 Context: #1263 (comment) We want the default trimmer infrastructure to be able to automatically preserve the `*Invoker` types which are required for interacting with `abstract` classes and interfaces. The most straightforward way to do this is to add a new `InvokerType` property to `JniTypeSignatureAttribute` (and eventually `RegisterAttribute`): partial class JniTypeSignatureAttribute { [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] public Type? InvokerType {get; set;} } Update `generator` so that `generator --codegen-target=JavaInterop1` output sets this new property on abstract classes and interfaces: namespace Java.Lang { [Java.Interop.JniTypeSignatureAttribute("java/lang/Runnable", GenerateJavaPeer=false, InvokerType=typeof(Java.Lang.IRunnableInvoker))] public partial interface IRunnable { } internal partial class IRunnableInvoker { } [Java.Interop.JniTypeSignatureAttribute("java/lang/Number", GenerateJavaPeer=false, InvokerType=typeof(Java.Lang.NumberInvoker))] public abstract partial class Number { } internal partial class NumberInvoker { } } This allows the default trimmer to automatically preserve the `*Invoker` type and constructors. Update `Java.Interop.JniRuntime.JniValueManager` to no longer look for `*Invoker` types "by string", and instead require use of the `JniTypeSignatureAttribute.InvokerType` property. Update unit tests and expected output so that everything works. Note: XAJavaInterop1 output is impacted, but it shouldn't matter, as it changes the `RegisterAttribute` `connector` on interfaces, which isn't *used* by anything: // old and busted [Register ("xamarin/test/NotificationCompatBase$Action$Factory", "", "Xamarin.Test.NotificationCompatBase/Action/IFactoryInvoker")] // new hawtness [Register ("xamarin/test/NotificationCompatBase$Action$Factory", "", "Xamarin.Test.NotificationCompatBase.Action.IFactoryInvoker")] Further note that the old value is non-sensical.
Context: #1263 Context: #1263 (comment) We want the default trimmer infrastructure to be able to automatically preserve the `*Invoker` types which are required for interacting with `abstract` classes and interfaces. The most straightforward way to do this is to add a new `InvokerType` property to `JniTypeSignatureAttribute` (and eventually `RegisterAttribute`): partial class JniTypeSignatureAttribute { [DynamicallyAccessedMembers (DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.NonPublicConstructors)] public Type? InvokerType {get; set;} } Update `generator` so that `generator --codegen-target=JavaInterop1` output sets this new property on abstract classes and interfaces: namespace Java.Lang { [Java.Interop.JniTypeSignatureAttribute("java/lang/Runnable", GenerateJavaPeer=false, InvokerType=typeof(Java.Lang.IRunnableInvoker))] public partial interface IRunnable { } internal partial class IRunnableInvoker { } [Java.Interop.JniTypeSignatureAttribute("java/lang/Number", GenerateJavaPeer=false, InvokerType=typeof(Java.Lang.NumberInvoker))] public abstract partial class Number { } internal partial class NumberInvoker { } } This allows the default trimmer to automatically preserve the `*Invoker` type and constructors. Update `Java.Interop.JniRuntime.JniValueManager` to no longer look for `*Invoker` types "by string", and instead require use of the `JniTypeSignatureAttribute.InvokerType` property. Update unit tests and expected output so that everything works.
In the current .NET for Android world order, the .NET for Android workload adds custom linker steps to preserve the
*Invoker
types when a bound Java type is preserved.In a possible future NativeAOT world order, there are no custom linker steps. We thus must use the linker-supported custom attributes to cause types to be preserved.
Given:
We should place
[DynamicDependency]
on the interface (and/orabstract
class) declaration so that the linker knows to preserve theInvoker
:The text was updated successfully, but these errors were encountered: