@@ -33,17 +33,46 @@ 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 {
60+ invalidate ();
61+ }
62+
63+ void invalidate () {
64+ std::lock_guard<std::mutex> lock (mutex_);
65+ if (globalDelegateRef_) {
66+ JNIEnv* env = getJNIEnv ();
67+ if (env) {
68+ env->DeleteGlobalRef (globalDelegateRef_);
69+ }
70+ globalDelegateRef_ = nullptr ;
71+ }
72+ }
4573
4674 void postMessage (const std::string& message) override {
75+ std::lock_guard<std::mutex> lock (mutex_);
4776 JNIEnv* env = getJNIEnv ();
4877 if (!env || !globalDelegateRef_)
4978 return ;
@@ -74,30 +103,9 @@ class JNISandboxDelegate : public rnsandbox::ISandboxDelegate {
74103
75104 private:
76105 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- }
106+ std::mutex mutex_;
88107};
89108
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-
101109static std::string safeGetStringProperty (
102110 jsi::Runtime& rt,
103111 const jsi::Object& obj,
@@ -124,7 +132,7 @@ stubJsiFunction(jsi::Runtime& runtime, jsi::Object& object, const char* name) {
124132
125133static void setupErrorHandler (
126134 jsi::Runtime& runtime,
127- jobject globalDelegateRef ) {
135+ std::weak_ptr<SandboxJSIState> stateWeak ) {
128136 jsi::Object global = runtime.global ();
129137 jsi::Value errorUtilsVal = global.getProperty (runtime, " ErrorUtils" );
130138 if (!errorUtilsVal.isObject ())
@@ -142,23 +150,27 @@ static void setupErrorHandler(
142150 runtime,
143151 jsi::PropNameID::forAscii (runtime, " sandboxGlobalErrorHandler" ),
144152 2 ,
145- [globalDelegateRef , originalHandler = std::move (originalHandler)](
153+ [stateWeak , originalHandler = std::move (originalHandler)](
146154 jsi::Runtime& rt,
147155 const jsi::Value&,
148156 const jsi::Value* args,
149157 size_t count) -> jsi::Value {
150158 if (count < 2 )
151159 return jsi::Value::undefined ();
152160
161+ auto state = stateWeak.lock ();
162+ if (!state || !state->delegateRef )
163+ return jsi::Value::undefined ();
164+
153165 JNIEnv* jniEnv = getJNIEnv ();
154166 if (!jniEnv)
155167 return jsi::Value::undefined ();
156168
157- jclass cls = jniEnv->GetObjectClass (globalDelegateRef );
169+ jclass cls = jniEnv->GetObjectClass (state-> delegateRef );
158170 jfieldID hasHandlerField =
159171 jniEnv->GetFieldID (cls, " hasOnErrorHandler" , " Z" );
160172 jboolean hasHandler =
161- jniEnv->GetBooleanField (globalDelegateRef , hasHandlerField);
173+ jniEnv->GetBooleanField (state-> delegateRef , hasHandlerField);
162174
163175 if (hasHandler) {
164176 const jsi::Object& error = args[0 ].asObject (rt);
@@ -175,7 +187,7 @@ static void setupErrorHandler(
175187 jstring jMsg = jniEnv->NewStringUTF (message.c_str ());
176188 jstring jStack = jniEnv->NewStringUTF (stack.c_str ());
177189 jniEnv->CallVoidMethod (
178- globalDelegateRef ,
190+ state-> delegateRef ,
179191 emitMethod,
180192 jName,
181193 jMsg,
@@ -223,7 +235,7 @@ jlong installSandboxJSIBindings(
223235 runtime,
224236 jsi::PropNameID::forAscii (runtime, " postMessage" ),
225237 2 ,
226- [globalDelegateRef ](
238+ [stateWeak = std::weak_ptr<SandboxJSIState>(state) ](
227239 jsi::Runtime& rt,
228240 const jsi::Value&,
229241 const jsi::Value* args,
@@ -238,6 +250,10 @@ jlong installSandboxJSIBindings(
238250 rt, " postMessage: first argument must be an object" );
239251 }
240252
253+ auto statePtr = stateWeak.lock ();
254+ if (!statePtr || !statePtr->delegateRef )
255+ return jsi::Value::undefined ();
256+
241257 jsi::Object jsonObj = rt.global ().getPropertyAsObject (rt, " JSON" );
242258 jsi::Function stringify =
243259 jsonObj.getPropertyAsFunction (rt, " stringify" );
@@ -255,7 +271,6 @@ jlong installSandboxJSIBindings(
255271 }
256272 std::string targetOrigin = args[1 ].getString (rt).utf8 (rt);
257273
258- // Fan out to all delegates registered for the target origin
259274 auto & registry = rnsandbox::SandboxRegistry::getInstance ();
260275 auto targets = registry.findAll (targetOrigin);
261276 if (!targets.empty ()) {
@@ -266,11 +281,11 @@ jlong installSandboxJSIBindings(
266281 LOGW (" postMessage: target '%s' not found" , targetOrigin.c_str ());
267282 }
268283 } else {
269- jclass cls = jniEnv->GetObjectClass (globalDelegateRef );
284+ jclass cls = jniEnv->GetObjectClass (statePtr-> delegateRef );
270285 jmethodID mid = jniEnv->GetMethodID (
271286 cls, " emitOnMessageFromJS" , " (Ljava/lang/String;)V" );
272287 jstring jMsg = jniEnv->NewStringUTF (messageJson.c_str ());
273- jniEnv->CallVoidMethod (globalDelegateRef , mid, jMsg);
288+ jniEnv->CallVoidMethod (statePtr-> delegateRef , mid, jMsg);
274289 jniEnv->DeleteLocalRef (jMsg);
275290 jniEnv->DeleteLocalRef (cls);
276291 }
@@ -356,7 +371,7 @@ jlong installSandboxJSIBindings(
356371 makePropDesc (std::move (setOnMessageFn)));
357372
358373 try {
359- setupErrorHandler (runtime, globalDelegateRef );
374+ setupErrorHandler (runtime, std::weak_ptr<SandboxJSIState>(state) );
360375 } catch (const std::exception& e) {
361376 LOGW (" Failed to setup error handler: %s" , e.what ());
362377 }
@@ -379,7 +394,7 @@ jlong installSandboxJSIBindings(
379394 if (!origin.empty ()) {
380395 state->origin = origin;
381396 auto delegate =
382- std::make_shared<JNISandboxDelegate>(globalDelegateRef);
397+ std::make_shared<JNISandboxDelegate>(jniEnv, globalDelegateRef);
383398 state->registryDelegate = delegate;
384399 rnsandbox::SandboxRegistry::getInstance ().registerSandbox (
385400 origin, delegate, std::set<std::string>());
@@ -481,7 +496,7 @@ Java_io_callstack_rnsandbox_SandboxJSIInstaller_nativeInstallErrorHandler(
481496 return ;
482497
483498 try {
484- setupErrorHandler (*state->runtime , state-> delegateRef );
499+ setupErrorHandler (*state->runtime , std::weak_ptr<SandboxJSIState>(state) );
485500 } catch (const std::exception& e) {
486501 LOGW (" Failed to setup error handler post-bundle: %s" , e.what ());
487502 }
@@ -497,19 +512,28 @@ Java_io_callstack_rnsandbox_SandboxJSIInstaller_nativeDestroy(
497512 if (it != gStates .end ()) {
498513 std::string origin;
499514 std::shared_ptr<rnsandbox::ISandboxDelegate> delegate;
515+ jobject delegateRef = nullptr ;
500516 {
501517 std::lock_guard<std::mutex> stateLock (it->second ->mutex );
502518 origin = it->second ->origin ;
503519 delegate = it->second ->registryDelegate ;
520+ delegateRef = it->second ->delegateRef ;
504521 it->second ->onMessageCallback .reset ();
505522 it->second ->pendingMessages .clear ();
506523 it->second ->runtime = nullptr ;
507524 it->second ->registryDelegate .reset ();
525+ it->second ->delegateRef = nullptr ;
508526 }
509527 if (!origin.empty () && delegate) {
510528 rnsandbox::SandboxRegistry::getInstance ().unregisterDelegate (
511529 origin, delegate);
512530 }
531+ if (delegateRef) {
532+ JNIEnv* env = getJNIEnv ();
533+ if (env) {
534+ env->DeleteGlobalRef (delegateRef);
535+ }
536+ }
513537 gStates .erase (it);
514538 }
515539}
0 commit comments