expo-modules-core 0.10.0 → 0.11.2

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 (63) hide show
  1. package/CHANGELOG.md +23 -0
  2. package/README.md +3 -3
  3. package/android/CMakeLists.txt +14 -5
  4. package/android/build.gradle +78 -28
  5. package/android/src/main/cpp/CachedReferencesRegistry.cpp +67 -0
  6. package/android/src/main/cpp/CachedReferencesRegistry.h +80 -0
  7. package/android/src/main/cpp/JNIFunctionBody.cpp +28 -12
  8. package/android/src/main/cpp/JNIFunctionBody.h +2 -2
  9. package/android/src/main/cpp/JNIInjector.cpp +4 -0
  10. package/android/src/main/cpp/JavaScriptModuleObject.cpp +86 -5
  11. package/android/src/main/cpp/JavaScriptModuleObject.h +27 -5
  12. package/android/src/main/cpp/JavaScriptRuntime.cpp +10 -12
  13. package/android/src/main/cpp/MethodMetadata.cpp +181 -40
  14. package/android/src/main/cpp/MethodMetadata.h +43 -3
  15. package/android/src/main/java/expo/modules/kotlin/AppContext.kt +63 -10
  16. package/android/src/main/java/expo/modules/kotlin/ModuleHolder.kt +6 -0
  17. package/android/src/main/java/expo/modules/kotlin/activityaware/AppCompatActivityAware.kt +49 -0
  18. package/android/src/main/java/expo/modules/kotlin/activityaware/AppCompatActivityAwareHelper.kt +43 -0
  19. package/android/src/main/java/expo/modules/kotlin/activityaware/OnActivityAvailableListener.kt +18 -0
  20. package/android/src/main/java/expo/modules/kotlin/activityresult/ActivityResultsManager.kt +99 -0
  21. package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultCaller.kt +25 -0
  22. package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultContract.kt +27 -0
  23. package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultFallbackCallback.kt +17 -0
  24. package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultLauncher.kt +30 -0
  25. package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultRegistry.kt +358 -0
  26. package/android/src/main/java/expo/modules/kotlin/activityresult/DataPersistor.kt +135 -0
  27. package/android/src/main/java/expo/modules/kotlin/functions/AnyFunction.kt +34 -1
  28. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunction.kt +7 -1
  29. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionBuilder.kt +0 -108
  30. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionComponent.kt +5 -2
  31. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionWithPromiseComponent.kt +5 -2
  32. package/android/src/main/java/expo/modules/kotlin/functions/SuspendFunctionComponent.kt +9 -2
  33. package/android/src/main/java/expo/modules/kotlin/functions/SyncFunctionComponent.kt +9 -1
  34. package/android/src/main/java/expo/modules/kotlin/jni/CppType.kt +1 -0
  35. package/android/src/main/java/expo/modules/kotlin/jni/JNIFunctionBody.kt +2 -2
  36. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptModuleObject.kt +4 -2
  37. package/android/src/main/java/expo/modules/kotlin/modules/Module.kt +1 -1
  38. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionBuilder.kt +5 -454
  39. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionData.kt +7 -15
  40. package/android/src/main/java/expo/modules/kotlin/objects/ObjectDefinitionBuilder.kt +271 -0
  41. package/android/src/main/java/expo/modules/kotlin/objects/ObjectDefinitionData.kt +21 -0
  42. package/android/src/main/java/expo/modules/kotlin/objects/PropertyComponent.kt +54 -0
  43. package/android/src/main/java/expo/modules/kotlin/objects/PropertyComponentBuilder.kt +32 -0
  44. package/android/src/main/java/expo/modules/kotlin/types/AnyTypeConverter.kt +36 -0
  45. package/android/src/main/java/expo/modules/kotlin/types/TypeConverterProvider.kt +7 -0
  46. package/android/src/main/java/expo/modules/kotlin/views/ViewGroupDefinitionBuilder.kt +0 -41
  47. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinitionBuilder.kt +0 -33
  48. package/build/PermissionsInterface.d.ts +29 -0
  49. package/build/PermissionsInterface.d.ts.map +1 -1
  50. package/build/PermissionsInterface.js +9 -0
  51. package/build/PermissionsInterface.js.map +1 -1
  52. package/ios/ExpoModulesCore.podspec +2 -1
  53. package/ios/JSI/EXJSIInstaller.mm +2 -0
  54. package/ios/JSI/EXJSIUtils.h +1 -0
  55. package/ios/NativeModulesProxy/EXNativeModulesProxy.mm +4 -3
  56. package/ios/Swift/AppContext.swift +2 -4
  57. package/ios/Swift/Classes/ClassComponentElementsBuilder.swift +2 -2
  58. package/ios/Swift/Exceptions/ChainableException.swift +3 -3
  59. package/ios/Swift/ExpoBridgeModule.swift +16 -2
  60. package/ios/Swift/Logging/Logger.swift +3 -0
  61. package/ios/Swift/Promise.swift +5 -1
  62. package/package.json +2 -2
  63. package/src/PermissionsInterface.ts +29 -0
@@ -36,6 +36,8 @@ void JavaScriptModuleObject::registerNatives() {
36
36
  JavaScriptModuleObject::registerSyncFunction),
37
37
  makeNativeMethod("registerAsyncFunction",
38
38
  JavaScriptModuleObject::registerAsyncFunction),
39
+ makeNativeMethod("registerProperty",
40
+ JavaScriptModuleObject::registerProperty),
39
41
  });
40
42
  }
41
43
 
@@ -49,8 +51,9 @@ std::shared_ptr<jsi::Object> JavaScriptModuleObject::getJSIObject(jsi::Runtime &
49
51
  return jsiObject;
50
52
  }
51
53
 
52
- void JavaScriptModuleObject::exportConstants(jni::alias_ref<react::NativeMap::javaobject>
53
- constants) {
54
+ void JavaScriptModuleObject::exportConstants(
55
+ jni::alias_ref<react::NativeMap::javaobject> constants
56
+ ) {
54
57
  auto dynamic = constants->cthis()->consume();
55
58
  assert(dynamic.isObject());
56
59
 
@@ -62,19 +65,73 @@ void JavaScriptModuleObject::exportConstants(jni::alias_ref<react::NativeMap::ja
62
65
  void JavaScriptModuleObject::registerSyncFunction(
63
66
  jni::alias_ref<jstring> name,
64
67
  jint args,
68
+ jni::alias_ref<jni::JArrayInt> desiredTypes,
65
69
  jni::alias_ref<JNIFunctionBody::javaobject> body
66
70
  ) {
67
- auto cName = name->toStdString();
68
- methodsMetadata.try_emplace(cName, cName, args, false, jni::make_global(body));
71
+ std::string cName = name->toStdString();
72
+ std::unique_ptr<int[]> types = desiredTypes->getRegion(0, args);
73
+
74
+ methodsMetadata.try_emplace(
75
+ cName,
76
+ cName,
77
+ args,
78
+ false,
79
+ std::move(types),
80
+ jni::make_global(body)
81
+ );
69
82
  }
70
83
 
71
84
  void JavaScriptModuleObject::registerAsyncFunction(
72
85
  jni::alias_ref<jstring> name,
73
86
  jint args,
87
+ jni::alias_ref<jni::JArrayInt> desiredTypes,
74
88
  jni::alias_ref<JNIAsyncFunctionBody::javaobject> body
75
89
  ) {
76
90
  auto cName = name->toStdString();
77
- methodsMetadata.try_emplace(cName, cName, args, true, jni::make_global(body));
91
+ std::unique_ptr<int[]> types = desiredTypes->getRegion(0, args);
92
+
93
+ methodsMetadata.try_emplace(
94
+ cName,
95
+ cName,
96
+ args,
97
+ true,
98
+ std::move(types),
99
+ jni::make_global(body)
100
+ );
101
+ }
102
+
103
+ void JavaScriptModuleObject::registerProperty(
104
+ jni::alias_ref<jstring> name,
105
+ jint desiredType,
106
+ jni::alias_ref<JNIFunctionBody::javaobject> getter,
107
+ jni::alias_ref<JNIFunctionBody::javaobject> setter
108
+ ) {
109
+ auto cName = name->toStdString();
110
+ std::unique_ptr<int[]> types = std::make_unique<int[]>(1);
111
+ types[0] = desiredType;
112
+
113
+ auto getterMetadata = MethodMetadata(
114
+ cName,
115
+ 0,
116
+ false,
117
+ std::make_unique<int[]>(0),
118
+ jni::make_global(getter)
119
+ );
120
+
121
+ auto setterMetadata = MethodMetadata(
122
+ cName,
123
+ 1,
124
+ false,
125
+ std::move(types),
126
+ jni::make_global(setter)
127
+ );
128
+
129
+ auto functions = std::make_pair(
130
+ std::move(getterMetadata),
131
+ std::move(setterMetadata)
132
+ );
133
+
134
+ properties.insert({cName, std::move(functions)});
78
135
  }
79
136
 
80
137
  JavaScriptModuleObject::HostObject::HostObject(
@@ -90,6 +147,12 @@ jsi::Value JavaScriptModuleObject::HostObject::get(jsi::Runtime &runtime,
90
147
  return jsi::valueFromDynamic(runtime, dynamic);
91
148
  }
92
149
 
150
+ auto propertyRecord = jsModule->properties.find(cName);
151
+ if (propertyRecord != jsModule->properties.end()) {
152
+ auto&[getter, _] = propertyRecord->second;
153
+ return getter.callSync(runtime, jsModule->jsiInteropModuleRegistry, nullptr, 0);
154
+ }
155
+
93
156
  auto metadataRecord = jsModule->methodsMetadata.find(cName);
94
157
  if (metadataRecord == jsModule->methodsMetadata.end()) {
95
158
  return jsi::Value::undefined();
@@ -103,6 +166,14 @@ void JavaScriptModuleObject::HostObject::set(
103
166
  const jsi::PropNameID &name,
104
167
  const jsi::Value &value
105
168
  ) {
169
+ auto cName = name.utf8(runtime);
170
+ auto propertyRecord = jsModule->properties.find(cName);
171
+ if (propertyRecord != jsModule->properties.end()) {
172
+ auto&[_, setter] = propertyRecord->second;
173
+ setter.callSync(runtime, jsModule->jsiInteropModuleRegistry, &value, 1);
174
+ return;
175
+ }
176
+
106
177
  throw jsi::JSError(
107
178
  runtime,
108
179
  "RuntimeError: Cannot override the host object for expo module '" + name.utf8(runtime) + "'"
@@ -133,6 +204,16 @@ std::vector<jsi::PropNameID> JavaScriptModuleObject::HostObject::getPropertyName
133
204
  }
134
205
  );
135
206
 
207
+ auto &properties = jsModule->properties;
208
+ std::transform(
209
+ properties.begin(),
210
+ properties.end(),
211
+ std::back_inserter(result),
212
+ [&rt](const auto &kv) {
213
+ return jsi::PropNameID::forUtf8(rt, kv.first);
214
+ }
215
+ );
216
+
136
217
  return result;
137
218
  }
138
219
  } // namespace expo
@@ -7,7 +7,7 @@
7
7
  #include <react/jni/ReadableNativeArray.h>
8
8
  #include <jni/JCallback.h>
9
9
 
10
- #include <map>
10
+ #include <unordered_map>
11
11
 
12
12
  #include "MethodMetadata.h"
13
13
  #include "JNIFunctionBody.h"
@@ -59,7 +59,8 @@ public:
59
59
  void registerSyncFunction(
60
60
  jni::alias_ref<jstring> name,
61
61
  jint args,
62
- jni::alias_ref<JNIFunctionBody::javaobject> JSIFunctionBody
62
+ jni::alias_ref<jni::JArrayInt> desiredTypes,
63
+ jni::alias_ref<JNIFunctionBody::javaobject> body
63
64
  );
64
65
 
65
66
  /**
@@ -69,7 +70,22 @@ public:
69
70
  void registerAsyncFunction(
70
71
  jni::alias_ref<jstring> name,
71
72
  jint args,
72
- jni::alias_ref<JNIAsyncFunctionBody::javaobject> JSIAsyncFunctionBody
73
+ jni::alias_ref<jni::JArrayInt> desiredTypes,
74
+ jni::alias_ref<JNIAsyncFunctionBody::javaobject> body
75
+ );
76
+
77
+ /**
78
+ * Registers a property
79
+ * @param name of the property
80
+ * @param desiredType of the setter argument
81
+ * @param getter body for the get method - can be nullptr
82
+ * @param setter body for the set method - can be nullptr
83
+ */
84
+ void registerProperty(
85
+ jni::alias_ref<jstring> name,
86
+ jint desiredType,
87
+ jni::alias_ref<JNIFunctionBody::javaobject> getter,
88
+ jni::alias_ref<JNIFunctionBody::javaobject> setter
73
89
  );
74
90
 
75
91
  /**
@@ -109,12 +125,18 @@ private:
109
125
  /**
110
126
  * Metadata map that stores information about all available methods on this module.
111
127
  */
112
- std::map<std::string, MethodMetadata> methodsMetadata;
128
+ std::unordered_map<std::string, MethodMetadata> methodsMetadata;
113
129
 
114
130
  /**
115
131
  * A constants map.
116
132
  */
117
- std::map<std::string, folly::dynamic> constants;
133
+ std::unordered_map<std::string, folly::dynamic> constants;
134
+
135
+ /**
136
+ * A registry of properties
137
+ * The first MethodMetadata points to the getter and the second one to the setter.
138
+ */
139
+ std::map<std::string, std::pair<MethodMetadata, MethodMetadata>> properties;
118
140
 
119
141
  explicit JavaScriptModuleObject(jni::alias_ref<jhybridobject> jThis)
120
142
  : javaPart_(jni::make_global(jThis)) {}
@@ -37,13 +37,6 @@ JavaScriptRuntime::JavaScriptRuntime()
37
37
  .withEnableSampleProfiling(false);
38
38
  runtime = facebook::hermes::makeHermesRuntime(config.build());
39
39
 
40
- // By default "global" property isn't set in the Hermes.
41
- runtime->global().setProperty(
42
- *runtime,
43
- jsi::PropNameID::forUtf8(*runtime, "global"),
44
- runtime->global()
45
- );
46
-
47
40
  // This version of the Hermes uses a Promise implementation that is provided by the RN.
48
41
  // The `setImmediate` function isn't defined, but is required by the Promise implementation.
49
42
  // That's why we inject it here.
@@ -67,6 +60,13 @@ JavaScriptRuntime::JavaScriptRuntime()
67
60
  #else
68
61
  runtime = facebook::jsc::makeJSCRuntime();
69
62
  #endif
63
+
64
+ // By default "global" property isn't set.
65
+ runtime->global().setProperty(
66
+ *runtime,
67
+ jsi::PropNameID::forUtf8(*runtime, "global"),
68
+ runtime->global()
69
+ );
70
70
  }
71
71
 
72
72
  JavaScriptRuntime::JavaScriptRuntime(
@@ -87,10 +87,10 @@ jsi::Runtime *JavaScriptRuntime::get() {
87
87
  jni::local_ref<JavaScriptValue::javaobject>
88
88
  JavaScriptRuntime::evaluateScript(const std::string &script) {
89
89
  auto scriptBuffer = std::make_shared<jsi::StringBuffer>(script);
90
- std::shared_ptr<jsi::Value> result;
91
90
  try {
92
- result = std::make_shared<jsi::Value>(
93
- runtime->evaluateJavaScript(scriptBuffer, "<<evaluated>>")
91
+ return JavaScriptValue::newObjectCxxArgs(
92
+ weak_from_this(),
93
+ std::make_shared<jsi::Value>(runtime->evaluateJavaScript(scriptBuffer, "<<evaluated>>"))
94
94
  );
95
95
  } catch (const jsi::JSError &error) {
96
96
  jni::throwNewJavaException(
@@ -107,8 +107,6 @@ JavaScriptRuntime::evaluateScript(const std::string &script) {
107
107
  ).get()
108
108
  );
109
109
  }
110
-
111
- return JavaScriptValue::newObjectCxxArgs(weak_from_this(), result);
112
110
  }
113
111
 
114
112
  jni::local_ref<JavaScriptObject::javaobject> JavaScriptRuntime::global() {
@@ -1,6 +1,13 @@
1
1
  #include "MethodMetadata.h"
2
-
3
2
  #include "JSIInteropModuleRegistry.h"
3
+ #include "JavaScriptValue.h"
4
+ #include "JavaScriptObject.h"
5
+ #include "CachedReferencesRegistry.h"
6
+
7
+ #include <utility>
8
+
9
+ #include "react/jni/ReadableNativeMap.h"
10
+ #include "react/jni/ReadableNativeArray.h"
4
11
 
5
12
  namespace jni = facebook::jni;
6
13
  namespace jsi = facebook::jsi;
@@ -68,15 +75,91 @@ jni::local_ref<react::JCxxCallbackImpl::JavaPart> createJavaCallbackFromJSIFunct
68
75
  return react::JCxxCallbackImpl::newObjectCxxArgs(fn);
69
76
  }
70
77
 
78
+ std::vector<jvalue> MethodMetadata::convertJSIArgsToJNI(
79
+ JSIInteropModuleRegistry *moduleRegistry,
80
+ JNIEnv *env,
81
+ jsi::Runtime &rt,
82
+ const jsi::Value *args,
83
+ size_t count,
84
+ bool returnGlobalReferences
85
+ ) {
86
+ std::vector<jvalue> result(count);
87
+
88
+ auto makeGlobalIfNecessary = [env, returnGlobalReferences](jobject obj) -> jobject {
89
+ if (returnGlobalReferences) {
90
+ return env->NewGlobalRef(obj);
91
+ }
92
+ return obj;
93
+ };
94
+
95
+ for (unsigned int argIndex = 0; argIndex < count; argIndex++) {
96
+ const jsi::Value *arg = &args[argIndex];
97
+ jvalue *jarg = &result[argIndex];
98
+ int desiredType = desiredTypes[argIndex];
99
+
100
+ if (desiredType & CppType::JS_VALUE) {
101
+ jarg->l = makeGlobalIfNecessary(
102
+ JavaScriptValue::newObjectCxxArgs(
103
+ moduleRegistry->runtimeHolder->weak_from_this(),
104
+ // TODO(@lukmccall): make sure that copy here is necessary
105
+ std::make_shared<jsi::Value>(jsi::Value(rt, *arg))
106
+ ).release()
107
+ );
108
+ } else if (desiredType & CppType::JS_OBJECT) {
109
+ jarg->l = makeGlobalIfNecessary(
110
+ JavaScriptObject::newObjectCxxArgs(
111
+ moduleRegistry->runtimeHolder->weak_from_this(),
112
+ std::make_shared<jsi::Object>(arg->getObject(rt))
113
+ ).release()
114
+ );
115
+ } else if (arg->isNull() || arg->isUndefined()) {
116
+ jarg->l = nullptr;
117
+ } else if (arg->isNumber()) {
118
+ auto &doubleClass = CachedReferencesRegistry::instance()
119
+ ->getJClass("java/lang/Double");
120
+ jmethodID doubleConstructor = doubleClass.getMethod("<init>", "(D)V");
121
+ jarg->l = makeGlobalIfNecessary(
122
+ env->NewObject(doubleClass.clazz, doubleConstructor, arg->getNumber()));
123
+ } else if (arg->isBool()) {
124
+ auto &booleanClass = CachedReferencesRegistry::instance()
125
+ ->getJClass("java/lang/Boolean");
126
+ jmethodID booleanConstructor = booleanClass.getMethod("<init>", "(Z)V");
127
+ jarg->l = makeGlobalIfNecessary(
128
+ env->NewObject(booleanClass.clazz, booleanConstructor, arg->getBool()));
129
+ } else if (arg->isString()) {
130
+ jarg->l = makeGlobalIfNecessary(env->NewStringUTF(arg->getString(rt).utf8(rt).c_str()));
131
+ } else if (arg->isObject()) {
132
+ const jsi::Object object = arg->getObject(rt);
133
+
134
+ // TODO(@lukmccall): stop using dynamic
135
+ auto dynamic = jsi::dynamicFromValue(rt, *arg);
136
+ if (arg->getObject(rt).isArray(rt)) {
137
+ jarg->l = makeGlobalIfNecessary(
138
+ react::ReadableNativeArray::newObjectCxxArgs(std::move(dynamic)).release());
139
+ } else {
140
+ jarg->l = makeGlobalIfNecessary(
141
+ react::ReadableNativeMap::createWithContents(std::move(dynamic)).release());
142
+ }
143
+ } else {
144
+ // TODO(@lukmccall): throw an exception
145
+ jarg->l = nullptr;
146
+ }
147
+ }
148
+
149
+ return result;
150
+ }
151
+
71
152
  MethodMetadata::MethodMetadata(
72
153
  std::string name,
73
154
  int args,
74
155
  bool isAsync,
156
+ std::unique_ptr<int[]> desiredTypes,
75
157
  jni::global_ref<jobject> &&jBodyReference
76
- ) : name(name),
158
+ ) : name(std::move(name)),
77
159
  args(args),
78
160
  isAsync(isAsync),
79
- jBodyReference(jBodyReference) {}
161
+ desiredTypes(std::move(desiredTypes)),
162
+ jBodyReference(std::move(jBodyReference)) {}
80
163
 
81
164
  std::shared_ptr<jsi::Function> MethodMetadata::toJSFunction(
82
165
  jsi::Runtime &runtime,
@@ -86,49 +169,86 @@ std::shared_ptr<jsi::Function> MethodMetadata::toJSFunction(
86
169
  if (isAsync) {
87
170
  body = std::make_shared<jsi::Function>(toAsyncFunction(runtime, moduleRegistry));
88
171
  } else {
89
- body = std::make_shared<jsi::Function>(toSyncFunction(runtime));
172
+ body = std::make_shared<jsi::Function>(toSyncFunction(runtime, moduleRegistry));
90
173
  }
91
174
  }
92
175
 
93
176
  return body;
94
177
  }
95
178
 
96
- jsi::Function MethodMetadata::toSyncFunction(jsi::Runtime &runtime) {
179
+ jsi::Function MethodMetadata::toSyncFunction(
180
+ jsi::Runtime &runtime,
181
+ JSIInteropModuleRegistry *moduleRegistry
182
+ ) {
97
183
  return jsi::Function::createFromHostFunction(
98
184
  runtime,
99
185
  jsi::PropNameID::forAscii(runtime, name),
100
186
  args,
101
- [this](
187
+ [this, moduleRegistry](
102
188
  jsi::Runtime &rt,
103
189
  const jsi::Value &thisValue,
104
190
  const jsi::Value *args,
105
191
  size_t count
106
192
  ) -> jsi::Value {
107
- auto dynamicArray = folly::dynamic::array();
108
- for (int i = 0; i < count; i++) {
109
- auto &arg = args[i];
110
- dynamicArray.push_back(jsi::dynamicFromValue(rt, arg));
111
- }
112
-
113
- // Cast in this place is safe, cause we know that this function is promise-less.
114
- auto syncFunction = jni::static_ref_cast<JNIFunctionBody>(this->jBodyReference);
115
- auto result = syncFunction->invoke(
116
- react::ReadableNativeArray::newObjectCxxArgs(std::move(dynamicArray)).get()
193
+ return this->callSync(
194
+ rt,
195
+ moduleRegistry,
196
+ args,
197
+ count
117
198
  );
199
+ });
200
+ }
118
201
 
119
- if (result == nullptr) {
120
- return jsi::Value::undefined();
121
- }
202
+ jsi::Value MethodMetadata::callSync(
203
+ jsi::Runtime &rt,
204
+ JSIInteropModuleRegistry *moduleRegistry,
205
+ const jsi::Value *args,
206
+ size_t count
207
+ ) {
208
+ if (this->jBodyReference == nullptr) {
209
+ return jsi::Value::undefined();
210
+ }
122
211
 
123
- return jsi::valueFromDynamic(rt, result->cthis()->consume())
124
- .asObject(rt)
125
- .asArray(rt)
126
- .getValueAtIndex(rt, 0);
127
- });
212
+ JNIEnv *env = jni::Environment::current();
213
+
214
+ /**
215
+ * This will push a new JNI stack frame for the LocalReferences in this
216
+ * function call. When the stack frame for this lambda is popped,
217
+ * all LocalReferences are deleted.
218
+ */
219
+ jni::JniLocalScope scope(env, (int) count);
220
+
221
+ std::vector<jvalue> convertedArgs = convertJSIArgsToJNI(moduleRegistry, env, rt, args, count,
222
+ false);
223
+
224
+ // TODO(@lukmccall): Remove this temp array
225
+ auto tempArray = env->NewObjectArray(
226
+ convertedArgs.size(),
227
+ CachedReferencesRegistry::instance()->getJClass("java/lang/Object").clazz,
228
+ nullptr
229
+ );
230
+ for (size_t i = 0; i < convertedArgs.size(); i++) {
231
+ env->SetObjectArrayElement(tempArray, i, convertedArgs[i].l);
232
+ }
233
+
234
+ // Cast in this place is safe, cause we know that this function is promise-less.
235
+ auto syncFunction = jni::static_ref_cast<JNIFunctionBody>(this->jBodyReference);
236
+ auto result = syncFunction->invoke(
237
+ tempArray
238
+ );
239
+
240
+ if (result == nullptr) {
241
+ return jsi::Value::undefined();
242
+ }
243
+
244
+ return jsi::valueFromDynamic(rt, result->cthis()->consume())
245
+ .asObject(rt)
246
+ .asArray(rt)
247
+ .getValueAtIndex(rt, 0);
128
248
  }
129
249
 
130
250
  jsi::Function MethodMetadata::toAsyncFunction(
131
- jsi::Runtime &runtime,
251
+ jsi::Runtime &runtime,
132
252
  JSIInteropModuleRegistry *moduleRegistry
133
253
  ) {
134
254
  return jsi::Function::createFromHostFunction(
@@ -141,17 +261,22 @@ jsi::Function MethodMetadata::toAsyncFunction(
141
261
  const jsi::Value *args,
142
262
  size_t count
143
263
  ) -> jsi::Value {
144
- auto dynamicArray = folly::dynamic::array();
145
- for (int i = 0; i < count; i++) {
146
- auto &arg = args[i];
147
- dynamicArray.push_back(jsi::dynamicFromValue(rt, arg));
148
- }
264
+ JNIEnv *env = jni::Environment::current();
265
+
266
+ /**
267
+ * This will push a new JNI stack frame for the LocalReferences in this
268
+ * function call. When the stack frame for this lambda is popped,
269
+ * all LocalReferences are deleted.
270
+ */
271
+ jni::JniLocalScope scope(env, (int) count);
272
+ std::vector<jvalue> convertedArgs = convertJSIArgsToJNI(moduleRegistry, env, rt, args, count,
273
+ true);
149
274
 
150
275
  auto Promise = rt.global().getPropertyAsFunction(rt, "Promise");
151
276
  // Creates a JSI promise
152
277
  jsi::Value promise = Promise.callAsConstructor(
153
278
  rt,
154
- createPromiseBody(rt, moduleRegistry, std::move(dynamicArray))
279
+ createPromiseBody(rt, moduleRegistry, std::move(convertedArgs))
155
280
  );
156
281
  return promise;
157
282
  }
@@ -161,7 +286,7 @@ jsi::Function MethodMetadata::toAsyncFunction(
161
286
  jsi::Function MethodMetadata::createPromiseBody(
162
287
  jsi::Runtime &runtime,
163
288
  JSIInteropModuleRegistry *moduleRegistry,
164
- folly::dynamic &&args
289
+ std::vector<jvalue> &&args
165
290
  ) {
166
291
  return jsi::Function::createFromHostFunction(
167
292
  runtime,
@@ -195,25 +320,36 @@ jsi::Function MethodMetadata::createPromiseBody(
195
320
 
196
321
  JNIEnv *env = jni::Environment::current();
197
322
 
198
- jclass jPromiseImpl =
199
- env->FindClass("com/facebook/react/bridge/PromiseImpl");
200
- jmethodID jPromiseImplConstructor = env->GetMethodID(
201
- jPromiseImpl,
323
+ auto &jPromise = CachedReferencesRegistry::instance()->getJClass(
324
+ "com/facebook/react/bridge/PromiseImpl");
325
+ jmethodID jPromiseConstructor = jPromise.getMethod(
202
326
  "<init>",
203
- "(Lcom/facebook/react/bridge/Callback;Lcom/facebook/react/bridge/Callback;)V");
327
+ "(Lcom/facebook/react/bridge/Callback;Lcom/facebook/react/bridge/Callback;)V"
328
+ );
204
329
 
205
330
  // Creates a promise object
206
331
  jobject promise = env->NewObject(
207
- jPromiseImpl,
208
- jPromiseImplConstructor,
332
+ jPromise.clazz,
333
+ jPromiseConstructor,
209
334
  resolve,
210
335
  reject
211
336
  );
212
337
 
338
+ auto argsSize = args.size();
339
+ // TODO(@lukmccall): Remove this temp array
340
+ auto tempArray = env->NewObjectArray(
341
+ argsSize,
342
+ CachedReferencesRegistry::instance()->getJClass("java/lang/Object").clazz,
343
+ nullptr
344
+ );
345
+ for (size_t i = 0; i < argsSize; i++) {
346
+ env->SetObjectArrayElement(tempArray, i, args[i].l);
347
+ }
348
+
213
349
  // Cast in this place is safe, cause we know that this function expects promise.
214
350
  auto asyncFunction = jni::static_ref_cast<JNIAsyncFunctionBody>(this->jBodyReference);
215
351
  asyncFunction->invoke(
216
- react::ReadableNativeArray::newObjectCxxArgs(args).get(),
352
+ tempArray,
217
353
  promise
218
354
  );
219
355
 
@@ -222,6 +358,11 @@ jsi::Function MethodMetadata::createPromiseBody(
222
358
  // the ownership to the `JNIAsyncFunctionBody`.
223
359
  env->DeleteLocalRef(promise);
224
360
 
361
+ for (const auto &arg: args) {
362
+ env->DeleteGlobalRef(arg.l);
363
+ }
364
+ env->DeleteLocalRef(tempArray);
365
+
225
366
  return jsi::Value::undefined();
226
367
  }
227
368
  );
@@ -17,6 +17,20 @@ namespace react = facebook::react;
17
17
  namespace expo {
18
18
  class JSIInteropModuleRegistry;
19
19
 
20
+ /**
21
+ * A cpp version of the `expo.modules.kotlin.jni.CppType` enum.
22
+ * Used to determine which representation of the js value should be sent to the Kotlin.
23
+ */
24
+ enum CppType {
25
+ DOUBLE = 1 << 0,
26
+ BOOLEAN = 1 << 1,
27
+ STRING = 1 << 2,
28
+ JS_OBJECT = 1 << 3,
29
+ JS_VALUE = 1 << 4,
30
+ READABLE_ARRAY = 1 << 5,
31
+ READABLE_MAP = 1 << 6
32
+ };
33
+
20
34
  /**
21
35
  * A class that holds information about the exported function.
22
36
  */
@@ -35,22 +49,29 @@ public:
35
49
  */
36
50
  bool isAsync;
37
51
 
52
+ std::unique_ptr<int[]> desiredTypes;
53
+
38
54
  MethodMetadata(
39
55
  std::string name,
40
56
  int args,
41
57
  bool isAsync,
58
+ std::unique_ptr<int[]> desiredTypes,
42
59
  jni::global_ref<jobject> &&jBodyReference
43
60
  );
44
61
 
45
62
  // We deleted the copy contractor to not deal with transforming the ownership of the `jBodyReference`.
46
63
  MethodMetadata(const MethodMetadata &) = delete;
47
64
 
65
+ MethodMetadata(MethodMetadata &&other) = default;
66
+
48
67
  /**
49
68
  * MethodMetadata owns the only reference to the Kotlin function.
50
69
  * We have to clean that, cause it's a `global_ref`.
51
70
  */
52
71
  ~MethodMetadata() {
53
- jBodyReference.release();
72
+ if (jBodyReference != nullptr) {
73
+ jBodyReference.release();
74
+ }
54
75
  }
55
76
 
56
77
  /**
@@ -65,6 +86,16 @@ public:
65
86
  JSIInteropModuleRegistry *moduleRegistry
66
87
  );
67
88
 
89
+ /**
90
+ * Calls the underlying Kotlin function.
91
+ */
92
+ jsi::Value callSync(
93
+ jsi::Runtime &rt,
94
+ JSIInteropModuleRegistry *moduleRegistry,
95
+ const jsi::Value *args,
96
+ size_t count
97
+ );
98
+
68
99
  private:
69
100
  /**
70
101
  * Reference to one of two java objects - `JNIFunctionBody` or `JNIAsyncFunctionBody`.
@@ -79,14 +110,23 @@ private:
79
110
  */
80
111
  std::shared_ptr<jsi::Function> body = nullptr;
81
112
 
82
- jsi::Function toSyncFunction(jsi::Runtime &runtime);
113
+ jsi::Function toSyncFunction(jsi::Runtime &runtime, JSIInteropModuleRegistry *moduleRegistry);
83
114
 
84
115
  jsi::Function toAsyncFunction(jsi::Runtime &runtime, JSIInteropModuleRegistry *moduleRegistry);
85
116
 
86
117
  jsi::Function createPromiseBody(
87
118
  jsi::Runtime &runtime,
88
119
  JSIInteropModuleRegistry *moduleRegistry,
89
- folly::dynamic &&args
120
+ std::vector<jvalue> &&args
121
+ );
122
+
123
+ std::vector<jvalue> convertJSIArgsToJNI(
124
+ JSIInteropModuleRegistry *moduleRegistry,
125
+ JNIEnv *env,
126
+ jsi::Runtime &rt,
127
+ const jsi::Value *args,
128
+ size_t count,
129
+ bool returnGlobalReferences
90
130
  );
91
131
  };
92
132
  } // namespace expo