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

Additional fixes and improvements to JavaClassWrapper #99492

Merged
merged 1 commit into from
Dec 18, 2024

Conversation

m4gr3d
Copy link
Contributor

@m4gr3d m4gr3d commented Nov 21, 2024

Follow up to #96182

  • Fix crashing bug when invoking Java class constructor with parameters
  • Add support for accessing class constants
  • Add support for Godot Callable arguments. A Godot Callable can be wrapped by a Java Runnable to allow Java logic to run arbitrary Godot lambdas
  • Automatically convert java.lang.CharSequence to Godot String as needed
  • Code cleanup

Those fixes make godotengine/godot-proposals#11189 feasible out of the box.

@syntaxerror247

This comment was marked as outdated.

Copy link
Contributor

@dsnopek dsnopek left a comment

Choose a reason for hiding this comment

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

The high-level idea of this looks great!

However, I suspect that some of the memory management around callables (particularly the Variant * that it hangs on to) isn't quite right. More details in the code comments.

I haven't had a chance to properly test it yet, though.


jclass bclass = p_env->FindClass("org/godotengine/godot/variant/Callable");
jmethodID ctor = p_env->GetMethodID(bclass, "<init>", "(J)V");
jobject jcallable = p_env->NewObject(bclass, ctor, reinterpret_cast<int64_t>(p_callable));
Copy link
Contributor

@dsnopek dsnopek Nov 21, 2024

Choose a reason for hiding this comment

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

I'm nervous about the p_callable pointer: do we know for sure that the memory it points to will remain alive?

Like, the org.godotengine.godot.variant.Callable class is only going to hang on to the pointer, and we aren't managing our own Variant * (I would imagine memnew() one here, and then have Callable do a memdelete() when it's cleaned up), so we're basically dependent on the caller to callable_to_jcallable() ensuring that the Variant stays alive.

I think the simplest solution to this problem is exactly what I described above: memnew()ing our own Variant *, assigning *p_callable to it, and having the Callable class memdelete() it up when it is destroyed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@dsnopek Thanks for the suggestion! I've updated the PR, and that also fixes the issue @syntaxerror247 was seeing where the toast doesn't show again. @syntaxerror247 Can you test again and check if the callable is called and the toast shows?

and having the Callable class memdelete() it up when it is destroyed.

This part is more complex to do because java objects don't get callbacks when they're destroyed, so we don't have a mechanism to know when to clean up. I've omitted it for now, which means we're allocating Variant * but not freeing, so looking for suggestions.

Copy link
Member

Choose a reason for hiding this comment

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

@m4gr3d I tested again, now it works!

Copy link
Contributor Author

Choose a reason for hiding this comment

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

This part is more complex to do because java objects don't get callbacks when they're destroyed, so we don't have a mechanism to know when to clean up. I've omitted it for now, which means we're allocating Variant * but not freeing, so looking for suggestions.

I ended up using the Object::finalize() method in order to detect when to deallocate the native memory.
As marked in the documentation, that method has been deprecated; unfortunately its replacement, java.lang.ref.Cleaner is only supported on Android api 33 and higher, so we'll need to update that logic when our min api target catches up.

Copy link
Contributor

Choose a reason for hiding this comment

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

The solution to this issue in the latest version of the PR looks good to me!

platform/android/java_class_wrapper.cpp Outdated Show resolved Hide resolved
platform/android/jni_utils.cpp Outdated Show resolved Hide resolved
@m4gr3d m4gr3d force-pushed the improve_java_class_wrapper branch 2 times, most recently from 02e6313 to 9b6719f Compare December 1, 2024 21:42
Copy link
Contributor

@dsnopek dsnopek left a comment

Choose a reason for hiding this comment

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

Thanks!

The latest code changes look good to me as far as fixing the memory management issue that I commented on in my last review! However, while looking at the PR again with memory management on my mind, I think I may have found another issue - a memory leak this time. See the code comments below for more details.

I also did some quick testing: basically, just trying the example given in the code comments in this PR. It worked great!

@llama-nl
Copy link

llama-nl commented Dec 13, 2024

I have a question:
based on this https://developer.android.com/reference/android/os/Build

I tried this code:

var android_runtime = Engine.get_singleton("AndroidRuntime")
if android_runtime:
	var build_class = JavaClassWrapper.wrap("android.os.Build") #JavaCalss  <android.os.Build>
	var model = build_class.BRAND
	print("Device Brand: %s (%s)" % [model])
else:
	printerr("Couldn't find AndroidRuntime singleton.")

And it was showing this error:

Invalid access to property or key 'BRAND' on a base object of type 'JavaClass'.

@dsnopek
Copy link
Contributor

dsnopek commented Dec 13, 2024

@llama-nl While there is some old code for doing fields and constants in JavaClassWrapper, we haven't fixed it yet (I had a note about that on PR #96182 - that PR focussed on fixing methods)

It shouldn't be too hard to do, though. I'll try and take a look at it soon!

@m4gr3d m4gr3d force-pushed the improve_java_class_wrapper branch 2 times, most recently from 7995875 to 350910f Compare December 18, 2024 02:41
@m4gr3d
Copy link
Contributor Author

m4gr3d commented Dec 18, 2024

@dsnopek I've addressed your feedback!

@llama-nl I've also fixed access to the class constants. Your code sample should now work with the latest version of this PR.

- Fix crashing bug when invoking class constructor with parameters
- Add support for accessing class constants
- Add support for Godot Callable arguments. A Godot Callable can be wrapped by a Java Runnable to allow Java logic to run arbitrary Godot lambdas
- Automatically convert java.lang.CharSequence to Godot String as needed
- Code cleanup
@m4gr3d m4gr3d force-pushed the improve_java_class_wrapper branch from 350910f to 23cea1b Compare December 18, 2024 02:46
Copy link
Contributor

@dsnopek dsnopek left a comment

Choose a reason for hiding this comment

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

Thanks! This looks great to me :-)

I've also fixed access to the class constants.

Oh, awesome! I hadn't found the time to dig into it myself yet

@akien-mga akien-mga merged commit 62e0266 into godotengine:master Dec 18, 2024
20 checks passed
@akien-mga
Copy link
Member

Thanks!

@m4gr3d m4gr3d deleted the improve_java_class_wrapper branch December 18, 2024 18:54
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.

5 participants