Skip to content
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

Refactor Java.Interop.Tools.JavaCallableWrappers #1174

Merged
merged 10 commits into from
Feb 21, 2024
Merged

Refactor Java.Interop.Tools.JavaCallableWrappers #1174

merged 10 commits into from
Feb 21, 2024

Conversation

jpobst
Copy link
Contributor

@jpobst jpobst commented Dec 8, 2023

Context: #540

Refactor Java.Interop.Tools.JavaCallableWrappers to be more maintainable and modular. Adopts the same architecture as generator: using a CecilImporter to import data from a compiled assembly into an intermediate model. Java code is then generated from the intermediate model.

Refactoring and updating this code should make it not only more maintainable, but also easier to extend with potential upcoming features we are considering:

XA test PR which requires a few XA side updates: dotnet/android#8701

@jonpryor
Copy link
Member

An alternate architectural "solution" which needs consideration is to turn Java Callable Wrapper generation into an illink step part of the illink pipeline:

https://github.com/xamarin/xamarin-android/blob/4eb515499d623fd26b636d95db95f241c696f2f4/src/Xamarin.Android.Build.Tasks/Microsoft.Android.Sdk/targets/Microsoft.Android.Sdk.ILLink.targets#L49-L96

We believe (hope?) that this should be possible, could be an "interesting"/"useful" "forcing function" to help split up <GenerateJavaStubs/>, and would mean that we wouldn't need to have changes such as this current PR. (Though the "incremental build" aspect is unknown, and Debug config builds don't currently have an illink pipeline at all, and we don't know what the perf impacts would be if we introduced such a thing, and…)

@jpobst
Copy link
Contributor Author

jpobst commented Dec 11, 2023

Moving the concern of figuring out what to generate from the [Register] attributes will become an illink step in either architectural solution.

The difference would be the concern of "writing out the Java source code". This PR proposes that should be a separate task that runs after the linker. But it could also be done by the linker.

If feels like something the linker shouldn't be doing, but that ship may have already sailed, as we do other "non-linker" tasks in the linker. 😉

@jpobst jpobst force-pushed the refactor-jcw branch 3 times, most recently from a2951e9 to db551f5 Compare December 12, 2023 19:20
@jpobst jpobst mentioned this pull request Jan 26, 2024
@jpobst jpobst force-pushed the refactor-jcw branch 3 times, most recently from b85a837 to a8816b6 Compare February 6, 2024 18:03
@jpobst jpobst changed the title [JavaCallableWrappers] Begin modularizing jcw process. Refactor Java.Interop.Tools.JavaCallableWrappers Feb 6, 2024
@jpobst jpobst marked this pull request as ready for review February 6, 2024 23:18
@jpobst jpobst requested a review from jonpryor February 6, 2024 23:18

namespace Java.Interop.Tools.JavaCallableWrappers;

public class CallableWrapperWriterOptions
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What I don't immediately understand is the criteria used to determine which "options" go into CallableWrapperWriterOptions and which "options" become properties on the given type.

Consider https://github.com/xamarin/xamarin-android/pull/8701/files, which provides "context" for how these changes would be used within xamarin-android.

Every property set on jcw_type is the same for every instance of jcw_type.

Wouldn't it make more sense for the "invariant" properties to instead be on CallableWrapperWriterOptions, and then "hoist" the var options = new CallableWrapperWriterOptions before the foreach loop? This would make explicit the values which don't depend upon type being emitted, as they would be the same for all types, e.g.

var options = new CallableWrapperWriterOptions {
    ApplicationJavaClass = ApplicationJavaClass,
    CodeGenerationTarget = JavaPeerStyle.XAJavaInterop1,
    GenerateOnCreateOverrides = generateOnCreateOverrides,
    MonoRuntimeInitialization = monoInit,
};
foreach (JavaType jt in newJavaTypes) {var jcw_type = CecilImporter.CreateType (t, cache, methodClassifier: classifier);
    jcw_type.Generate (writer, options);
}

As a quick spot-check, CallableWrapperType.cs use ApplicationJavaClass, GenerateOnCreateOverrides, and MonoRuntimeInitialization from methods which already have a CallableWrapperWriterOptions options parameter, so such a change wouldn't be significant.

So why not have these "invariant" properties on CallableWrapperWriterOptions?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Counter-argument: CallableWrapperWriterOptions contains options that apply to Types, Fields, Methods, etc., and e.g. ApplicationJavaClass isn't used by CallableWrapperField.

Counter-counter-argument: CecilImporter.CreateType() is the entrypoint. If we ever needed to introduce a global MSBuild option specific to field generation, having a caller iterate over all fields within a CallableWrapperField to set a field-specific value would be incredibly annoying. It would be much easier to add such options to CallableWrapperWriterOptions directly. (Or pass it into CecilImporter.CreateType()? At which point we might instead want a CallableWrapperImportOptions?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Created CallableWrapperReaderOptions, as it seemed like the cleanest way to implement this.


public class CallableWrapperConstructor : CallableWrapperMethod
{
public bool CannotRegisterInStaticConstructor { get; set; }
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of these two fields, I think I'd prefer to have a public CallableWrapperType? DeclaringType {get;set;} property, then read CannotRegisterInStaticConstructor from e.g. DeclaringType.CannotRegisterInStaticConstructor. This would help ensure consistency, as these values cannot differ from the declaring type.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed

@jonpryor jonpryor merged commit c825dca into main Feb 21, 2024
4 checks passed
@jonpryor jonpryor deleted the refactor-jcw branch February 21, 2024 15:23
jonpryor pushed a commit to dotnet/android that referenced this pull request Feb 21, 2024
Changes: dotnet/java-interop@ae65609...c825dca

  * dotnet/java-interop@c825dcad: [Java.Interop.Tools.JavaCallableWrappers] Refactor (dotnet/java-interop#1174)

`Java.Interop.Tools.JavaCallableWrappers.dll` was refactored and now
has a significant API changes.

Bump to dotnet/java-interop@c825dcad and update callsites accordingly.
@github-actions github-actions bot locked and limited conversation to collaborators Apr 12, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants