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.
- package/CHANGELOG.md +23 -0
- package/README.md +3 -3
- package/android/CMakeLists.txt +14 -5
- package/android/build.gradle +78 -28
- package/android/src/main/cpp/CachedReferencesRegistry.cpp +67 -0
- package/android/src/main/cpp/CachedReferencesRegistry.h +80 -0
- package/android/src/main/cpp/JNIFunctionBody.cpp +28 -12
- package/android/src/main/cpp/JNIFunctionBody.h +2 -2
- package/android/src/main/cpp/JNIInjector.cpp +4 -0
- package/android/src/main/cpp/JavaScriptModuleObject.cpp +86 -5
- package/android/src/main/cpp/JavaScriptModuleObject.h +27 -5
- package/android/src/main/cpp/JavaScriptRuntime.cpp +10 -12
- package/android/src/main/cpp/MethodMetadata.cpp +181 -40
- package/android/src/main/cpp/MethodMetadata.h +43 -3
- package/android/src/main/java/expo/modules/kotlin/AppContext.kt +63 -10
- package/android/src/main/java/expo/modules/kotlin/ModuleHolder.kt +6 -0
- package/android/src/main/java/expo/modules/kotlin/activityaware/AppCompatActivityAware.kt +49 -0
- package/android/src/main/java/expo/modules/kotlin/activityaware/AppCompatActivityAwareHelper.kt +43 -0
- package/android/src/main/java/expo/modules/kotlin/activityaware/OnActivityAvailableListener.kt +18 -0
- package/android/src/main/java/expo/modules/kotlin/activityresult/ActivityResultsManager.kt +99 -0
- package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultCaller.kt +25 -0
- package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultContract.kt +27 -0
- package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultFallbackCallback.kt +17 -0
- package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultLauncher.kt +30 -0
- package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultRegistry.kt +358 -0
- package/android/src/main/java/expo/modules/kotlin/activityresult/DataPersistor.kt +135 -0
- package/android/src/main/java/expo/modules/kotlin/functions/AnyFunction.kt +34 -1
- package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunction.kt +7 -1
- package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionBuilder.kt +0 -108
- package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionComponent.kt +5 -2
- package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionWithPromiseComponent.kt +5 -2
- package/android/src/main/java/expo/modules/kotlin/functions/SuspendFunctionComponent.kt +9 -2
- package/android/src/main/java/expo/modules/kotlin/functions/SyncFunctionComponent.kt +9 -1
- package/android/src/main/java/expo/modules/kotlin/jni/CppType.kt +1 -0
- package/android/src/main/java/expo/modules/kotlin/jni/JNIFunctionBody.kt +2 -2
- package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptModuleObject.kt +4 -2
- package/android/src/main/java/expo/modules/kotlin/modules/Module.kt +1 -1
- package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionBuilder.kt +5 -454
- package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionData.kt +7 -15
- package/android/src/main/java/expo/modules/kotlin/objects/ObjectDefinitionBuilder.kt +271 -0
- package/android/src/main/java/expo/modules/kotlin/objects/ObjectDefinitionData.kt +21 -0
- package/android/src/main/java/expo/modules/kotlin/objects/PropertyComponent.kt +54 -0
- package/android/src/main/java/expo/modules/kotlin/objects/PropertyComponentBuilder.kt +32 -0
- package/android/src/main/java/expo/modules/kotlin/types/AnyTypeConverter.kt +36 -0
- package/android/src/main/java/expo/modules/kotlin/types/TypeConverterProvider.kt +7 -0
- package/android/src/main/java/expo/modules/kotlin/views/ViewGroupDefinitionBuilder.kt +0 -41
- package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinitionBuilder.kt +0 -33
- package/build/PermissionsInterface.d.ts +29 -0
- package/build/PermissionsInterface.d.ts.map +1 -1
- package/build/PermissionsInterface.js +9 -0
- package/build/PermissionsInterface.js.map +1 -1
- package/ios/ExpoModulesCore.podspec +2 -1
- package/ios/JSI/EXJSIInstaller.mm +2 -0
- package/ios/JSI/EXJSIUtils.h +1 -0
- package/ios/NativeModulesProxy/EXNativeModulesProxy.mm +4 -3
- package/ios/Swift/AppContext.swift +2 -4
- package/ios/Swift/Classes/ClassComponentElementsBuilder.swift +2 -2
- package/ios/Swift/Exceptions/ChainableException.swift +3 -3
- package/ios/Swift/ExpoBridgeModule.swift +16 -2
- package/ios/Swift/Logging/Logger.swift +3 -0
- package/ios/Swift/Promise.swift +5 -1
- package/package.json +2 -2
- 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(
|
|
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
|
-
|
|
68
|
-
|
|
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
|
-
|
|
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 <
|
|
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<
|
|
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<
|
|
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::
|
|
128
|
+
std::unordered_map<std::string, MethodMetadata> methodsMetadata;
|
|
113
129
|
|
|
114
130
|
/**
|
|
115
131
|
* A constants map.
|
|
116
132
|
*/
|
|
117
|
-
std::
|
|
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
|
-
|
|
93
|
-
|
|
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
|
-
|
|
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(
|
|
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
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
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
|
-
|
|
120
|
-
|
|
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
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
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
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
199
|
-
|
|
200
|
-
jmethodID
|
|
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
|
-
|
|
208
|
-
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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
|