expo-modules-core 0.12.0 → 0.13.1

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 (69) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/android/CMakeLists.txt +1 -1
  3. package/android/build.gradle +54 -24
  4. package/android/proguard-rules.pro +7 -0
  5. package/android/src/main/cpp/Exceptions.cpp +49 -16
  6. package/android/src/main/cpp/Exceptions.h +17 -1
  7. package/android/src/main/cpp/ExpoModulesHostObject.cpp +8 -0
  8. package/android/src/main/cpp/ExpoModulesHostObject.h +2 -0
  9. package/android/src/main/cpp/JNIFunctionBody.cpp +8 -2
  10. package/android/src/main/cpp/JSReferencesCache.cpp +0 -10
  11. package/android/src/main/cpp/JSReferencesCache.h +1 -2
  12. package/android/src/main/cpp/JavaScriptModuleObject.cpp +23 -0
  13. package/android/src/main/cpp/JavaScriptModuleObject.h +11 -2
  14. package/android/src/main/cpp/JavaScriptValue.cpp +1 -1
  15. package/android/src/main/cpp/MethodMetadata.cpp +96 -22
  16. package/android/src/main/cpp/MethodMetadata.h +5 -0
  17. package/android/src/main/cpp/types/FrontendConverter.cpp +2 -2
  18. package/android/src/main/java/expo/modules/adapters/react/ModuleRegistryAdapter.java +2 -1
  19. package/android/src/main/java/expo/modules/adapters/react/NativeModulesProxy.java +4 -0
  20. package/android/src/main/java/expo/modules/interfaces/barcodescanner/BarCodeScannerResult.java +52 -0
  21. package/android/src/main/java/expo/modules/interfaces/filesystem/AppDirectoriesModuleInterface.kt +8 -0
  22. package/android/src/main/java/expo/modules/kotlin/AppContext.kt +33 -13
  23. package/android/src/main/java/expo/modules/kotlin/KotlinInteropModuleRegistry.kt +0 -7
  24. package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultRegistry.kt +1 -1
  25. package/android/src/main/java/expo/modules/kotlin/jni/JSIInteropModuleRegistry.kt +2 -1
  26. package/android/src/main/java/expo/modules/kotlin/providers/CurrentActivityProvider.kt +3 -4
  27. package/android/src/main/java/expo/modules/kotlin/types/EnumTypeConverter.kt +8 -0
  28. package/android/src/main/java/expo/modules/kotlin/types/Enumerable.kt +9 -0
  29. package/android/src/main/java/expo/modules/kotlin/{callbacks/ViewCallback.kt → viewevent/ViewEvent.kt} +7 -3
  30. package/android/src/main/java/expo/modules/kotlin/{callbacks/ViewCallbackDelegate.kt → viewevent/ViewEventDelegate.kt} +13 -6
  31. package/android/src/main/java/expo/modules/kotlin/views/ViewDefinitionBuilder.kt +17 -6
  32. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinition.kt +2 -1
  33. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerWrapperDelegate.kt +3 -2
  34. package/build/EventEmitter.d.ts +1 -0
  35. package/build/EventEmitter.d.ts.map +1 -1
  36. package/build/EventEmitter.js +9 -1
  37. package/build/EventEmitter.js.map +1 -1
  38. package/ios/Fabric/ExpoFabricView.swift +10 -0
  39. package/ios/Fabric/ExpoFabricViewObjC.h +2 -0
  40. package/ios/Fabric/ExpoFabricViewObjC.mm +6 -0
  41. package/ios/JSI/EXJSIUtils.h +4 -0
  42. package/ios/JSI/EXJSIUtils.mm +21 -4
  43. package/ios/Swift/Arguments/{ConvertibleArgument.swift → Convertible.swift} +4 -1
  44. package/ios/Swift/Arguments/Convertibles.swift +6 -6
  45. package/ios/Swift/Arguments/{EnumArgument.swift → Enumerable.swift} +7 -4
  46. package/ios/Swift/Conversions.swift +12 -3
  47. package/ios/Swift/Convertibles/Convertibles+Color.swift +2 -2
  48. package/ios/Swift/Convertibles/Either.swift +1 -1
  49. package/ios/Swift/DynamicTypes/AnyDynamicType.swift +1 -1
  50. package/ios/Swift/DynamicTypes/DynamicConvertibleType.swift +2 -2
  51. package/ios/Swift/DynamicTypes/DynamicEnumType.swift +2 -2
  52. package/ios/Swift/DynamicTypes/DynamicType.swift +2 -2
  53. package/ios/Swift/Events/EventDispatcher.swift +65 -8
  54. package/ios/Swift/Exceptions/CodedError.swift +13 -8
  55. package/ios/Swift/Exceptions/Exception.swift +17 -4
  56. package/ios/Swift/Modules/ModuleDefinition.swift +9 -0
  57. package/ios/Swift/Promise.swift +1 -1
  58. package/ios/Swift/Records/Record.swift +2 -2
  59. package/ios/Swift/Views/ViewDefinition.swift +8 -0
  60. package/ios/Swift/Views/ViewLifecycleMethod.swift +48 -0
  61. package/ios/Swift/Views/ViewManagerDefinition.swift +17 -0
  62. package/ios/Swift/Views/ViewManagerDefinitionComponents.swift +11 -0
  63. package/ios/Swift/Views/ViewModuleWrapper.swift +4 -2
  64. package/ios/Tests/DynamicTypeSpec.swift +2 -2
  65. package/ios/Tests/{EnumArgumentSpec.swift → EnumerableSpec.swift} +2 -2
  66. package/package.json +3 -3
  67. package/src/EventEmitter.ts +19 -1
  68. package/android/src/main/java/expo/modules/kotlin/callbacks/Callback.kt +0 -5
  69. package/ios/Swift/Events/Event.swift +0 -43
package/CHANGELOG.md CHANGED
@@ -10,6 +10,36 @@
10
10
 
11
11
  ### 💡 Others
12
12
 
13
+ ## 0.13.1 — 2022-10-27
14
+
15
+ ### 🐛 Bug fixes
16
+
17
+ - Fixed `~CallbackWrapper()` dangling pointer crashes when reloading the app on Android. ([#19699](https://github.com/expo/expo/pull/19699) by [@kudo](https://github.com/kudo), [@kudo](https://github.com/kudo))
18
+
19
+ ## 0.13.0 — 2022-10-25
20
+
21
+ ### 🛠 Breaking changes
22
+
23
+ - Convertible enums must inherit from `expo.modules.kotlin.types.Enumerable` on Android. ([#19551](https://github.com/expo/expo/pull/19551) by [@lukmccall](https://github.com/lukmccall))
24
+ - `AppContext.currentActivity` is not longer returning `AppCompatActivity`, but an instance of `android.app.Activity` class. ([#19573](https://github.com/expo/expo/pull/19573) by [@lukmccall](https://github.com/lukmccall))
25
+
26
+ ### ⚠️ Notices
27
+
28
+ - Deprecated `ConvertibleArgument` in favor of `Convertible` and `EnumArgument` in favor of `Enumerable`. ([#19612](https://github.com/expo/expo/pull/19612) by [@tsapeta](https://github.com/tsapeta))
29
+
30
+ ### 🎉 New features
31
+
32
+ - Implemented a mechanism for hooking into to the view lifecycle events (introduces new `OnViewDidUpdateProps` definition component). ([#19549](https://github.com/expo/expo/pull/19549) by [@tsapeta](https://github.com/tsapeta))
33
+
34
+ ### 🐛 Bug fixes
35
+
36
+ - Fixed records aren't correctly converted to JS objects in the release builds on Android. ([#19551](https://github.com/expo/expo/pull/19551) by [@lukmccall](https://github.com/lukmccall))
37
+ - Reject promises with a `CodedError` instead of a plain object. ([#19605](https://github.com/expo/expo/pull/19605) by [@tsapeta](https://github.com/tsapeta))
38
+
39
+ ### 💡 Others
40
+
41
+ - Simplified dispatching view events. ([#19537](https://github.com/expo/expo/pull/19537) by [@tsapeta](https://github.com/tsapeta))
42
+
13
43
  ## 0.12.0 — 2022-10-06
14
44
 
15
45
  ### 🛠 Breaking changes
@@ -43,6 +73,7 @@
43
73
  - Add the `RegisterActivityContracts` component to register all of activity result contracts on Android. ([#19180](https://github.com/expo/expo/pull/19180) by [@lukmccall](https://github.com/lukmccall))
44
74
  - Improves JSI/JNI type conversion to support complex function arguments on Android. ([#19120](https://github.com/expo/expo/pull/19120) & [#19094](https://github.com/expo/expo/pull/19094) by [@lukmccall](https://github.com/lukmccall))
45
75
  - Using JSI instead of the bridge to call native methods also on legacy modules on iOS. ([#19209](https://github.com/expo/expo/pull/19209) by [@lukmccall](https://github.com/lukmccall))
76
+ - Added `cacheDirectory` and `persistentFilesDirectory` to `AppContext` on Android to fix cache directories being incorrect in new Sweet API modules. It uses a new `AppDirectoriesModule` to get correct scoped directories from old module API. ([#19205](https://github.com/expo/expo/pull/19205) by [@aleqsio](https://github.com/aleqsio))
46
77
 
47
78
  ### 🐛 Bug fixes
48
79
 
@@ -51,6 +82,7 @@
51
82
  - Update gradle excludes to fix detox tests. ([#19254](https://github.com/expo/expo/pull/19254) by [@esamelson](https://github.com/esamelson))
52
83
  - Fixed event listeners do not work when running with remote debugging mode on iOS. ([#19211](https://github.com/expo/expo/pull/19211) by [@kudo](https://github.com/kudo))
53
84
  - Use shared C++ runtime to reduce library size on Android. ([#19372](https://github.com/expo/expo/pull/19372) by [@kudo](https://github.com/kudo))
85
+ - Fixed `JSCRuntime destroyed with a dangling API object` on Android. ([#19487](https://github.com/expo/expo/pull/19487) by [@lukmccall](https://github.com/lukmccall))
54
86
 
55
87
  ### 💡 Others
56
88
 
@@ -85,7 +85,7 @@ if(${UNIT_TEST})
85
85
  PATHS ${HERMES_SO_DIR}
86
86
  NO_CMAKE_FIND_ROOT_PATH
87
87
  )
88
- set(JSEXECUTOR_INCLUDE "${HERMES_HEADER_DIR}")
88
+ set(JSEXECUTOR_INCLUDE ${HERMES_HEADER_DIR} ${HERMES_HEADER_DIR}/API ${HERMES_HEADER_DIR}/public)
89
89
  else()
90
90
  find_library(
91
91
  JSEXECUTOR_LIB
@@ -6,7 +6,7 @@ apply plugin: 'maven-publish'
6
6
  apply plugin: "de.undercouch.download"
7
7
 
8
8
  group = 'host.exp.exponent'
9
- version = '0.12.0'
9
+ version = '0.13.1'
10
10
 
11
11
  buildscript {
12
12
  def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
@@ -39,12 +39,6 @@ buildscript {
39
39
  }
40
40
  }
41
41
 
42
- def isAndroidTest = {
43
- Gradle gradle = getGradle()
44
- String tskReqStr = gradle.getStartParameter().getTaskRequests().toString()
45
- return tskReqStr.contains("AndroidTest")
46
- }.call()
47
-
48
42
  def isExpoModulesCoreTests = {
49
43
  Gradle gradle = getGradle()
50
44
  String tskReqStr = gradle.getStartParameter().getTaskRequests().toString()
@@ -89,7 +83,12 @@ def currentHermesVersion = file("${REACT_NATIVE_DIR}/sdks/.hermesversion").exist
89
83
  def hasHermesProject = findProject(":ReactAndroid:hermes-engine") != null
90
84
  def prebuiltHermesCacheHit = hasHermesProject && currentHermesVersion == prebuiltHermesVersion
91
85
 
86
+ // By default we are going to download and unzip hermes inside the /sdks/hermes folder
87
+ // but you can provide an override for where the hermes source code is located.
88
+ def hermesDir = System.getenv("REACT_NATIVE_OVERRIDE_HERMES_DIR") ?: file("${REACT_NATIVE_DIR}/sdks/hermes")
89
+
92
90
  def USE_HERMES = false
91
+ def NEED_DOWNLOAD_HERMES = false
93
92
  def HERMES_HEADER_DIR = null
94
93
  def HERMES_AAR = null
95
94
  if (findProject(":app")) {
@@ -114,19 +113,14 @@ if (USE_HERMES) {
114
113
  HERMES_HEADER_DIR = file("${thirdPartyNdkDir}/hermes/prefab/modules/libhermes/include")
115
114
  HERMES_AAR = file("${REACT_NATIVE_DIR}/ReactAndroid/hermes-engine/build/outputs/aar/hermes-engine-debug.aar")
116
115
  } else {
117
- // The `hermes-engine` package doesn't contain the correct version of the Hermes.
118
- // The AAR we need to import is located in the React Native package.
119
- // However, the version from RN doesn't include header files.
120
- // To get those, we must fall back to the `hermes-engine` package.
121
- def hermesEngineDir = new File(["node", "--print", "require.resolve('hermes-engine/package.json')"].execute(null, rootDir).text.trim()).parent
122
-
123
116
  def prebuiltAAR = fileTree(REACT_NATIVE_DIR).matching { include "**/hermes-engine/**/hermes-engine-*-debug.aar" }
124
117
  if (prebuiltAAR.any()) {
125
118
  HERMES_AAR = prebuiltAAR.singleFile
126
119
  } else {
127
120
  HERMES_AAR = file("${hermesEngineDir}/android/hermes-debug.aar")
128
121
  }
129
- HERMES_HEADER_DIR = file("${hermesEngineDir}/android/include")
122
+ HERMES_HEADER_DIR = file("${hermesDir}")
123
+ NEED_DOWNLOAD_HERMES = true
130
124
  }
131
125
  }
132
126
  // END HERMES
@@ -160,6 +154,24 @@ afterEvaluate {
160
154
  android {
161
155
  compileSdkVersion safeExtGet("compileSdkVersion", 31)
162
156
 
157
+ if (REACT_NATIVE_BUILD_FROM_SOURCE) {
158
+ if (rootProject.hasProperty("ndkPath")) {
159
+ ndkPath rootProject.ext.ndkPath
160
+ }
161
+ if (rootProject.hasProperty("ndkVersion")) {
162
+ ndkVersion rootProject.ext.ndkVersion
163
+ }
164
+ } else {
165
+ // This ndkVersion should align to prebuilt react-native building ndkVersion.
166
+ // Because the unwind library is different before NDK r23 and after,
167
+ // if we build this library with newer NDK, C++ exceptions are incompatible between these libraries.
168
+ // For example, C++ exceptions throwing from hermes are not catchable from this library.
169
+ // Ref: https://android.googlesource.com/platform/ndk/+/master/docs/BuildSystemMaintainers.md#unwinding
170
+ //
171
+ // TODO: Revisit this version when upgrade react-native 0.71 (Expo SDK 48)
172
+ ndkVersion = "21.4.7075529"
173
+ }
174
+
163
175
  compileOptions {
164
176
  sourceCompatibility JavaVersion.VERSION_11
165
177
  targetCompatibility JavaVersion.VERSION_11
@@ -174,7 +186,7 @@ android {
174
186
  targetSdkVersion safeExtGet("targetSdkVersion", 31)
175
187
  consumerProguardFiles 'proguard-rules.pro'
176
188
  versionCode 1
177
- versionName "0.12.0"
189
+ versionName "0.13.1"
178
190
  buildConfigField "boolean", "IS_NEW_ARCHITECTURE_ENABLED", isNewArchitectureEnabled.toString()
179
191
 
180
192
  testInstrumentationRunner "expo.modules.TestRunner"
@@ -237,15 +249,9 @@ android {
237
249
  // In android (instrumental) tests, we want to package all so files to enable our JSI functionality.
238
250
  // Otherwise, those files should be excluded, because will be loaded by the application.
239
251
  if (isExpoModulesCoreTests) {
240
- excludes = []
241
- pickFirsts = sharedLibraries
252
+ pickFirsts += sharedLibraries
242
253
  } else {
243
- excludes = sharedLibraries
244
- }
245
- if (isExpoModulesCoreTests || isAndroidTest) {
246
- excludes.add("META-INF/MANIFEST.MF")
247
- excludes.add("META-INF/com.android.tools/proguard/coroutines.pro")
248
- excludes.add("META-INF/proguard/coroutines.pro")
254
+ excludes += sharedLibraries
249
255
  }
250
256
  }
251
257
 
@@ -469,12 +475,36 @@ task prepareFolly(dependsOn: [downloadFolly], type: Copy) {
469
475
  includeEmptyDirs = false
470
476
  into("$thirdPartyNdkDir/folly")
471
477
  }
472
- // END FOLLy
478
+ // END FOLLY
479
+
480
+ task downloadHermes(type: Download) {
481
+ def hermesVersion = currentHermesVersion ?: "main"
482
+ src("https://github.com/facebook/hermes/tarball/${hermesVersion}")
483
+ onlyIfNewer(true)
484
+ overwrite(false)
485
+ dest(new File(downloadsDir, "hermes-${hermesVersion}.tar.gz"))
486
+ }
487
+
488
+ task unzipHermes(dependsOn: downloadHermes, type: Copy) {
489
+ from(tarTree(downloadHermes.dest)) {
490
+ eachFile { file ->
491
+ // We flatten the unzip as the tarball contains a `facebook-hermes-<SHA>`
492
+ // folder at the top level.
493
+ if (file.relativePath.segments.size() > 1) {
494
+ file.relativePath = new org.gradle.api.file.RelativePath(!file.isDirectory(), file.relativePath.segments.drop(1))
495
+ }
496
+ }
497
+ }
498
+ into(hermesDir)
499
+ }
473
500
 
474
501
  task prepareHermes(dependsOn: createNativeDepsDirectories) {
475
502
  if (!USE_HERMES) {
476
503
  return
477
504
  }
505
+ if (NEED_DOWNLOAD_HERMES) {
506
+ dependsOn(unzipHermes)
507
+ }
478
508
 
479
509
  doLast {
480
510
  def files = zipTree(HERMES_AAR).matching({ it.include "**/*.so", "prefab/modules/libhermes/include/**/*" })
@@ -11,3 +11,10 @@
11
11
  -keepclassmembers class * {
12
12
  @expo.modules.core.interfaces.DoNotStrip *;
13
13
  }
14
+
15
+ -keep class * implements expo.modules.kotlin.records.Record {
16
+ *;
17
+ }
18
+ -keepclassmembers enum * implements expo.modules.kotlin.types.Enumerable {
19
+ *;
20
+ }
@@ -46,9 +46,27 @@ jni::local_ref<UnexpectedException> UnexpectedException::create(const std::strin
46
46
  );
47
47
  }
48
48
 
49
+ jsi::Value makeCodedError(
50
+ jsi::Runtime &rt,
51
+ jsi::String code,
52
+ jsi::String message
53
+ ) {
54
+ auto codedErrorConstructor = rt
55
+ .global()
56
+ .getProperty(rt, "ExpoModulesCore_CodedError")
57
+ .asObject(rt)
58
+ .asFunction(rt);
59
+
60
+ return codedErrorConstructor.callAsConstructor(
61
+ rt, {
62
+ jsi::Value(rt, code),
63
+ jsi::Value(rt, message)
64
+ }
65
+ );
66
+ }
67
+
49
68
  void rethrowAsCodedError(
50
69
  jsi::Runtime &rt,
51
- JSIInteropModuleRegistry *registry,
52
70
  jni::JniException &jniException
53
71
  ) {
54
72
  jni::local_ref<jni::JThrowable> unboxedThrowable = jniException.getThrowable();
@@ -57,25 +75,40 @@ void rethrowAsCodedError(
57
75
  auto code = codedException->getCode();
58
76
  auto message = codedException->getLocalizedMessage();
59
77
 
60
- auto *codedErrorPointer = registry->jsRegistry->getOptionalObject<jsi::Function>(
61
- JSReferencesCache::JSKeys::CODED_ERROR
78
+ auto codedError = makeCodedError(
79
+ rt,
80
+ jsi::String::createFromUtf8(rt, code),
81
+ jsi::String::createFromUtf8(rt, message.value_or(""))
82
+ );
83
+
84
+ throw jsi::JSError(
85
+ message.value_or(""),
86
+ rt,
87
+ std::move(codedError)
62
88
  );
63
- if (codedErrorPointer != nullptr) {
64
- auto &jsCodedError = *codedErrorPointer;
65
-
66
- throw jsi::JSError(
67
- message.value_or(""),
68
- rt,
69
- jsCodedError.callAsConstructor(
70
- rt,
71
- jsi::String::createFromUtf8(rt, code),
72
- jsi::String::createFromUtf8(rt, message.value_or(""))
73
- )
74
- );
75
- }
76
89
  }
77
90
 
78
91
  // Rethrow error if we can't wrap it.
79
92
  throw;
80
93
  }
94
+
95
+ void throwPendingJniExceptionAsCppException() {
96
+ JNIEnv* env = jni::Environment::current();
97
+ if (env->ExceptionCheck() == JNI_FALSE) {
98
+ return;
99
+ }
100
+
101
+ auto throwable = env->ExceptionOccurred();
102
+ if (!throwable) {
103
+ throw std::runtime_error("Unable to get pending JNI exception.");
104
+ }
105
+ env->ExceptionClear();
106
+
107
+ throw jni::JniException(jni::adopt_local(throwable));
108
+ }
109
+
110
+ void throwNewJavaException(jthrowable throwable) {
111
+ throw jni::JniException(jni::wrap_alias(throwable));
112
+ }
113
+
81
114
  } // namespace expo
@@ -65,7 +65,23 @@ public:
65
65
  */
66
66
  [[noreturn]] void rethrowAsCodedError(
67
67
  jsi::Runtime &rt,
68
- JSIInteropModuleRegistry *registry,
69
68
  jni::JniException &jniException
70
69
  );
70
+
71
+ jsi::Value makeCodedError(
72
+ jsi::Runtime &runtime,
73
+ jsi::String code,
74
+ jsi::String message
75
+ );
76
+
77
+ /**
78
+ * fbjni@0.2.2 is built by ndk r21, its exceptions are not catchable by expo-modules-core built by ndk r23+.
79
+ * To catch these excetptions, we copy the `facebook::jni::throwPendingJniExceptionAsCppException` here and throw exceptions on our own.
80
+ */
81
+ void throwPendingJniExceptionAsCppException();
82
+
83
+ /**
84
+ * Same as `facebook::jni::throwNewJavaException` but throwing exceptions on our own.
85
+ */
86
+ [[noreturn]] void throwNewJavaException(jthrowable throwable);
71
87
  } // namespace expo
@@ -12,6 +12,14 @@ namespace expo {
12
12
  ExpoModulesHostObject::ExpoModulesHostObject(JSIInteropModuleRegistry *installer)
13
13
  : installer(installer) {}
14
14
 
15
+ /**
16
+ * Clears jsi references held by JSRegistry and JavaScriptRuntime.
17
+ */
18
+ ExpoModulesHostObject::~ExpoModulesHostObject() {
19
+ installer->jsRegistry.reset();
20
+ installer->runtimeHolder.reset();
21
+ }
22
+
15
23
  jsi::Value ExpoModulesHostObject::get(jsi::Runtime &runtime, const jsi::PropNameID &name) {
16
24
  auto cName = name.utf8(runtime);
17
25
  auto module = installer->getModule(cName);
@@ -20,6 +20,8 @@ class ExpoModulesHostObject : public jsi::HostObject {
20
20
  public:
21
21
  ExpoModulesHostObject(JSIInteropModuleRegistry *installer);
22
22
 
23
+ ~ExpoModulesHostObject() override;
24
+
23
25
  jsi::Value get(jsi::Runtime &, const jsi::PropNameID &name) override;
24
26
 
25
27
  void set(jsi::Runtime &, const jsi::PropNameID &name, const jsi::Value &value) override;
@@ -1,12 +1,14 @@
1
1
  // Copyright © 2021-present 650 Industries, Inc. (aka Expo)
2
2
 
3
3
  #include "JNIFunctionBody.h"
4
+ #include "Exceptions.h"
4
5
  #include "JavaReferencesCache.h"
5
6
 
6
7
  namespace jni = facebook::jni;
7
8
  namespace react = facebook::react;
8
9
 
9
10
  namespace expo {
11
+
10
12
  jni::local_ref<jni::JObject>
11
13
  JNIFunctionBody::invoke(jobjectArray args) {
12
14
  // Do NOT use getClass here!
@@ -20,7 +22,9 @@ JNIFunctionBody::invoke(jobjectArray args) {
20
22
  "([Ljava/lang/Object;)Ljava/lang/Object;"
21
23
  );
22
24
 
23
- return method(this->self(), args);
25
+ auto result = jni::Environment::current()->CallObjectMethod(this->self(), method.getId(), args);
26
+ throwPendingJniExceptionAsCppException();
27
+ return jni::adopt_local(static_cast<jni::JniType<jni::JObject>>(result));
24
28
  }
25
29
 
26
30
  void JNIAsyncFunctionBody::invoke(
@@ -40,6 +44,8 @@ void JNIAsyncFunctionBody::invoke(
40
44
  "([Ljava/lang/Object;Lexpo/modules/kotlin/jni/PromiseImpl;)V"
41
45
  );
42
46
 
43
- method(this->self(), args, promise);
47
+ jni::Environment::current()->CallVoidMethod(this->self(), method.getId(), args, promise);
48
+ throwPendingJniExceptionAsCppException();
44
49
  }
50
+
45
51
  } // namespace expo
@@ -8,16 +8,6 @@ JSReferencesCache::JSReferencesCache(jsi::Runtime &runtime) {
8
8
  runtime.global().getPropertyAsFunction(runtime, "Promise")
9
9
  )
10
10
  );
11
-
12
- if (runtime.global().hasProperty(runtime, "ExpoModulesCore_CodedError")) {
13
- auto jsCodedError = runtime.global()
14
- .getPropertyAsFunction(runtime, "ExpoModulesCore_CodedError");
15
-
16
- jsObjectRegistry.emplace(
17
- JSKeys::CODED_ERROR,
18
- std::make_unique<jsi::Object>(std::move(jsCodedError))
19
- );
20
- }
21
11
  }
22
12
 
23
13
  jsi::PropNameID &JSReferencesCache::getPropNameID(
@@ -21,8 +21,7 @@ namespace expo {
21
21
  class JSReferencesCache {
22
22
  public:
23
23
  enum class JSKeys {
24
- PROMISE,
25
- CODED_ERROR
24
+ PROMISE
26
25
  };
27
26
 
28
27
  JSReferencesCache() = delete;
@@ -72,6 +72,7 @@ void JavaScriptModuleObject::registerSyncFunction(
72
72
 
73
73
  methodsMetadata.try_emplace(
74
74
  cName,
75
+ longLivedObjectCollection_,
75
76
  cName,
76
77
  args,
77
78
  false,
@@ -90,6 +91,7 @@ void JavaScriptModuleObject::registerAsyncFunction(
90
91
 
91
92
  methodsMetadata.try_emplace(
92
93
  cName,
94
+ longLivedObjectCollection_,
93
95
  cName,
94
96
  args,
95
97
  true,
@@ -107,6 +109,7 @@ void JavaScriptModuleObject::registerProperty(
107
109
  auto cName = name->toStdString();
108
110
 
109
111
  auto getterMetadata = MethodMetadata(
112
+ longLivedObjectCollection_,
110
113
  cName,
111
114
  0,
112
115
  false,
@@ -117,6 +120,7 @@ void JavaScriptModuleObject::registerProperty(
117
120
  auto types = std::vector<std::unique_ptr<AnyType>>();
118
121
  types.push_back(std::make_unique<AnyType>(jni::make_local(expectedArgType)));
119
122
  auto setterMetadata = MethodMetadata(
123
+ longLivedObjectCollection_,
120
124
  cName,
121
125
  1,
122
126
  false,
@@ -135,6 +139,19 @@ void JavaScriptModuleObject::registerProperty(
135
139
  JavaScriptModuleObject::HostObject::HostObject(
136
140
  JavaScriptModuleObject *jsModule) : jsModule(jsModule) {}
137
141
 
142
+ /**
143
+ * Clears all the JSI references held by the `JavaScriptModuleObject`.
144
+ */
145
+ JavaScriptModuleObject::HostObject::~HostObject() {
146
+ if (jsModule->jsiObject != nullptr) {
147
+ jsModule->jsiObject.reset();
148
+ }
149
+ jsModule->methodsMetadata.clear();
150
+ jsModule->constants.clear();
151
+ jsModule->properties.clear();
152
+ jsModule->longLivedObjectCollection_->clear();
153
+ }
154
+
138
155
  jsi::Value JavaScriptModuleObject::HostObject::get(jsi::Runtime &runtime,
139
156
  const jsi::PropNameID &name) {
140
157
  auto cName = name.utf8(runtime);
@@ -214,4 +231,10 @@ std::vector<jsi::PropNameID> JavaScriptModuleObject::HostObject::getPropertyName
214
231
 
215
232
  return result;
216
233
  }
234
+
235
+ JavaScriptModuleObject::JavaScriptModuleObject(jni::alias_ref<jhybridobject> jThis)
236
+ : javaPart_(jni::make_global(jThis)) {
237
+ longLivedObjectCollection_ = std::make_shared<react::LongLivedObjectCollection>();
238
+ }
239
+
217
240
  } // namespace expo
@@ -4,6 +4,7 @@
4
4
 
5
5
  #include <fbjni/fbjni.h>
6
6
  #include <jsi/jsi.h>
7
+ #include <react/bridging/LongLivedObject.h>
7
8
  #include <react/jni/ReadableNativeArray.h>
8
9
  #include <jni/JCallback.h>
9
10
 
@@ -104,6 +105,8 @@ public:
104
105
  public:
105
106
  HostObject(JavaScriptModuleObject *);
106
107
 
108
+ ~HostObject() override;
109
+
107
110
  jsi::Value get(jsi::Runtime &, const jsi::PropNameID &name) override;
108
111
 
109
112
  void set(jsi::Runtime &, const jsi::PropNameID &name, const jsi::Value &value) override;
@@ -114,6 +117,9 @@ public:
114
117
  JavaScriptModuleObject *jsModule;
115
118
  };
116
119
 
120
+ private:
121
+ explicit JavaScriptModuleObject(jni::alias_ref<jhybridobject> jThis);
122
+
117
123
  private:
118
124
  friend HybridBase;
119
125
  /**
@@ -139,7 +145,10 @@ private:
139
145
  */
140
146
  std::map<std::string, std::pair<MethodMetadata, MethodMetadata>> properties;
141
147
 
142
- explicit JavaScriptModuleObject(jni::alias_ref<jhybridobject> jThis)
143
- : javaPart_(jni::make_global(jThis)) {}
148
+ /**
149
+ * The `LongLivedObjectCollection` to hold `LongLivedObject` (callbacks or promises) for this module.
150
+ */
151
+ std::shared_ptr<react::LongLivedObjectCollection> longLivedObjectCollection_;
152
+
144
153
  };
145
154
  } // namespace expo
@@ -78,7 +78,7 @@ std::string JavaScriptValue::kind() {
78
78
  return "object";
79
79
  }
80
80
 
81
- jni::throwNewJavaException(
81
+ throwNewJavaException(
82
82
  UnexpectedException::create("Unknown type").get()
83
83
  );
84
84
  }