-
Notifications
You must be signed in to change notification settings - Fork 564
[marshal methods] Part 2 of ? #7123
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
Conversation
b069552 to
8f142d8
Compare
cc9e57a to
e18d523
Compare
e18d523 to
d088e1f
Compare
#998) Context: dotnet/android#7123 Context: fb94d59 Instead of having `JavaCallableWrapperGenerator` care about and collect marshal methods override information, introduce a new `JavaCallableMethodClassifier` type, and use that as the integration point: public abstract partial class JavaCallableMethodClassifier { public abstract bool ShouldBeDynamicallyRegistered TypeDefinition topType, MethodDefinition registeredMethod, MethodDefinition implementedMethod, CustomAttribute registerAttribute ); } An instance of `JavaCallableMethodClassifier` can now be provided to the `JavaCallableWrapperGenerator` constructor. If provided, then `ShouldBeDynamicallyRegistered()` will be used to control which methods are added to `__md_methods` and `Runtime.register()`. If a type `JavaCallableWrapperGenerator` is processing doesn't have any dynamically registered methods, then the code to register them is no longer generated.
d088e1f to
b0e8cd8
Compare
b0e8cd8 to
1a1ffca
Compare
|
|
||
| res.Load (assembly.ItemSpec); | ||
| #if ENABLE_MARSHAL_METHODS | ||
| if (!Debug) { |
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.
Why not skip these if (!Debug) StoreMarshalAssemblyPath() checks and just call StoreMarshalAssemblyPath())? StoreMarshalAssemblyPath() checks Debug anyway, so this is just adding extra levels of complexity & indentation, for no observable benefit.
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.
In the first case we save one string allocation, in the second instance yes, it's not necessary
|
|
||
| // Step 2 - Generate type maps | ||
| // Type mappings need to use all the assemblies, always. | ||
| WriteTypeMappings (allJavaTypes, cache); |
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.
Why move this? Type mappings should be unrelated to the contents of the .java files, and allJavaTypes appears to be unchanged, so…. What "hidden"/"non-obvious" bits am I not seeing?
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.
Generating marshal methods also potentially rewrites assemblies, thus changing token ids and type maps must absolutely see the final ones.
| jti.Generate (writer); | ||
| #if ENABLE_MARSHAL_METHODS | ||
| overriddenMethodDescriptors.AddRange (jti.OverriddenMethodDescriptors); | ||
| if (!Debug) { |
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.
We could remove this if + indentation if we just did if (classifier?.FoundDynamicallyRegisteredMethods), thus implicitly relying on the fact that classifier is null in Debug builds.
But that means relying on "implicit context".
Not sure which tradeoff is "better": minimizing indentation or keeping things explicit.
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.
I much prefer if the code is explicit, makes it easier to understand the flow for someone not familiar with it
src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsAssemblyRewriter.cs
Outdated
Show resolved
Hide resolved
src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsAssemblyRewriter.cs
Outdated
Show resolved
Hide resolved
| method.CallbackField?.DeclaringType.Fields.Remove (method.CallbackField); | ||
| } | ||
|
|
||
| Console.WriteLine (); |
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.
I think we'll be wanting Log.LogDebugMessage(), not Console.WriteLine()…
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 CWLs will be gone eventually, I will remove them in the last PR in the series
| // versions around. | ||
| foreach (string path in newAssemblyPaths) { | ||
| string target = Path.Combine (Path.GetDirectoryName (path), Path.GetFileNameWithoutExtension (path)); | ||
| MoveFile (path, target); |
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.
We may want/need to rework the build system so that we write files into new/separate directories instead of modifying files in-place, otherwise we'll provoke the incremental build + file sharing gods.
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.
I asked @jonathanpeppers about this and he thought it was better to write them in place. We can optimize this in the last PR in the series though
| } | ||
| } | ||
| } | ||
| #endif |
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.
At this point I suspect I'm the only one who remembers this convention, but please place a comment on #endif with the #if expression:
#endif // ENABLE_MARSHAL_METHODSThere 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.
I apply the convention when it's there to stay :) This macro is going to go away in the last PR of the series, it's just a measure to not "poison" current code until marshal methods are finished
src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsClassifier.cs
Outdated
Show resolved
Hide resolved
src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsClassifier.cs
Outdated
Show resolved
Hide resolved
| RegisteredMethod = registeredMethod ?? throw new ArgumentNullException (nameof (registeredMethod)); | ||
| ImplementedMethod = implementedMethod ?? throw new ArgumentNullException (nameof (implementedMethod)); | ||
| CallbackField = callbackField; | ||
| JniTypeName = jniTypeName; |
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 we also verify that these are not null?
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.
CallbackField is not required, I'll add checks for the Jni* arguments
| public IDictionary<string, IList<MarshalMethodEntry>> MarshalMethods => marshalMethods; | ||
| public ICollection<AssemblyDefinition> Assemblies => assemblies; | ||
| public bool FoundDynamicallyRegisteredMethods => haveDynamicMethods; | ||
| #endif |
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.
Ditto #endif comment…
|
|
||
| TypeDefinitionCache tdCache; | ||
| DirectoryAssemblyResolver resolver; | ||
| Dictionary<string, IList<MarshalMethodEntry>> marshalMethods; |
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.
Given that this is a private field, why IList<MarshalMethodEntry> and not just List<MarshalMethodEntry>? Interface method invocation is (slightly) slower than class method invocation.
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 field is exported by the MarshalMethods property
62d3a01 to
8766a35
Compare
src/Xamarin.Android.Build.Tasks/Utilities/MarshalMethodsAssemblyRewriter.cs
Show resolved
Hide resolved
9171a38 to
27b6a4d
Compare
Implement a marshal method candidate classifier which is passed to JavaCallableWrapperGenerator in order to identify methods that can be registered "statically" and for which we can generate native code instead of using System.Reflection.Emit. The selected methods are marked with the `[UnmanagedCallersOnly]` attribute and their support code in the form of the connector method (which returns a delegate to the native callback) as well as its backing field which stores the delegate, are removed from the assemblies.
27b6a4d to
2449d6f
Compare
2449d6f to
f3c1d34
Compare
[Xamarin.Android.Build.Tasks, monodroid] Marshal Method Classifier
Context: e1af9587bb98d4c249bbc392ceccc2b53ffff155
Context: https://github.com/xamarin/xamarin-android/pull/7163
Changes: https://github.com/xamarin/java.interop/compare/c942ab683c12e88e0527ed584a13b411e005ea57...fadbb82c3b8ab7979c19e9f139bdf2589e47549e
* xamarin/java.interop@fadbb82c: [generator] Add support for @explicitInterface metadata (#1006)
* xamarin/java.interop@3e4a3c4f: [Java.Interop.Tools.JavaCallableWrappers] JavaCallableMethodClassifier (#998)
xamarin/java.interop@3e4a3c4f reworked how
`Java.Interop.Tools.JavaCallableWrappers.JavaCallableWrapperGenerator`
can be used to find JNI marshal methods.
Update `Xamarin.Android.Build.Tasks.dll` to provide a
`JavaCallableMethodClassifier` to `JavaCallableWrapperGenerator`,
collecting Cecil `MethodDefinition` instances for JNI marshal methods
which can be created at build time via LLVM Marshal Methods.
Methods which cannot be created at build time include methods with
`[Export]`.
Once candidate LLVM marshal methods are found, the marshal method is
updated to have the [`UnmanagedCallersOnlyAttribute` attribute][0],
and related System.Reflection.Emit-based infrastructure such as the
`Get…Handler()` methods and `cb_…` fields are removed.
As with e1af9587, this feature is *not* enabled by default, and
remains a xamarin-android Build time configuration option.
[0]:https://docs.microsoft.com/dotnet/api/system.runtime.interopservices.unmanagedcallersonlyattribute?view=net-6.0 |
Implement a marshal method candidate classifier which is passed to
JavaCallableWrapperGeneratorin order to identify methods that can be registered "statically" and for which we can generate
native code instead of using
System.Reflection.Emit.The selected methods are marked with the
[UnmanagedCallersOnly]attribute and their supportcode in the form of the connector method (which returns a delegate to the native callback) as
well as its backing field which stores the delegate, are removed from the assemblies.