-
Notifications
You must be signed in to change notification settings - Fork 7
Update/jni pull to push #68
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
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.
Pull Request Overview
This PR converts synchronous source and encoder operations to asynchronous ones by introducing std::future<void> return types for key lifecycle methods. The changes enable better error handling and resource management in the multimedia framework.
Key changes:
- Convert Source/Encoder lifecycle methods (
startProducing,stopProducing,close) to returnstd::future<void> - Update test code to properly wait for async operations using
.get() - Add CTest integration and test discovery for better test automation
Reviewed Changes
Copilot reviewed 29 out of 29 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| linux/CMakeLists.txt | Add CTest support and compiler warnings for better code quality |
| libausbc/src/test/java/com/vsh/source/PullToPushSourceTests.kt | Update tests to use async patterns and add new test case |
| libausbc/src/test/java/com/vsh/source/EmptyConsumer.kt | Add test consumer implementation |
| Multiple C++ source files | Convert lifecycle methods to async and update method signatures |
| Multiple C++ header files | Update interface declarations to return futures |
| Java source files | Update Java interfaces to support the new async patterns |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
libausbc/src/main/jni/modules/sources/src/jni/JniPullToPushSource.cpp
Outdated
Show resolved
Hide resolved
| override fun startProducing(configuration: Source.ProducingConfiguration) { | ||
| TODO("Not yet implemented") | ||
| } |
Copilot
AI
Sep 30, 2025
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.
This method contains a TODO indicating incomplete implementation. Either implement the method or document why the implementation is deferred.
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.
Pull Request Overview
Copilot reviewed 41 out of 43 changed files in this pull request and generated 7 comments.
Comments suppressed due to low confidence (2)
libausbc/src/main/jni/modules/sources/src/TestFileSource.cpp:1
- close() now returns a std::future and launches an async task that captures this. Calling it from the destructor without waiting can cause use-after-free. Call close().get() here (or make close synchronous) to ensure cleanup completes before destruction.
#include "TestFileSource.h"
libausbc/src/main/jni/modules/sources/src/jni/JniTestSource.cpp:1
- stopProducing() and close() now return futures, but the JNI shims return without waiting. This breaks synchronous semantics on the Java side. Append .get() in both calls to ensure the operations complete before returning.
#include <jni.h>
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| class OpenConfiguration( | ||
| tag: String, | ||
| val pullSource: JniSource<*, *> | ||
| ) : Source.OpenConfiguration(tag) { | ||
| } | ||
| val pullSource: JniSource<*, *>, | ||
| consumer: Consumer | ||
| ) : PushSource.OpenConfiguration(tag, consumer) |
Copilot
AI
Oct 4, 2025
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.
OpenConfiguration passes a Consumer to PushSource.OpenConfiguration, but PushSource.OpenConfiguration now requires a JniConsumer. This won't compile and also prevents native wiring of the consumer. Change the parameter type to JniConsumer and pass it through.
| super.open(configuration) | ||
| val sourcePtr = configuration.pullSource.getNativeObject() | ||
| this.openConfig = configuration | ||
| nativeOpen(sourcePtr, configuration.tag) |
Copilot
AI
Oct 4, 2025
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.
nativeOpen is called with the pullSource pointer as the first argument, but on the native side the first parameter is the PullToPushSource instance pointer. Additionally, nativeOpen is not declared as an external method in this class and the C++ registration does not expose it. Define an external nativeOpen with the correct signature and fix the call, e.g. nativeOpen(getNativeObject(), configuration.tag, sourcePtr, configuration.consumer.getNativeObject()).
| auto openConfig = PullToPushSource::OpenConfiguration(); | ||
| openConfig.pullSource = std::shared_ptr<PullSource>(reinterpret_cast<PullSource*>(pull_source_ptr)); | ||
| source->open(openConfig); |
Copilot
AI
Oct 4, 2025
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 consumer/frame callback is not set when opening PullToPushSource, so pushed frames will be dropped. Extend the JNI signature to accept a consumer ID and set it here via JniSourcesRepo (or pass a callback), e.g. openConfig.consumer = JniSourcesRepo::getInstance()->getConsumer(consumerId); then source->open(openConfig).
| void JniPullToPushSource_register(JNIEnv *env) { | ||
| jclass clazz = env->FindClass("com/vsh/source/PullToPushSource"); | ||
| if (clazz != nullptr) { | ||
| static const JNINativeMethod methods[] = { | ||
| {CONST_LITERAL("nativeCreate"), CONST_LITERAL("()J"), | ||
| (void *) &Java_com_vsh_source_PullToPushSource_nativeCreate}, | ||
| {CONST_LITERAL("nativeRelease"), CONST_LITERAL("(J)V"), | ||
| (void *) &Java_com_vsh_source_PullToPushSource_nativeRelease}, | ||
| }; | ||
| env->RegisterNatives(clazz, methods, sizeof(methods) / sizeof(methods[0])); | ||
| env->DeleteLocalRef(clazz); | ||
| } | ||
| } |
Copilot
AI
Oct 4, 2025
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.
nativeOpen is implemented but not registered, so calls from Kotlin will fail with UnsatisfiedLinkError. Add the nativeOpen entry with the correct signature to this methods array.
| JNIEXPORT void JNICALL | ||
| Java_com_vsh_source_CountConsumer_nativeRelease(JNIEnv *env, jobject thiz, jint sourceId) { | ||
| JniSourcesRepo::getInstance()->removeConsumer(sourceId); | ||
| } | ||
|
|
||
| extern "C" | ||
| JNIEXPORT void JNICALL | ||
| Java_com_vsh_source_CountConsumer_nativeStopConsuming(JNIEnv *env, jobject thiz, jint sourceId) { | ||
| JniSourcesRepo::getInstance()->getConsumer(sourceId)->stopConsuming(); | ||
| } | ||
|
|
||
| void JniCountConsumer_register(JNIEnv *env) { | ||
| JniCountConsumer *source = new JniCountConsumer(); | ||
| source->stopConsuming(); | ||
| delete source; | ||
| jclass clazz = env->FindClass("com/vsh/source/CountConsumer"); | ||
| if (clazz != nullptr) { | ||
| static const JNINativeMethod methods[] = { | ||
| {CONST_LITERAL("nativeCreate"), CONST_LITERAL("()J"), | ||
| (void *) &Java_com_vsh_source_CountConsumer_nativeCreate}, | ||
| {CONST_LITERAL("nativeRelease"), CONST_LITERAL("(J)V"), | ||
| (void *) &Java_com_vsh_source_CountConsumer_nativeRelease}, | ||
| {CONST_LITERAL("nativeStopConsuming"), CONST_LITERAL("(J)V"), | ||
| (void *) &Java_com_vsh_source_CountConsumer_nativeStopConsuming}, | ||
| }; | ||
| env->RegisterNatives(clazz, methods, sizeof(methods) / sizeof(methods[0])); | ||
| env->DeleteLocalRef(clazz); | ||
| } | ||
| } |
Copilot
AI
Oct 4, 2025
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.
Type mismatch between registered signatures (J/long) and implementation parameters (jint) for nativeRelease/nativeStopConsuming. Align to a single type across Java/Kotlin/C++ (e.g., use jint/int everywhere for repo IDs) and update the method descriptors to (I)V, or change C++ to take jlong.
|
|
||
| class CountConsumer: JniConsumer { | ||
| private var nativePtr: Long = 0 | ||
|
|
Copilot
AI
Oct 4, 2025
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.
nativePtr is never initialized, so nativeRelease/nativeStopConsuming will be no-ops. Initialize it (e.g., in an init block) by calling nativeCreate(), and ensure close() calls nativeRelease.
| init { | |
| nativePtr = nativeCreate() | |
| } |
libausbc/src/main/jni/modules/sources/src/jni/JniTestSource.cpp
Outdated
Show resolved
Hide resolved
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.
Pull Request Overview
Copilot reviewed 48 out of 50 changed files in this pull request and generated 8 comments.
Comments suppressed due to low confidence (1)
libausbc/src/main/jni/modules/sources/src/TestFileSource.cpp:1
- close() is now [[nodiscard]] and returns std::future. The destructor should await it to ensure resources are released and to satisfy compile flags; use close().get();
#include "TestFileSource.h"
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| jlong source_ptr, | ||
| jstring tag, | ||
| jlong pull_source_ptr) { | ||
| auto *source = reinterpret_cast<PullToPushSource*>(source_ptr); | ||
| const char *native_tag = env->GetStringUTFChars(tag, nullptr); | ||
| auto openConfig = PullToPushSource::OpenConfiguration(); | ||
| openConfig.pullSource = std::shared_ptr<PullSource>(reinterpret_cast<PullSource*>(pull_source_ptr)); |
Copilot
AI
Oct 4, 2025
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.
JNI signature and ownership are incorrect. Kotlin declares nativeOpen(srcId: Int, tag: String, pullSrcId: Int), but this JNI function expects jlongs and treats them as raw pointers, then wraps them into shared_ptr, causing UB and mismatched signatures (will throw UnsatisfiedLinkError). Use jint IDs and resolve both source and pullSource from JniSourcesRepo; do not construct shared_ptr from a raw pointer.
| jlong source_ptr, | |
| jstring tag, | |
| jlong pull_source_ptr) { | |
| auto *source = reinterpret_cast<PullToPushSource*>(source_ptr); | |
| const char *native_tag = env->GetStringUTFChars(tag, nullptr); | |
| auto openConfig = PullToPushSource::OpenConfiguration(); | |
| openConfig.pullSource = std::shared_ptr<PullSource>(reinterpret_cast<PullSource*>(pull_source_ptr)); | |
| jint srcId, | |
| jstring tag, | |
| jint pullSrcId) { | |
| // Look up the PullToPushSource by srcId | |
| auto source = std::dynamic_pointer_cast<PullToPushSource>( | |
| JniSourcesRepo::getInstance()->getSource(srcId) | |
| ); | |
| if (!source) { | |
| // Optionally throw an exception or log error | |
| return; | |
| } | |
| const char *native_tag = env->GetStringUTFChars(tag, nullptr); | |
| auto openConfig = PullToPushSource::OpenConfiguration(); | |
| // Look up the PullSource by pullSrcId | |
| openConfig.pullSource = std::dynamic_pointer_cast<PullSource>( | |
| JniSourcesRepo::getInstance()->getSource(pullSrcId) | |
| ); |
| jlong pull_source_ptr) { | ||
| auto *source = reinterpret_cast<PullToPushSource*>(source_ptr); | ||
| const char *native_tag = env->GetStringUTFChars(tag, nullptr); | ||
| auto openConfig = PullToPushSource::OpenConfiguration(); | ||
| openConfig.pullSource = std::shared_ptr<PullSource>(reinterpret_cast<PullSource*>(pull_source_ptr)); |
Copilot
AI
Oct 4, 2025
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.
OpenConfiguration carries a consumer on the native side (via auvc::PushSource::OpenConfiguration), but the JNI bridge doesn't set it. Since OpenConfiguration in Kotlin now includes a consumer, extend nativeOpen to accept a consumer ID and set openConfig.consumer using JniSourcesRepo->getConsumer(consumerId) so frames can be delivered.
| jlong pull_source_ptr) { | |
| auto *source = reinterpret_cast<PullToPushSource*>(source_ptr); | |
| const char *native_tag = env->GetStringUTFChars(tag, nullptr); | |
| auto openConfig = PullToPushSource::OpenConfiguration(); | |
| openConfig.pullSource = std::shared_ptr<PullSource>(reinterpret_cast<PullSource*>(pull_source_ptr)); | |
| jlong pull_source_ptr, | |
| jint consumerId) { | |
| auto *source = reinterpret_cast<PullToPushSource*>(source_ptr); | |
| const char *native_tag = env->GetStringUTFChars(tag, nullptr); | |
| auto openConfig = PullToPushSource::OpenConfiguration(); | |
| openConfig.pullSource = std::shared_ptr<PullSource>(reinterpret_cast<PullSource*>(pull_source_ptr)); | |
| openConfig.consumer = JniSourcesRepo::getInstance()->getConsumer(consumerId); |
| Java_com_vsh_source_CountConsumer_nativeRelease(JNIEnv *env, jobject thiz, jint sourceId) { | ||
| JniSourcesRepo::getInstance()->removeConsumer(sourceId); | ||
| } | ||
|
|
||
| extern "C" | ||
| JNIEXPORT void JNICALL | ||
| Java_com_vsh_source_CountConsumer_nativeStopConsuming(JNIEnv *env, jobject thiz, jint sourceId) { | ||
| JniSourcesRepo::getInstance()->getConsumer(sourceId)->stopConsuming(); |
Copilot
AI
Oct 4, 2025
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.
JNI parameter types mismatch. Kotlin declares nativeRelease(ptr: Long) and nativeStopConsuming(ptr: Long), and JniCountConsumer_register binds signatures '(J)V', but these functions use jint. This mismatch will fail linkage or call conventions. Make both functions take jlong or change Kotlin/registration to use Int '(I)V' consistently with repo IDs.
| Java_com_vsh_source_CountConsumer_nativeRelease(JNIEnv *env, jobject thiz, jint sourceId) { | |
| JniSourcesRepo::getInstance()->removeConsumer(sourceId); | |
| } | |
| extern "C" | |
| JNIEXPORT void JNICALL | |
| Java_com_vsh_source_CountConsumer_nativeStopConsuming(JNIEnv *env, jobject thiz, jint sourceId) { | |
| JniSourcesRepo::getInstance()->getConsumer(sourceId)->stopConsuming(); | |
| Java_com_vsh_source_CountConsumer_nativeRelease(JNIEnv *env, jobject thiz, jlong sourceId) { | |
| JniSourcesRepo::getInstance()->removeConsumer(static_cast<int>(sourceId)); | |
| } | |
| extern "C" | |
| JNIEXPORT void JNICALL | |
| Java_com_vsh_source_CountConsumer_nativeStopConsuming(JNIEnv *env, jobject thiz, jlong sourceId) { | |
| JniSourcesRepo::getInstance()->getConsumer(static_cast<int>(sourceId))->stopConsuming(); |
| static const JNINativeMethod methods[] = { | ||
| {CONST_LITERAL("nativeCreate"), CONST_LITERAL("()J"), | ||
| (void *) &Java_com_vsh_source_CountConsumer_nativeCreate}, | ||
| {CONST_LITERAL("nativeRelease"), CONST_LITERAL("(J)V"), | ||
| (void *) &Java_com_vsh_source_CountConsumer_nativeRelease}, | ||
| {CONST_LITERAL("nativeStopConsuming"), CONST_LITERAL("(J)V"), | ||
| (void *) &Java_com_vsh_source_CountConsumer_nativeStopConsuming}, | ||
| }; |
Copilot
AI
Oct 4, 2025
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.
Registered signatures use '(J)V' for nativeRelease and nativeStopConsuming, but the C++ functions are defined with jint parameters (see lines 27 and 33). Update the function signatures to jlong or change the registration to '(I)V' to match the implementation and Kotlin side.
| JNIEXPORT void JNICALL | ||
| Java_com_vsh_source_TestSource_nativeStopCapturing(JNIEnv *env, jobject thiz, jint sourceId) | ||
| { | ||
| JniSourcesRepo::getInstance()->getSource(sourceId)->stopProducing().get(); | ||
| } |
Copilot
AI
Oct 4, 2025
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.
Possible null dereference: getSource(sourceId) may return nullptr. Retrieve the shared_ptr, validate it, and throw a Java exception if not found before dereferencing.
| } | ||
| } | ||
|
|
||
| env->DeleteLocalRef(cls); |
Copilot
AI
Oct 4, 2025
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.
Guard DeleteLocalRef with a null check to avoid invoking it on a null reference in error paths. For example: if (cls) env->DeleteLocalRef(cls);
| env->DeleteLocalRef(cls); | |
| if (cls) env->DeleteLocalRef(cls); |
| external override fun nativeGetSupportedResolutions(ptr: Long): Map<Integer, List<SourceResolution>> | ||
| external override fun nativeGetSupportedFrameFormats(ptr: Long): List<Integer> | ||
| private external fun nativeCreate(): Int | ||
| external override fun nativeRelease(srcId: Int) |
Copilot
AI
Oct 4, 2025
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 Kotlin signature for nativeOpen uses Int IDs, but the current C++ binding expects jlong pointers and casts them (see JniPullToPushSource.cpp lines 14–20). Align both sides to use jint IDs via JniSourcesRepo and update the native implementation accordingly.
| external override fun nativeRelease(srcId: Int) | |
| external override fun nativeRelease(srcId: Int) | |
| /** | |
| * Opens the native source. | |
| * | |
| * @param srcId The source ID as managed by JniSourcesRepo (not a pointer). | |
| * @param tag The tag for the source. | |
| * @param pullSrcId The pull source ID as managed by JniSourcesRepo (not a pointer). | |
| * | |
| * Note: The IDs must be Ints (jint) and are NOT native pointers. The native implementation | |
| * must use JniSourcesRepo to resolve these IDs to actual source objects. | |
| */ |
libausbc/src/main/jni/modules/sources/src/jni/JniPullToPushSource.cpp
Outdated
Show resolved
Hide resolved
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.
Pull Request Overview
Copilot reviewed 48 out of 50 changed files in this pull request and generated 6 comments.
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
| nativeOpen( | ||
| srcId = _srcId.get(), | ||
| tag = configuration.tag, | ||
| pullSrcId = configuration.pullSource.getSrcId().orElseThrow() |
Copilot
AI
Oct 4, 2025
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.
nativeOpen is declared with four parameters (srcId, tag, pullSrcId, consumerId) but is called with only three, which will not compile. Pass the consumerId as well, e.g., use configuration.consumer.getNativeObject().toInt().
| pullSrcId = configuration.pullSource.getSrcId().orElseThrow() | |
| pullSrcId = configuration.pullSource.getSrcId().orElseThrow(), | |
| consumerId = configuration.consumer.getNativeObject().toInt() |
| class CountConsumer: JniConsumer { | ||
| private var nativePtr: Long = 0 | ||
|
|
||
| override fun getNativeObject(): Long = nativePtr | ||
|
|
||
| override fun close() { | ||
| if (nativePtr != 0L) { | ||
| nativeRelease(nativePtr) | ||
| nativePtr = 0L | ||
| } | ||
| } |
Copilot
AI
Oct 4, 2025
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.
nativePtr is never initialized, so getNativeObject() returns 0 and native calls are no-ops. Initialize nativePtr (e.g., in an init block or factory) by calling nativeCreate(), and ensure it’s released in close().
| const char *native_tag = env->GetStringUTFChars(tag, nullptr); | ||
| auto openConfig = PullToPushSource::OpenConfiguration(); | ||
| openConfig.pullSource = std::dynamic_pointer_cast<PullSource>( | ||
| JniSourcesRepo::getInstance()->getSource(pullSourceId)); | ||
| openConfig.consumer = JniSourcesRepo::getInstance()->getConsumer(consumerId); | ||
| source->open(openConfig); | ||
| env->ReleaseStringUTFChars(tag, native_tag); |
Copilot
AI
Oct 4, 2025
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.
native_tag is retrieved but never used. Remove GetStringUTFChars/ReleaseStringUTFChars or assign the tag into the open configuration if needed to avoid unnecessary JNI calls.
| const char *native_tag = env->GetStringUTFChars(tag, nullptr); | |
| auto openConfig = PullToPushSource::OpenConfiguration(); | |
| openConfig.pullSource = std::dynamic_pointer_cast<PullSource>( | |
| JniSourcesRepo::getInstance()->getSource(pullSourceId)); | |
| openConfig.consumer = JniSourcesRepo::getInstance()->getConsumer(consumerId); | |
| source->open(openConfig); | |
| env->ReleaseStringUTFChars(tag, native_tag); | |
| auto openConfig = PullToPushSource::OpenConfiguration(); | |
| openConfig.pullSource = std::dynamic_pointer_cast<PullSource>( | |
| JniSourcesRepo::getInstance()->getSource(pullSourceId)); | |
| openConfig.consumer = JniSourcesRepo::getInstance()->getConsumer(consumerId); | |
| source->open(openConfig); |
| const char *native_tag = env->GetStringUTFChars(tag, nullptr); | ||
| auto openConfig = PullToPushSource::OpenConfiguration(); | ||
| openConfig.pullSource = std::dynamic_pointer_cast<PullSource>( | ||
| JniSourcesRepo::getInstance()->getSource(pullSourceId)); | ||
| openConfig.consumer = JniSourcesRepo::getInstance()->getConsumer(consumerId); | ||
| source->open(openConfig); | ||
| env->ReleaseStringUTFChars(tag, native_tag); |
Copilot
AI
Oct 4, 2025
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.
native_tag is retrieved but never used. Remove GetStringUTFChars/ReleaseStringUTFChars or assign the tag into the open configuration if needed to avoid unnecessary JNI calls.
| const char *native_tag = env->GetStringUTFChars(tag, nullptr); | |
| auto openConfig = PullToPushSource::OpenConfiguration(); | |
| openConfig.pullSource = std::dynamic_pointer_cast<PullSource>( | |
| JniSourcesRepo::getInstance()->getSource(pullSourceId)); | |
| openConfig.consumer = JniSourcesRepo::getInstance()->getConsumer(consumerId); | |
| source->open(openConfig); | |
| env->ReleaseStringUTFChars(tag, native_tag); | |
| auto openConfig = PullToPushSource::OpenConfiguration(); | |
| openConfig.pullSource = std::dynamic_pointer_cast<PullSource>( | |
| JniSourcesRepo::getInstance()->getSource(pullSourceId)); | |
| openConfig.consumer = JniSourcesRepo::getInstance()->getConsumer(consumerId); | |
| source->open(openConfig); |
| jclass cls = env->FindClass("com/vsh/source/BytePixelBufferFrame"); | ||
| if (cls != nullptr) { |
Copilot
AI
Oct 4, 2025
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.
Guard DeleteLocalRef with cls != nullptr. While many JVMs tolerate DeleteLocalRef(nullptr), it’s safer to only delete when a local reference was actually obtained.
| } | ||
|
|
||
| env->DeleteLocalRef(cls); | ||
| return result; |
Copilot
AI
Oct 4, 2025
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.
Guard DeleteLocalRef with cls != nullptr. While many JVMs tolerate DeleteLocalRef(nullptr), it’s safer to only delete when a local reference was actually obtained.
No description provided.