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

JavaClassWrapper: Improve handling of typed array arguments #102817

Merged

Conversation

dsnopek
Copy link
Contributor

@dsnopek dsnopek commented Feb 13, 2025

Fixes #102712

There are 3 issues that come from that:

  • Method selection doesn't take typed arrays (including the Packed*Array types) into account when selected an overloaded method
  • [x] vararg methods aren't supported
  • Arrays of JavaObjects aren't supported (this one I noticed while working on this)

So far, this PR only fixes the first one, which is why this is currently marked as DRAFT.

UPDATE: All the issues are fixed now in my testing!

UPDATE 2: I ended up deciding to remove the explicit support for varargs - see this comment.

@dsnopek dsnopek added this to the 4.x milestone Feb 13, 2025
@dsnopek dsnopek requested a review from a team as a code owner February 13, 2025 18:47
@dsnopek dsnopek marked this pull request as draft February 13, 2025 18:47
@dsnopek dsnopek force-pushed the java-class-wrapper-array-improvements branch 2 times, most recently from d405381 to 19b3254 Compare February 13, 2025 23:23
@dsnopek dsnopek marked this pull request as ready for review February 13, 2025 23:24
@dsnopek
Copy link
Contributor Author

dsnopek commented Feb 13, 2025

I've now got all the issues fixed in my testing!

However, this does change quite a large amount of code, and will need a thorough review.

@dsnopek dsnopek changed the title [DRAFT] JavaClassWrapper: Improve handling of array arguments JavaClassWrapper: Improve handling of array arguments Feb 13, 2025
@dsnopek dsnopek force-pushed the java-class-wrapper-array-improvements branch from 19b3254 to 874e835 Compare February 14, 2025 14:37
@dsnopek
Copy link
Contributor Author

dsnopek commented Feb 14, 2025

After sleeping on this, I've decided to remove the explicit varargs support, and just treat Java varargs methods as having a final array parameter (which is contrary to what I originally wrote here).

While implementing Java varargs as GDScript varargs makes for more idiomatic GDScript code, it creates problems with selecting the correct overridden method, since varargs in GDScript are untyped whereas in Java they are typed. This would make it impossible to distinguish between these two methods from GDScript:

        @JvmStatic
        fun testMethod(int: Int, vararg args: String): String {
            return "String varargs: " + args.contentToString()
        }

        @JvmStatic
        fun testMethod(int: Int, vararg args: Int): String {
            return "Int varargs: " + args.contentToString()
        }

... whereas, if we treat that final argument as a normal array, you can use either Array[String] or Array[int] in GDScript to target one or the other.

So, that's how it works now in my latest push! (I still have previous varargs approach in a branch, in case we want to go back to it.)

This also makes the PR much smaller and easier to review, perhaps making it more likely we could sneak it in for Godot 4.4?

cc @m4gr3d @syntaxerror247

@dsnopek dsnopek changed the title JavaClassWrapper: Improve handling of array arguments JavaClassWrapper: Improve handling of typed array arguments Feb 14, 2025
Copy link
Member

@syntaxerror247 syntaxerror247 left a comment

Choose a reason for hiding this comment

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

The code looks good to me. I prefer the current approach (treating Java varargs methods as having a final array parameter), but I'd like to hear @m4gr3d's preference and leave the approval to him.

@dsnopek dsnopek modified the milestones: 4.x, 4.5 Feb 18, 2025
@akien-mga akien-mga requested a review from m4gr3d February 27, 2025 18:03
Copy link
Contributor

@m4gr3d m4gr3d left a comment

Choose a reason for hiding this comment

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

Looks great!

The changes are small and scoped enough they should be safe to include in 4.4. Leaving it up to @akien-mga to decide.

if (cn.begins_with("[L") && cn.ends_with(";")) {
cn = cn.substr(2, cn.length() - 3);
}
jclass c = env->FindClass(cn.utf8().get_data());
Copy link
Contributor

Choose a reason for hiding this comment

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

We should check that c is valid before proceeding.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We would have checked this earlier on line 224.

This process goes in two phase: a validation phase, where it reports errors about any of the provided arguments, and then a calling phase where (if there were no validation errors) it marshals the data into the Java types and actually calls the method. This would have gotten caught in the validation phase.

However, it's no problem to add if (c) { ... }, so I have done that in my latest push :-)

@m4gr3d m4gr3d modified the milestones: 4.5, 4.4 Feb 27, 2025
@dsnopek dsnopek force-pushed the java-class-wrapper-array-improvements branch from 874e835 to dfa8936 Compare February 27, 2025 22:25
@dsnopek
Copy link
Contributor Author

dsnopek commented Feb 27, 2025

The changes are small and scoped enough they should be safe to include in 4.4.

The ship may have sailed on Godot 4.4, but it'd fine for this to end up in 4.4.1.

@dsnopek dsnopek force-pushed the java-class-wrapper-array-improvements branch from dfa8936 to d767212 Compare March 3, 2025 18:53
@dsnopek
Copy link
Contributor Author

dsnopek commented Mar 3, 2025

In my latest push, I added one of the same efficiency improvements as I did in PR #103375, where JNI fills some the arrays (the ones where the types match) rather than us going element by element. I have a test project that comprehensively tests all the conversion branches in this one, so I'm pretty confident about this update!

for (int j = 0; j < arr.size(); j++) {
jshort val = arr[j];
env->SetShortArrayRegion(a, j, 1, &val);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

Does the copy logic not work for short?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Not with the way that I decided to convert from PackedInt32Array: it's set up to treat each item in the PackedInt32Array as representing a single short.

If we decided that each item in the PackedInt32Array represented 2 shorts (with one of them shifted two bytes to the left) then we could copy directly. However, that didn't seem like the most friendly for developers - my guess is that they would assume it was the former.

Although, that was just a judgement call I made! If anyone thinks otherwise, let's discuss :-)

Copy link
Contributor Author

@dsnopek dsnopek Mar 4, 2025

Choose a reason for hiding this comment

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

Actually, thinking about this a little more, I don't think we actually could treat each item in PackedInt32Array as representing two shorts: how would you pass an array with an odd number of items? There'd be no way to indicate that the last element didn't exist.

I think the way I'm doing the conversion is really the only way to do it if we use PackedInt32Array. If we wanted to pack them tighter, we could use PackedByteArray, but that would be so tricky to use from GDScript.

I'm happy with the judgement call I made here: it optimizes for ease of use by the developer, not performance or lower memory usage. I don't think APIs that take short[] are super common anyway, so odds are this conversion won't get used that much

Copy link
Member

@syntaxerror247 syntaxerror247 Mar 4, 2025

Choose a reason for hiding this comment

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

Yeah, I don't really remember the last time I used short[].

@Repiteo Repiteo merged commit 8ef0075 into godotengine:master Mar 5, 2025
20 checks passed
@Repiteo
Copy link
Contributor

Repiteo commented Mar 5, 2025

Thanks!

@akien-mga
Copy link
Member

Cherry-picked for 4.4.1.

@akien-mga akien-mga added topic:plugin and removed cherrypick:4.4 Considered for cherry-picking into a future 4.4.x release labels Mar 12, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

When using JavaClassWrapper, calling a method with multiple overloaded parameters will cause an error.
5 participants