@@ -33,17 +33,44 @@ struct SandboxJSIState {
3333static std::mutex gRegistryMutex ;
3434static std::unordered_map<jlong, std::shared_ptr<SandboxJSIState>> gStates ;
3535
36+ static JNIEnv* getJNIEnv () {
37+ JNIEnv* env = nullptr ;
38+ if (gJavaVM ) {
39+ gJavaVM ->GetEnv (reinterpret_cast <void **>(&env), JNI_VERSION_1_6);
40+ if (!env) {
41+ gJavaVM ->AttachCurrentThread (&env, nullptr );
42+ }
43+ }
44+ return env;
45+ }
46+
3647/* *
3748 * ISandboxDelegate that dispatches postMessage through the Kotlin delegate
3849 * via JNI. The Kotlin postMessage uses runOnJSQueueThread to safely access
3950 * the JSI runtime on the correct thread.
51+ *
52+ * Holds its own JNI global reference which must be released via invalidate().
4053 */
4154class JNISandboxDelegate : public rnsandbox ::ISandboxDelegate {
4255 public:
43- JNISandboxDelegate (jobject globalDelegateRef)
44- : globalDelegateRef_(globalDelegateRef) {}
56+ explicit JNISandboxDelegate (JNIEnv* env, jobject delegateRef)
57+ : globalDelegateRef_(env->NewGlobalRef (delegateRef)) {}
58+
59+ ~JNISandboxDelegate () override { invalidate (); }
60+
61+ void invalidate () {
62+ std::lock_guard<std::mutex> lock (mutex_);
63+ if (globalDelegateRef_) {
64+ JNIEnv* env = getJNIEnv ();
65+ if (env) {
66+ env->DeleteGlobalRef (globalDelegateRef_);
67+ }
68+ globalDelegateRef_ = nullptr ;
69+ }
70+ }
4571
4672 void postMessage (const std::string& message) override {
73+ std::lock_guard<std::mutex> lock (mutex_);
4774 JNIEnv* env = getJNIEnv ();
4875 if (!env || !globalDelegateRef_)
4976 return ;
@@ -74,30 +101,9 @@ class JNISandboxDelegate : public rnsandbox::ISandboxDelegate {
74101
75102 private:
76103 jobject globalDelegateRef_;
77-
78- static JNIEnv* getJNIEnv () {
79- JNIEnv* env = nullptr ;
80- if (gJavaVM ) {
81- gJavaVM ->GetEnv (reinterpret_cast <void **>(&env), JNI_VERSION_1_6);
82- if (!env) {
83- gJavaVM ->AttachCurrentThread (&env, nullptr );
84- }
85- }
86- return env;
87- }
104+ std::mutex mutex_;
88105};
89106
90- static JNIEnv* getJNIEnv () {
91- JNIEnv* env = nullptr ;
92- if (gJavaVM ) {
93- gJavaVM ->GetEnv (reinterpret_cast <void **>(&env), JNI_VERSION_1_6);
94- if (!env) {
95- gJavaVM ->AttachCurrentThread (&env, nullptr );
96- }
97- }
98- return env;
99- }
100-
101107static std::string safeGetStringProperty (
102108 jsi::Runtime& rt,
103109 const jsi::Object& obj,
@@ -124,7 +130,7 @@ stubJsiFunction(jsi::Runtime& runtime, jsi::Object& object, const char* name) {
124130
125131static void setupErrorHandler (
126132 jsi::Runtime& runtime,
127- jobject globalDelegateRef ) {
133+ std::weak_ptr<SandboxJSIState> stateWeak ) {
128134 jsi::Object global = runtime.global ();
129135 jsi::Value errorUtilsVal = global.getProperty (runtime, " ErrorUtils" );
130136 if (!errorUtilsVal.isObject ())
@@ -142,23 +148,27 @@ static void setupErrorHandler(
142148 runtime,
143149 jsi::PropNameID::forAscii (runtime, " sandboxGlobalErrorHandler" ),
144150 2 ,
145- [globalDelegateRef , originalHandler = std::move (originalHandler)](
151+ [stateWeak , originalHandler = std::move (originalHandler)](
146152 jsi::Runtime& rt,
147153 const jsi::Value&,
148154 const jsi::Value* args,
149155 size_t count) -> jsi::Value {
150156 if (count < 2 )
151157 return jsi::Value::undefined ();
152158
159+ auto state = stateWeak.lock ();
160+ if (!state || !state->delegateRef )
161+ return jsi::Value::undefined ();
162+
153163 JNIEnv* jniEnv = getJNIEnv ();
154164 if (!jniEnv)
155165 return jsi::Value::undefined ();
156166
157- jclass cls = jniEnv->GetObjectClass (globalDelegateRef );
167+ jclass cls = jniEnv->GetObjectClass (state-> delegateRef );
158168 jfieldID hasHandlerField =
159169 jniEnv->GetFieldID (cls, " hasOnErrorHandler" , " Z" );
160170 jboolean hasHandler =
161- jniEnv->GetBooleanField (globalDelegateRef , hasHandlerField);
171+ jniEnv->GetBooleanField (state-> delegateRef , hasHandlerField);
162172
163173 if (hasHandler) {
164174 const jsi::Object& error = args[0 ].asObject (rt);
@@ -175,7 +185,7 @@ static void setupErrorHandler(
175185 jstring jMsg = jniEnv->NewStringUTF (message.c_str ());
176186 jstring jStack = jniEnv->NewStringUTF (stack.c_str ());
177187 jniEnv->CallVoidMethod (
178- globalDelegateRef ,
188+ state-> delegateRef ,
179189 emitMethod,
180190 jName,
181191 jMsg,
@@ -223,7 +233,7 @@ jlong installSandboxJSIBindings(
223233 runtime,
224234 jsi::PropNameID::forAscii (runtime, " postMessage" ),
225235 2 ,
226- [globalDelegateRef ](
236+ [stateWeak = std::weak_ptr<SandboxJSIState>(state) ](
227237 jsi::Runtime& rt,
228238 const jsi::Value&,
229239 const jsi::Value* args,
@@ -238,6 +248,10 @@ jlong installSandboxJSIBindings(
238248 rt, " postMessage: first argument must be an object" );
239249 }
240250
251+ auto statePtr = stateWeak.lock ();
252+ if (!statePtr || !statePtr->delegateRef )
253+ return jsi::Value::undefined ();
254+
241255 jsi::Object jsonObj = rt.global ().getPropertyAsObject (rt, " JSON" );
242256 jsi::Function stringify =
243257 jsonObj.getPropertyAsFunction (rt, " stringify" );
@@ -255,7 +269,6 @@ jlong installSandboxJSIBindings(
255269 }
256270 std::string targetOrigin = args[1 ].getString (rt).utf8 (rt);
257271
258- // Fan out to all delegates registered for the target origin
259272 auto & registry = rnsandbox::SandboxRegistry::getInstance ();
260273 auto targets = registry.findAll (targetOrigin);
261274 if (!targets.empty ()) {
@@ -266,11 +279,11 @@ jlong installSandboxJSIBindings(
266279 LOGW (" postMessage: target '%s' not found" , targetOrigin.c_str ());
267280 }
268281 } else {
269- jclass cls = jniEnv->GetObjectClass (globalDelegateRef );
282+ jclass cls = jniEnv->GetObjectClass (statePtr-> delegateRef );
270283 jmethodID mid = jniEnv->GetMethodID (
271284 cls, " emitOnMessageFromJS" , " (Ljava/lang/String;)V" );
272285 jstring jMsg = jniEnv->NewStringUTF (messageJson.c_str ());
273- jniEnv->CallVoidMethod (globalDelegateRef , mid, jMsg);
286+ jniEnv->CallVoidMethod (statePtr-> delegateRef , mid, jMsg);
274287 jniEnv->DeleteLocalRef (jMsg);
275288 jniEnv->DeleteLocalRef (cls);
276289 }
@@ -356,7 +369,7 @@ jlong installSandboxJSIBindings(
356369 makePropDesc (std::move (setOnMessageFn)));
357370
358371 try {
359- setupErrorHandler (runtime, globalDelegateRef );
372+ setupErrorHandler (runtime, std::weak_ptr<SandboxJSIState>(state) );
360373 } catch (const std::exception& e) {
361374 LOGW (" Failed to setup error handler: %s" , e.what ());
362375 }
@@ -379,7 +392,7 @@ jlong installSandboxJSIBindings(
379392 if (!origin.empty ()) {
380393 state->origin = origin;
381394 auto delegate =
382- std::make_shared<JNISandboxDelegate>(globalDelegateRef);
395+ std::make_shared<JNISandboxDelegate>(jniEnv, globalDelegateRef);
383396 state->registryDelegate = delegate;
384397 rnsandbox::SandboxRegistry::getInstance ().registerSandbox (
385398 origin, delegate, std::set<std::string>());
@@ -481,7 +494,7 @@ Java_io_callstack_rnsandbox_SandboxJSIInstaller_nativeInstallErrorHandler(
481494 return ;
482495
483496 try {
484- setupErrorHandler (*state->runtime , state-> delegateRef );
497+ setupErrorHandler (*state->runtime , std::weak_ptr<SandboxJSIState>(state) );
485498 } catch (const std::exception& e) {
486499 LOGW (" Failed to setup error handler post-bundle: %s" , e.what ());
487500 }
@@ -497,19 +510,28 @@ Java_io_callstack_rnsandbox_SandboxJSIInstaller_nativeDestroy(
497510 if (it != gStates .end ()) {
498511 std::string origin;
499512 std::shared_ptr<rnsandbox::ISandboxDelegate> delegate;
513+ jobject delegateRef = nullptr ;
500514 {
501515 std::lock_guard<std::mutex> stateLock (it->second ->mutex );
502516 origin = it->second ->origin ;
503517 delegate = it->second ->registryDelegate ;
518+ delegateRef = it->second ->delegateRef ;
504519 it->second ->onMessageCallback .reset ();
505520 it->second ->pendingMessages .clear ();
506521 it->second ->runtime = nullptr ;
507522 it->second ->registryDelegate .reset ();
523+ it->second ->delegateRef = nullptr ;
508524 }
509525 if (!origin.empty () && delegate) {
510526 rnsandbox::SandboxRegistry::getInstance ().unregisterDelegate (
511527 origin, delegate);
512528 }
529+ if (delegateRef) {
530+ JNIEnv* env = getJNIEnv ();
531+ if (env) {
532+ env->DeleteGlobalRef (delegateRef);
533+ }
534+ }
513535 gStates .erase (it);
514536 }
515537}
0 commit comments