expo-modules-core 55.0.2 → 55.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/android/build.gradle +2 -2
  3. package/android/src/main/cpp/JSIContext.cpp +14 -2
  4. package/android/src/main/cpp/JSIContext.h +5 -14
  5. package/android/src/main/cpp/JavaScriptArrayBuffer.cpp +11 -12
  6. package/android/src/main/cpp/JavaScriptArrayBuffer.h +2 -7
  7. package/android/src/main/cpp/JavaScriptFunction.cpp +9 -13
  8. package/android/src/main/cpp/JavaScriptFunction.h +1 -7
  9. package/android/src/main/cpp/JavaScriptObject.cpp +78 -44
  10. package/android/src/main/cpp/JavaScriptObject.h +12 -17
  11. package/android/src/main/cpp/JavaScriptRuntime.cpp +1 -5
  12. package/android/src/main/cpp/JavaScriptRuntime.h +7 -1
  13. package/android/src/main/cpp/JavaScriptTypedArray.cpp +16 -18
  14. package/android/src/main/cpp/JavaScriptTypedArray.h +0 -6
  15. package/android/src/main/cpp/JavaScriptValue.cpp +48 -32
  16. package/android/src/main/cpp/JavaScriptValue.h +1 -7
  17. package/android/src/main/cpp/JavaScriptWeakObject.cpp +21 -15
  18. package/android/src/main/cpp/JavaScriptWeakObject.h +13 -10
  19. package/android/src/main/java/expo/modules/kotlin/AppContext.kt +13 -4
  20. package/android/src/main/java/expo/modules/kotlin/jni/JNIDeallocator.kt +10 -9
  21. package/android/src/main/java/expo/modules/kotlin/jni/JSIContext.kt +4 -4
  22. package/android/src/main/java/expo/modules/kotlin/jni/JavaCallback.kt +3 -3
  23. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptArrayBuffer.kt +3 -3
  24. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptFunction.kt +3 -3
  25. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptModuleObject.kt +4 -4
  26. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptObject.kt +3 -3
  27. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptValue.kt +3 -3
  28. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptWeakObject.kt +4 -3
  29. package/android/src/main/java/expo/modules/kotlin/jni/NativeArrayBuffer.kt +3 -3
  30. package/android/src/main/java/expo/modules/kotlin/jni/PromiseImpl.kt +5 -5
  31. package/android/src/main/java/expo/modules/kotlin/jni/decorators/JSDecoratorsBridgingObject.kt +4 -4
  32. package/android/src/main/java/expo/modules/kotlin/jni/worklets/Serializable.kt +3 -3
  33. package/ios/Core/Objects/ConstantDefinition.swift +9 -0
  34. package/ios/DevTools/ModuleDefinitionEncoder.swift +11 -14
  35. package/package.json +3 -3
  36. package/android/src/main/cpp/WeakRuntimeHolder.cpp +0 -24
  37. package/android/src/main/cpp/WeakRuntimeHolder.h +0 -40
package/CHANGELOG.md CHANGED
@@ -10,6 +10,18 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 55.0.4 — 2026-01-26
14
+
15
+ ### 🐛 Bug fixes
16
+
17
+ - [Android] Added missing checks in the promise implementation. ([#42467](https://github.com/expo/expo/pull/42467) by [@lukmccall](https://github.com/lukmccall))
18
+
19
+ ## 55.0.3 — 2026-01-22
20
+
21
+ ### 🐛 Bug fixes
22
+
23
+ - [Android] Fixed crashes in JavaScriptValue/JavaScriptObject destructor caused by freeing memory after the runtime is destroyed. ([#42440](https://github.com/expo/expo/pull/42440) by [@lukmccall](https://github.com/lukmccall))
24
+
13
25
  ## 55.0.2 — 2026-01-22
14
26
 
15
27
  ### 💡 Others
@@ -29,7 +29,7 @@ if (shouldIncludeCompose) {
29
29
  }
30
30
 
31
31
  group = 'host.exp.exponent'
32
- version = '55.0.2'
32
+ version = '55.0.4'
33
33
 
34
34
  def isExpoModulesCoreTests = {
35
35
  Gradle gradle = getGradle()
@@ -96,7 +96,7 @@ android {
96
96
  defaultConfig {
97
97
  consumerProguardFiles 'proguard-rules.pro'
98
98
  versionCode 1
99
- versionName "55.0.2"
99
+ versionName "55.0.4"
100
100
  buildConfigField "String", "EXPO_MODULES_CORE_VERSION", "\"${versionName}\""
101
101
  buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", "true"
102
102
 
@@ -13,13 +13,13 @@
13
13
  #include <fbjni/fbjni.h>
14
14
 
15
15
  #include <memory>
16
+ #include <shared_mutex>
16
17
 
17
18
  namespace jni = facebook::jni;
18
19
  namespace jsi = facebook::jsi;
19
20
 
20
21
  namespace expo {
21
22
 
22
-
23
23
  void JSIContext::registerNatives() {
24
24
  registerHybrid({
25
25
  makeNativeMethod("evaluateScript", JSIContext::evaluateScript),
@@ -263,14 +263,26 @@ bool JSIContext::wasDeallocated() const noexcept {
263
263
  return wasDeallocated_;
264
264
  }
265
265
 
266
- std::unordered_map<uintptr_t, JSIContext *> jsiContexts;
266
+ static std::unordered_map<uintptr_t, JSIContext *> jsiContexts;
267
+ static std::shared_mutex jsiContextsMutex;
267
268
 
268
269
  void bindJSIContext(const jsi::Runtime &runtime, JSIContext *jsiContext) {
270
+ std::unique_lock lock(jsiContextsMutex);
269
271
  jsiContexts[reinterpret_cast<uintptr_t>(&runtime)] = jsiContext;
270
272
  }
271
273
 
272
274
  void unbindJSIContext(const jsi::Runtime &runtime) {
275
+ std::unique_lock lock(jsiContextsMutex);
273
276
  jsiContexts.erase(reinterpret_cast<uintptr_t>(&runtime));
274
277
  }
275
278
 
279
+ JSIContext *getJSIContext(const jsi::Runtime &runtime) {
280
+ std::shared_lock lock(jsiContextsMutex);
281
+ const auto iterator = jsiContexts.find(reinterpret_cast<uintptr_t>(&runtime));
282
+ if (iterator == jsiContexts.end()) {
283
+ throw std::invalid_argument("JSIContext for the given runtime doesn't exist");
284
+ }
285
+ return iterator->second;
286
+ }
287
+
276
288
  } // namespace expo
@@ -167,14 +167,9 @@ private:
167
167
  ) noexcept;
168
168
  };
169
169
 
170
- /**
171
- * We are binding the JSIContext to the runtime using a thread-local map.
172
- * This is a simplification of how we're accessing the JSIContext from different places.
173
- */
174
- extern std::unordered_map<uintptr_t, JSIContext *> jsiContexts;
175
-
176
170
  /**
177
171
  * Binds the JSIContext to the runtime.
172
+ * Thread-safe: uses exclusive lock.
178
173
  * @param runtime
179
174
  * @param jsiContext
180
175
  */
@@ -182,22 +177,18 @@ void bindJSIContext(const jsi::Runtime &runtime, JSIContext *jsiContext);
182
177
 
183
178
  /**
184
179
  * Unbinds the JSIContext from the runtime.
180
+ * Thread-safe: uses exclusive lock.
185
181
  * @param runtime
186
182
  */
187
183
  void unbindJSIContext(const jsi::Runtime &runtime);
188
184
 
189
185
  /**
190
186
  * Gets the JSIContext for the given runtime.
187
+ * Thread-safe: uses exclusive lock.
191
188
  * @param runtime
192
189
  * @return JSIContext * - it should never be stored when received from this function.
193
- * It might throw an exception if the JSIContext for the given runtime doesn't exist.
190
+ * @throws std::invalid_argument if the JSIContext for the given runtime doesn't exist.
194
191
  */
195
- inline JSIContext *getJSIContext(const jsi::Runtime &runtime) {
196
- const auto iterator = jsiContexts.find(reinterpret_cast<uintptr_t>(&runtime));
197
- if (iterator == jsiContexts.end()) {
198
- throw std::invalid_argument("JSIContext for the given runtime doesn't exist");
199
- }
200
- return iterator->second;
201
- }
192
+ JSIContext *getJSIContext(const jsi::Runtime &runtime);
202
193
 
203
194
  } // namespace expo
@@ -22,14 +22,7 @@ JavaScriptArrayBuffer::JavaScriptArrayBuffer(
22
22
  std::weak_ptr<JavaScriptRuntime> runtime,
23
23
  std::shared_ptr<jsi::ArrayBuffer> jsObject
24
24
  ) : runtimeHolder(std::move(runtime)), arrayBuffer(std::move(jsObject)) {
25
- runtimeHolder.ensureRuntimeIsValid();
26
- }
27
-
28
- JavaScriptArrayBuffer::JavaScriptArrayBuffer(
29
- WeakRuntimeHolder runtime,
30
- std::shared_ptr<jsi::ArrayBuffer> jsObject
31
- ) : runtimeHolder(std::move(runtime)), arrayBuffer(std::move(jsObject)) {
32
- runtimeHolder.ensureRuntimeIsValid();
25
+ assert((!runtimeHolder.expired()) && "JS Runtime was used after deallocation");
33
26
  }
34
27
 
35
28
  jni::local_ref<JavaScriptArrayBuffer::javaobject> JavaScriptArrayBuffer::newInstance(
@@ -46,13 +39,19 @@ jni::local_ref<JavaScriptArrayBuffer::javaobject> JavaScriptArrayBuffer::newInst
46
39
  }
47
40
 
48
41
  int JavaScriptArrayBuffer::size() {
49
- jsi::Runtime &jsRuntime = runtimeHolder.getJSRuntime();
50
- return (int) arrayBuffer->size(jsRuntime);
42
+ auto runtime = runtimeHolder.lock();
43
+ assert((runtime != nullptr) && "JS Runtime was used after deallocation");
44
+ auto &rawRuntime = runtime->get();
45
+
46
+ return (int) arrayBuffer->size(rawRuntime);
51
47
  }
52
48
 
53
49
  uint8_t *JavaScriptArrayBuffer::data() {
54
- jsi::Runtime &jsRuntime = runtimeHolder.getJSRuntime();
55
- return arrayBuffer->data(jsRuntime);
50
+ auto runtime = runtimeHolder.lock();
51
+ assert((runtime != nullptr) && "JS Runtime was used after deallocation");
52
+ auto &rawRuntime = runtime->get();
53
+
54
+ return arrayBuffer->data(rawRuntime);
56
55
  }
57
56
 
58
57
  jni::local_ref<jni::JByteBuffer> JavaScriptArrayBuffer::toDirectBuffer() {
@@ -1,7 +1,7 @@
1
1
  #pragma once
2
2
 
3
- #include "WeakRuntimeHolder.h"
4
3
  #include "JNIDeallocator.h"
4
+ #include "JavaScriptRuntime.h"
5
5
 
6
6
  #include <fbjni/ByteBuffer.h>
7
7
  #include <fbjni/fbjni.h>
@@ -35,11 +35,6 @@ public:
35
35
  std::shared_ptr<jsi::ArrayBuffer> arrayBuffer
36
36
  );
37
37
 
38
- JavaScriptArrayBuffer(
39
- WeakRuntimeHolder runtime,
40
- std::shared_ptr<jsi::ArrayBuffer> arrayBuffer
41
- );
42
-
43
38
  [[nodiscard]] int size();
44
39
 
45
40
  [[nodiscard]] uint8_t* data();
@@ -54,7 +49,7 @@ public:
54
49
  }
55
50
 
56
51
  private:
57
- WeakRuntimeHolder runtimeHolder;
52
+ std::weak_ptr<JavaScriptRuntime> runtimeHolder;
58
53
  std::shared_ptr<jsi::ArrayBuffer> arrayBuffer;
59
54
  };
60
55
  } // namespace expo
@@ -18,14 +18,7 @@ JavaScriptFunction::JavaScriptFunction(
18
18
  std::weak_ptr<JavaScriptRuntime> runtime,
19
19
  std::shared_ptr<jsi::Function> jsFunction
20
20
  ) : runtimeHolder(std::move(runtime)), jsFunction(std::move(jsFunction)) {
21
- runtimeHolder.ensureRuntimeIsValid();
22
- }
23
-
24
- JavaScriptFunction::JavaScriptFunction(
25
- WeakRuntimeHolder runtime,
26
- std::shared_ptr<jsi::Function> jsFunction
27
- ) : runtimeHolder(std::move(runtime)), jsFunction(std::move(jsFunction)) {
28
- runtimeHolder.ensureRuntimeIsValid();
21
+ assert((!runtimeHolder.expired()) && "JS Runtime was used after deallocation");
29
22
  }
30
23
 
31
24
  std::shared_ptr<jsi::Function> JavaScriptFunction::get() {
@@ -37,7 +30,10 @@ jobject JavaScriptFunction::invoke(
37
30
  jni::alias_ref<jni::JArrayClass<jni::JObject>> args,
38
31
  jni::alias_ref<ExpectedType::javaobject> expectedReturnType
39
32
  ) {
40
- auto &rt = runtimeHolder.getJSRuntime();
33
+ auto runtime = runtimeHolder.lock();
34
+ assert((runtime != nullptr) && "JS Runtime was used after deallocation");
35
+ auto &rawRuntime = runtime->get();
36
+
41
37
  JNIEnv *env = jni::Environment::current();
42
38
 
43
39
  size_t size = args->size();
@@ -46,25 +42,25 @@ jobject JavaScriptFunction::invoke(
46
42
 
47
43
  for (size_t i = 0; i < size; i++) {
48
44
  jni::local_ref<jobject> arg = args->getElement(i);
49
- convertedArgs.push_back(convert(env, rt, arg));
45
+ convertedArgs.push_back(convert(env, rawRuntime, arg));
50
46
  }
51
47
 
52
48
  // TODO(@lukmccall): add better error handling
53
49
  jsi::Value result = jsThis == nullptr ?
54
50
  jsFunction->call(
55
- rt,
51
+ rawRuntime,
56
52
  (const jsi::Value *) convertedArgs.data(),
57
53
  size
58
54
  ) :
59
55
  jsFunction->callWithThis(
60
- rt,
56
+ rawRuntime,
61
57
  *(jsThis->cthis()->get()),
62
58
  (const jsi::Value *) convertedArgs.data(),
63
59
  size
64
60
  );
65
61
 
66
62
  auto converter = AnyType(jni::make_local(expectedReturnType)).converter;
67
- auto convertedResult = converter->convert(rt, env, result);
63
+ auto convertedResult = converter->convert(rawRuntime, env, result);
68
64
  return convertedResult;
69
65
  }
70
66
 
@@ -4,7 +4,6 @@
4
4
 
5
5
  #include "JSIObjectWrapper.h"
6
6
  #include "JavaScriptRuntime.h"
7
- #include "WeakRuntimeHolder.h"
8
7
  #include "types/ExpectedType.h"
9
8
 
10
9
  #include <fbjni/fbjni.h>
@@ -39,18 +38,13 @@ public:
39
38
  std::shared_ptr<jsi::Function> jsFunction
40
39
  );
41
40
 
42
- JavaScriptFunction(
43
- WeakRuntimeHolder runtime,
44
- std::shared_ptr<jsi::Function> jsFunction
45
- );
46
-
47
41
  std::shared_ptr<jsi::Function> get() override;
48
42
 
49
43
 
50
44
  private:
51
45
  friend HybridBase;
52
46
 
53
- WeakRuntimeHolder runtimeHolder;
47
+ std::weak_ptr<JavaScriptRuntime> runtimeHolder;
54
48
  std::shared_ptr<jsi::Function> jsFunction;
55
49
 
56
50
  jobject invoke(
@@ -51,14 +51,7 @@ JavaScriptObject::JavaScriptObject(
51
51
  std::weak_ptr<JavaScriptRuntime> runtime,
52
52
  std::shared_ptr<jsi::Object> jsObject
53
53
  ) : runtimeHolder(std::move(runtime)), jsObject(std::move(jsObject)) {
54
- runtimeHolder.ensureRuntimeIsValid();
55
- }
56
-
57
- JavaScriptObject::JavaScriptObject(
58
- WeakRuntimeHolder runtime,
59
- std::shared_ptr<jsi::Object> jsObject
60
- ) : runtimeHolder(std::move(runtime)), jsObject(std::move(jsObject)) {
61
- runtimeHolder.ensureRuntimeIsValid();
54
+ assert((!runtimeHolder.expired()) && "JS Runtime was used after deallocation");
62
55
  }
63
56
 
64
57
  std::shared_ptr<jsi::Object> JavaScriptObject::get() {
@@ -66,13 +59,19 @@ std::shared_ptr<jsi::Object> JavaScriptObject::get() {
66
59
  }
67
60
 
68
61
  bool JavaScriptObject::hasProperty(const std::string &name) {
69
- auto &jsRuntime = runtimeHolder.getJSRuntime();
70
- return jsObject->hasProperty(jsRuntime, name.c_str());
62
+ auto runtime = runtimeHolder.lock();
63
+ assert((runtime != nullptr) && "JS Runtime was used after deallocation");
64
+ auto &rawRuntime = runtime->get();
65
+
66
+ return jsObject->hasProperty(rawRuntime, name.c_str());
71
67
  }
72
68
 
73
69
  jsi::Value JavaScriptObject::getProperty(const std::string &name) {
74
- auto &jsRuntime = runtimeHolder.getJSRuntime();
75
- return jsObject->getProperty(jsRuntime, name.c_str());
70
+ auto runtime = runtimeHolder.lock();
71
+ assert((runtime != nullptr) && "JS Runtime was used after deallocation");
72
+ auto &rawRuntime = runtime->get();
73
+
74
+ return jsObject->getProperty(rawRuntime, name.c_str());
76
75
  }
77
76
 
78
77
  bool JavaScriptObject::jniHasProperty(jni::alias_ref<jstring> name) {
@@ -82,26 +81,32 @@ bool JavaScriptObject::jniHasProperty(jni::alias_ref<jstring> name) {
82
81
  jni::local_ref<JavaScriptValue::javaobject> JavaScriptObject::jniGetProperty(
83
82
  jni::alias_ref<jstring> name
84
83
  ) {
84
+ auto runtime = runtimeHolder.lock();
85
+ assert((runtime != nullptr) && "JS Runtime was used after deallocation");
86
+ auto &rawRuntime = runtime->get();
87
+
85
88
  auto result = std::make_shared<jsi::Value>(getProperty(name->toStdString()));
86
89
  return JavaScriptValue::newInstance(
87
- runtimeHolder.getJSIContext(),
90
+ expo::getJSIContext(rawRuntime),
88
91
  runtimeHolder,
89
92
  result
90
93
  );
91
94
  }
92
95
 
93
96
  std::vector<std::string> JavaScriptObject::getPropertyNames() {
94
- auto &jsRuntime = runtimeHolder.getJSRuntime();
97
+ auto runtime = runtimeHolder.lock();
98
+ assert((runtime != nullptr) && "JS Runtime was used after deallocation");
99
+ auto &rawRuntime = runtime->get();
95
100
 
96
- jsi::Array properties = jsObject->getPropertyNames(jsRuntime);
97
- auto size = properties.size(jsRuntime);
101
+ jsi::Array properties = jsObject->getPropertyNames(rawRuntime);
102
+ auto size = properties.size(rawRuntime);
98
103
 
99
104
  std::vector<std::string> names(size);
100
105
  for (size_t i = 0; i < size; i++) {
101
106
  auto propertyName = properties
102
- .getValueAtIndex(jsRuntime, i)
103
- .asString(jsRuntime)
104
- .utf8(jsRuntime);
107
+ .getValueAtIndex(rawRuntime, i)
108
+ .asString(rawRuntime)
109
+ .utf8(rawRuntime);
105
110
  names[i] = propertyName;
106
111
  }
107
112
 
@@ -120,33 +125,46 @@ jni::local_ref<jni::JArrayClass<jstring>> JavaScriptObject::jniGetPropertyNames(
120
125
 
121
126
  jni::local_ref<jni::HybridClass<JavaScriptWeakObject, Destructible>::javaobject>
122
127
  JavaScriptObject::createWeak() {
128
+ auto runtime = runtimeHolder.lock();
129
+ assert((runtime != nullptr) && "JS Runtime was used after deallocation");
130
+ auto &rawRuntime = runtime->get();
131
+
123
132
  return JavaScriptWeakObject::newInstance(
124
- runtimeHolder.getJSIContext(),
133
+ expo::getJSIContext(rawRuntime),
125
134
  runtimeHolder,
126
135
  get()
127
136
  );
128
137
  }
129
138
 
130
139
  jni::local_ref<JavaScriptFunction::javaobject> JavaScriptObject::jniAsFunction() {
131
- auto &jsRuntime = runtimeHolder.getJSRuntime();
132
- auto jsFuncion = std::make_shared<jsi::Function>(jsObject->asFunction(jsRuntime));
140
+ auto runtime = runtimeHolder.lock();
141
+ assert((runtime != nullptr) && "JS Runtime was used after deallocation");
142
+ auto &rawRuntime = runtime->get();
143
+
144
+ auto jsFuncion = std::make_shared<jsi::Function>(jsObject->asFunction(rawRuntime));
133
145
  return JavaScriptFunction::newInstance(
134
- runtimeHolder.getJSIContext(),
146
+ expo::getJSIContext(rawRuntime),
135
147
  runtimeHolder,
136
148
  jsFuncion
137
149
  );
138
150
  }
139
151
 
140
152
  void JavaScriptObject::setProperty(const std::string &name, jsi::Value value) {
141
- auto &jsRuntime = runtimeHolder.getJSRuntime();
142
- jsObject->setProperty(jsRuntime, name.c_str(), value);
153
+ auto runtime = runtimeHolder.lock();
154
+ assert((runtime != nullptr) && "JS Runtime was used after deallocation");
155
+ auto &rawRuntime = runtime->get();
156
+
157
+ jsObject->setProperty(rawRuntime, name.c_str(), value);
143
158
  }
144
159
 
145
160
  void JavaScriptObject::unsetProperty(jni::alias_ref<jstring> name) {
146
- auto &jsRuntime = runtimeHolder.getJSRuntime();
161
+ auto runtime = runtimeHolder.lock();
162
+ assert((runtime != nullptr) && "JS Runtime was used after deallocation");
163
+ auto &rawRuntime = runtime->get();
164
+
147
165
  auto cName = name->toStdString();
148
166
  jsObject->setProperty(
149
- jsRuntime,
167
+ rawRuntime,
150
168
  cName.c_str(),
151
169
  jsi::Value::undefined()
152
170
  );
@@ -178,11 +196,14 @@ jni::local_ref<JavaScriptObject::javaobject> JavaScriptObject::newInstance(
178
196
  void JavaScriptObject::defineNativeDeallocator(
179
197
  jni::alias_ref<JNIFunctionBody::javaobject> deallocator
180
198
  ) {
181
- auto &rt = runtimeHolder.getJSRuntime();
199
+ auto runtime = runtimeHolder.lock();
200
+ assert((runtime != nullptr) && "JS Runtime was used after deallocation");
201
+ auto &rawRuntime = runtime->get();
202
+
182
203
  jni::global_ref<JNIFunctionBody::javaobject> globalRef = jni::make_global(deallocator);
183
204
 
184
205
  common::setDeallocator(
185
- rt,
206
+ rawRuntime,
186
207
  jsObject,
187
208
  [globalRef = std::move(globalRef)]() mutable {
188
209
  auto args = jni::Environment::ensureCurrentThreadIsAttached()->NewObjectArray(
@@ -197,28 +218,36 @@ void JavaScriptObject::defineNativeDeallocator(
197
218
  }
198
219
 
199
220
  void JavaScriptObject::setExternalMemoryPressure(int size) {
200
- auto &jsRuntime = runtimeHolder.getJSRuntime();
201
- jsObject->setExternalMemoryPressure(jsRuntime, size);
221
+ auto runtime = runtimeHolder.lock();
222
+ assert((runtime != nullptr) && "JS Runtime was used after deallocation");
223
+ auto &rawRuntime = runtime->get();
224
+
225
+ jsObject->setExternalMemoryPressure(rawRuntime, size);
202
226
  }
203
227
 
204
228
  bool JavaScriptObject::isArray() {
205
- auto &jsRuntime = runtimeHolder.getJSRuntime();
206
- return jsObject->isArray(jsRuntime);
229
+ auto runtime = runtimeHolder.lock();
230
+ assert((runtime != nullptr) && "JS Runtime was used after deallocation");
231
+ auto &rawRuntime = runtime->get();
232
+
233
+ return jsObject->isArray(rawRuntime);
207
234
  }
208
235
 
209
236
  jni::local_ref<jni::JArrayClass<JavaScriptValue::javaobject>> JavaScriptObject::getArray() {
210
- auto &jsRuntime = runtimeHolder.getJSRuntime();
211
- auto moduleRegistry = runtimeHolder.getJSIContext();
237
+ auto runtime = runtimeHolder.lock();
238
+ assert((runtime != nullptr) && "JS Runtime was used after deallocation");
239
+ auto &rawRuntime = runtime->get();
240
+ auto jsiContext = expo::getJSIContext(rawRuntime);
212
241
 
213
- auto jsArray = jsObject->getArray(jsRuntime);
214
- size_t size = jsArray.size(jsRuntime);
242
+ auto jsArray = jsObject->getArray(rawRuntime);
243
+ size_t size = jsArray.size(rawRuntime);
215
244
 
216
245
  auto result = jni::JArrayClass<JavaScriptValue::javaobject>::newArray(size);
217
246
  for (size_t i = 0; i < size; i++) {
218
247
  auto element = JavaScriptValue::newInstance(
219
- moduleRegistry,
248
+ jsiContext,
220
249
  runtimeHolder,
221
- std::make_shared<jsi::Value>(jsArray.getValueAtIndex(jsRuntime, i))
250
+ std::make_shared<jsi::Value>(jsArray.getValueAtIndex(rawRuntime, i))
222
251
  );
223
252
 
224
253
  result->setElement(i, element.release());
@@ -227,18 +256,23 @@ jni::local_ref<jni::JArrayClass<JavaScriptValue::javaobject>> JavaScriptObject::
227
256
  }
228
257
 
229
258
  bool JavaScriptObject::isArrayBuffer() {
230
- auto &jsRuntime = runtimeHolder.getJSRuntime();
231
- return jsObject->isArrayBuffer(jsRuntime);
259
+ auto runtime = runtimeHolder.lock();
260
+ assert((runtime != nullptr) && "JS Runtime was used after deallocation");
261
+ auto &rawRuntime = runtime->get();
262
+
263
+ return jsObject->isArrayBuffer(rawRuntime);
232
264
  }
233
265
 
234
266
  jni::local_ref<JavaScriptArrayBuffer::javaobject> JavaScriptObject::getArrayBuffer() {
235
- auto &jsRuntime = runtimeHolder.getJSRuntime();
236
- auto jsiContext = runtimeHolder.getJSIContext();
267
+ auto runtime = runtimeHolder.lock();
268
+ assert((runtime != nullptr) && "JS Runtime was used after deallocation");
269
+ auto &rawRuntime = runtime->get();
270
+ auto jsiContext = expo::getJSIContext(rawRuntime);
237
271
 
238
272
  return JavaScriptArrayBuffer::newInstance(
239
273
  jsiContext,
240
274
  runtimeHolder,
241
- std::make_shared<jsi::ArrayBuffer>(jsObject->getArrayBuffer(jsRuntime))
275
+ std::make_shared<jsi::ArrayBuffer>(jsObject->getArrayBuffer(rawRuntime))
242
276
  );
243
277
  }
244
278
 
@@ -5,7 +5,6 @@
5
5
  #include "JSIObjectWrapper.h"
6
6
  #include "JSITypeConverter.h"
7
7
  #include "JavaScriptRuntime.h"
8
- #include "WeakRuntimeHolder.h"
9
8
  #include "JNIFunctionBody.h"
10
9
  #include "JNIDeallocator.h"
11
10
  #include "JSIUtils.h"
@@ -47,13 +46,6 @@ public:
47
46
  std::shared_ptr<jsi::Object> jsObject
48
47
  );
49
48
 
50
- JavaScriptObject(
51
- WeakRuntimeHolder runtime,
52
- std::shared_ptr<jsi::Object> jsObject
53
- );
54
-
55
- virtual ~JavaScriptObject() = default;
56
-
57
49
  std::shared_ptr<jsi::Object> get() override;
58
50
 
59
51
  /**
@@ -92,9 +84,8 @@ public:
92
84
  [[nodiscard]] jni::local_ref<jni::HybridClass<JavaScriptArrayBuffer, Destructible>::javaobject> getArrayBuffer();
93
85
 
94
86
  protected:
95
- WeakRuntimeHolder runtimeHolder;
87
+ std::weak_ptr<JavaScriptRuntime> runtimeHolder;
96
88
  std::shared_ptr<jsi::Object> jsObject;
97
-
98
89
  private:
99
90
  friend HybridBase;
100
91
 
@@ -130,13 +121,15 @@ private:
130
121
  typename = std::enable_if_t<is_jsi_type_converter_defined<T>>
131
122
  >
132
123
  void setProperty(jni::alias_ref<jstring> name, T value) {
133
- jsi::Runtime &jsRuntime = runtimeHolder.getJSRuntime();
124
+ auto runtime = runtimeHolder.lock();
125
+ assert((runtime != nullptr) && "JS Runtime was used after deallocation");
126
+ auto &rawRuntime = runtime->get();
134
127
 
135
128
  auto cName = name->toStdString();
136
129
  jsObject->setProperty(
137
- jsRuntime,
130
+ rawRuntime,
138
131
  cName.c_str(),
139
- jsi_type_converter<T>::convert(jsRuntime, value)
132
+ jsi_type_converter<T>::convert(rawRuntime, value)
140
133
  );
141
134
  }
142
135
 
@@ -145,12 +138,14 @@ private:
145
138
  typename = std::enable_if_t<is_jsi_type_converter_defined<T>>
146
139
  >
147
140
  void defineProperty(jni::alias_ref<jstring> name, T value, int options) {
148
- jsi::Runtime &jsRuntime = runtimeHolder.getJSRuntime();
141
+ auto runtime = runtimeHolder.lock();
142
+ assert((runtime != nullptr) && "JS Runtime was used after deallocation");
143
+ auto &rawRuntime = runtime->get();
149
144
 
150
145
  auto cName = name->toStdString();
151
- jsi::Object descriptor = preparePropertyDescriptor(jsRuntime, options);
152
- descriptor.setProperty(jsRuntime, "value", jsi_type_converter<T>::convert(jsRuntime, value));
153
- common::defineProperty(jsRuntime, jsObject.get(), cName.c_str(), std::move(descriptor));
146
+ jsi::Object descriptor = preparePropertyDescriptor(rawRuntime, options);
147
+ descriptor.setProperty(rawRuntime, "value", jsi_type_converter<T>::convert(rawRuntime, value));
148
+ common::defineProperty(rawRuntime, jsObject.get(), cName.c_str(), std::move(descriptor));
154
149
  }
155
150
  };
156
151
  } // namespace expo
@@ -14,11 +14,7 @@ namespace expo {
14
14
  JavaScriptRuntime::JavaScriptRuntime(
15
15
  jsi::Runtime *runtime,
16
16
  std::shared_ptr<react::CallInvoker> jsInvoker
17
- ) : jsInvoker(std::move(jsInvoker)) {
18
- // Creating a shared pointer that points to the runtime but doesn't own it, thus doesn't release it.
19
- // In this code flow, the runtime should be owned by something else like the CatalystInstance.
20
- // See explanation for constructor (8): https://en.cppreference.com/w/cpp/memory/shared_ptr/shared_ptr
21
- this->runtime = std::shared_ptr<jsi::Runtime>(std::shared_ptr<jsi::Runtime>(), runtime);
17
+ ) : runtime(runtime), jsInvoker(std::move(jsInvoker)) {
22
18
  }
23
19
 
24
20
  jsi::Runtime &JavaScriptRuntime::get() const noexcept {
@@ -76,6 +76,12 @@ public:
76
76
  std::shared_ptr<react::CallInvoker> jsInvoker;
77
77
 
78
78
  private:
79
- std::shared_ptr<jsi::Runtime> runtime;
79
+ /**
80
+ * Raw pointer to the runtime. We do not own this - it's managed by React Native.
81
+ * The runtime's lifetime is guaranteed to exceed JavaScriptRuntime's lifetime,
82
+ * as JSIContext::prepareForDeallocation() invalidates all weak references before
83
+ * the runtime is deallocated.
84
+ */
85
+ jsi::Runtime *runtime;
80
86
  };
81
87
  } // namespace expo
@@ -5,24 +5,17 @@
5
5
 
6
6
  namespace expo {
7
7
 
8
- JavaScriptTypedArray::JavaScriptTypedArray(
9
- WeakRuntimeHolder runtime,
10
- std::shared_ptr<jsi::Object> jsObject
11
- ) : jni::HybridClass<JavaScriptTypedArray, JavaScriptObject>(std::move(runtime),
12
- std::move(jsObject)) {
13
- jsi::Runtime &jsRuntime = runtimeHolder.getJSRuntime();
14
- typedArrayWrapper = std::make_shared<expo::TypedArray>(jsRuntime, *get());
15
- rawPointer = static_cast<char *>(typedArrayWrapper->getRawPointer(jsRuntime));
16
- }
17
-
18
8
  JavaScriptTypedArray::JavaScriptTypedArray(
19
9
  std::weak_ptr<JavaScriptRuntime> runtime,
20
10
  std::shared_ptr<jsi::Object> jsObject
21
11
  ) : jni::HybridClass<JavaScriptTypedArray, JavaScriptObject>(std::move(runtime),
22
12
  std::move(jsObject)) {
23
- jsi::Runtime &jsRuntime = runtimeHolder.getJSRuntime();
24
- typedArrayWrapper = std::make_shared<expo::TypedArray>(jsRuntime, *get());
25
- rawPointer = static_cast<char *>(typedArrayWrapper->getRawPointer(jsRuntime));
13
+ auto jsRuntime = runtimeHolder.lock();
14
+ assert((jsRuntime != nullptr) && "JS Runtime was used after deallocation");
15
+ auto &rawRuntime = jsRuntime->get();
16
+
17
+ typedArrayWrapper = std::make_shared<expo::TypedArray>(rawRuntime, *JavaScriptObject::get());
18
+ rawPointer = static_cast<char *>(typedArrayWrapper->getRawPointer(rawRuntime));
26
19
  }
27
20
 
28
21
  void JavaScriptTypedArray::registerNatives() {
@@ -49,17 +42,22 @@ void JavaScriptTypedArray::registerNatives() {
49
42
  }
50
43
 
51
44
  int JavaScriptTypedArray::getRawKind() {
52
- jsi::Runtime &jsRuntime = runtimeHolder.getJSRuntime();
53
- return (int) typedArrayWrapper->getKind(jsRuntime);
45
+ auto runtime = runtimeHolder.lock();
46
+ assert((runtime != nullptr) && "JS Runtime was used after deallocation");
47
+ auto &rawRuntime = runtime->get();
48
+
49
+ return (int) typedArrayWrapper->getKind(rawRuntime);
54
50
  }
55
51
 
56
52
  jni::local_ref<jni::JByteBuffer> JavaScriptTypedArray::toDirectBuffer() {
57
- jsi::Runtime &jsRuntime = runtimeHolder.getJSRuntime();
53
+ auto runtime = runtimeHolder.lock();
54
+ assert((runtime != nullptr) && "JS Runtime was used after deallocation");
55
+ auto &rawRuntime = runtime->get();
58
56
 
59
- auto byteLength = typedArrayWrapper->byteLength(jsRuntime);
57
+ auto byteLength = typedArrayWrapper->byteLength(rawRuntime);
60
58
 
61
59
  auto byteBuffer = jni::JByteBuffer::wrapBytes(
62
- static_cast<uint8_t *>(typedArrayWrapper->getRawPointer(jsRuntime)),
60
+ static_cast<uint8_t *>(typedArrayWrapper->getRawPointer(rawRuntime)),
63
61
  byteLength
64
62
  );
65
63