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

Add nullable JS API parameter support #1756

Merged
merged 3 commits into from
Jan 31, 2025

Conversation

drubanovich-soti
Copy link
Contributor

This change allows to define JS APIs which receive nullable primitive type parameters. E.g. before the change it was not possible to pass null to JS function with string parameter, instead the function received a string "null".

As standard Java doesn't have nullable types, this feature currently can only be used if the files are written in Kotlin. Kotlin metadata library is used to detect nullable parameter types.

@drubanovich-soti
Copy link
Contributor Author

drubanovich-soti commented Dec 13, 2024

I would like to run this idea with you before I fix the module (it doesn't export Kotlin dependencies, so the clients will need to explicitly add those) and add unit tests.
Thanks!

@gbrail
Copy link
Collaborator

gbrail commented Dec 14, 2024

Thanks for working on this! This is an interesting idea, and it'd be great to have Rhino work better with Kotlin.

However, right now Rhino has no dependencies, and I think it's important that we keep it that way. This PR makes Rhino depend on a Kotlin package. I don't know how intrusive that is (like how many nested dependencies it brings in) but I'm still very reluctant to make a core library like this something that Rhino requires.

Is there another way to do this, like making the dependency optional and searching for it via reflection, or adding a "rhino-kotlin" module that's loaded via ServiceLoader, or incorporating the part of the Kotlin library that Rhino needs in to the project directly?

@drubanovich-soti
Copy link
Contributor Author

Sure Greg, if you like the idea, I will work on a proper implementation.
I'll try your ServiceLoader suggestion with Kotlin-specific implementation in a new "rhino-kotlin" module.

@gbrail
Copy link
Collaborator

gbrail commented Dec 14, 2024 via email

@drubanovich-soti
Copy link
Contributor Author

I finally got some time to work on it. I tested it with my project and it works fine.
I still plan to add some unit tests and maybe Readme.md in kotlin-rhino, but it would be great to receive any feedback for the implementation. E.g. I wasn't sure about module/package names, maybe you can provide a better alternative.

@gbrail
Copy link
Collaborator

gbrail commented Jan 23, 2025

I think that this approach will work -- having a separate "rhino-kotlin" module sounds good to me. Of course, I really think we need to find a way to build some test, even if it only runs as part of our CI, to actually exercise this code with Kotlin.

Also, I don't necessarily understand why there is a new module called "rhino-reflection." Would this work if we only added the new "rhino-kotlin" module?

@drubanovich-soti
Copy link
Contributor Author

drubanovich-soti commented Jan 23, 2025

I've added Kotlin tests in the third commit, note that it brings in several Kotlin dependencies though.

I don't necessarily understand why there is a new module called "rhino-reflection." Would this work if we only added the new "rhino-kotlin" module?

I can make NullabilityDetector interface to be a part of rhino. A separate "rhino-reflection" interface module gives more flexibility to clients though. E.g. clients who don't want to use Kotlin but still want to create JS functions with nullable primitive parameters can write their own NullabilityDetector implementation which relies on their custom Nullable annotations.
Another reason is that we can now move rhino-kotlin into a different repository, making this one kotlin-independent.

P.S. After I wrote that I realized both "flexibility advantages" could be also achieved without a separate rhino-reflection module. The only minor advantage of rhino-reflection is that rhino-kotlin (and any custom extension) have more focused dependencies. I will work on merging rhino-reflection into rhino.

@gbrail
Copy link
Collaborator

gbrail commented Jan 24, 2025

I think that we should move the contents of "rhino-reflection" into the main "rhino" package. It's a very small amount of code and I don't think we lose anything in terms of flexibility by having it be part of the main module. Then we can still provide the behavior that the new behavior only is triggered when the "rhino-kotlin" package is present.

@drubanovich-soti drubanovich-soti force-pushed the nullable-parameters branch 3 times, most recently from 170ab77 to 44e102b Compare January 27, 2025 19:17
@drubanovich-soti
Copy link
Contributor Author

Hi Greg, I added the tests and addressed the comments.

Copy link
Collaborator

@gbrail gbrail left a comment

Choose a reason for hiding this comment

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

Two small comments on this (but I'd like to fix both before we merge).

And one last thing -- can you please update README.md to add a description of the new module and a short description of why someone might want to use it? Thanks!

}

public NullabilityDetector getImpl() {
Iterator<NullabilityDetector> iterator = loader.iterator();
Copy link
Collaborator

Choose a reason for hiding this comment

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

I (very recently) added ScriptRuntime.loadOneServiceImplementation to handle this case and throw an error if there are duplicates (to avoid inconsistency if the classpath is weird). Can you look at using that? It's package-public but could easily be public.


@Test
public void checkArgumentConversion() {
Context context = new Context();
Copy link
Collaborator

Choose a reason for hiding this comment

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

That context constructor is deprecated with a good reason. Take a look at the Javadoc for it. You can either use "Context.enter()" in a "try-with-resources" block, or better yet you can use Utils.runWithAllModes, which will test your stuff in both interpreted and compiled mode.

This change allows to define JS APIs which receive nullable primitive
type parameters, by overriding NullabilityDetector interface.
E.g. clients can introduce Nullable-like annotation which they can
read at runtime, thus enabling passing null to JS function with string
parameter (by default such function receives a string "null")

One of the following commits will introduce KotlinNullabilityDetector,
which will rely on parsing Kotlin metadata and will allow a better
inter-operation of Rhino with Kotlin.
KotlinNullabilityDetector relies on Kotlin Metadata to detect parameter
nullability.

The limitation of this detector is that it cannot distinguish between
overloads with the same number of parameters. To avoid false positives,
KotlinNullabilityDetector returns array of 'false' values for methods
or constructors with such overloads.
@drubanovich-soti
Copy link
Contributor Author

Back for the review

@gbrail
Copy link
Collaborator

gbrail commented Jan 31, 2025

This looks good to me, and thanks for doing all this work!

@gbrail gbrail merged commit 07492b5 into mozilla:master Jan 31, 2025
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants