expo-modules-core 0.9.0 → 0.10.0

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 (167) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/android/CMakeLists.txt +154 -0
  3. package/android/build.gradle +293 -5
  4. package/android/src/main/cpp/Exceptions.cpp +22 -0
  5. package/android/src/main/cpp/Exceptions.h +38 -0
  6. package/android/src/main/cpp/ExpoModulesHostObject.cpp +47 -0
  7. package/android/src/main/cpp/ExpoModulesHostObject.h +32 -0
  8. package/android/src/main/cpp/JNIFunctionBody.cpp +29 -0
  9. package/android/src/main/cpp/JNIFunctionBody.h +50 -0
  10. package/android/src/main/cpp/JNIInjector.cpp +19 -0
  11. package/android/src/main/cpp/JSIInteropModuleRegistry.cpp +122 -0
  12. package/android/src/main/cpp/JSIInteropModuleRegistry.h +96 -0
  13. package/android/src/main/cpp/JSIObjectWrapper.h +33 -0
  14. package/android/src/main/cpp/JSITypeConverter.h +84 -0
  15. package/android/src/main/cpp/JavaScriptModuleObject.cpp +138 -0
  16. package/android/src/main/cpp/JavaScriptModuleObject.h +122 -0
  17. package/android/src/main/cpp/JavaScriptObject.cpp +125 -0
  18. package/android/src/main/cpp/JavaScriptObject.h +131 -0
  19. package/android/src/main/cpp/JavaScriptRuntime.cpp +127 -0
  20. package/android/src/main/cpp/JavaScriptRuntime.h +87 -0
  21. package/android/src/main/cpp/JavaScriptValue.cpp +172 -0
  22. package/android/src/main/cpp/JavaScriptValue.h +78 -0
  23. package/android/src/main/cpp/MethodMetadata.cpp +230 -0
  24. package/android/src/main/cpp/MethodMetadata.h +92 -0
  25. package/android/src/main/java/expo/modules/adapters/react/NativeModulesProxy.java +2 -0
  26. package/android/src/main/java/expo/modules/core/errors/ContextDestroyedException.kt +7 -0
  27. package/android/src/main/java/expo/modules/interfaces/permissions/Permissions.java +30 -0
  28. package/android/src/main/java/expo/modules/kotlin/AppContext.kt +49 -1
  29. package/android/src/main/java/expo/modules/kotlin/ConcatIterator.kt +18 -0
  30. package/android/src/main/java/expo/modules/kotlin/KotlinInteropModuleRegistry.kt +15 -12
  31. package/android/src/main/java/expo/modules/kotlin/ModuleHolder.kt +39 -3
  32. package/android/src/main/java/expo/modules/kotlin/defaultmodules/ErrorManagerModule.kt +2 -2
  33. package/android/src/main/java/expo/modules/kotlin/exception/CodedException.kt +13 -0
  34. package/android/src/main/java/expo/modules/kotlin/exception/ExceptionDecorator.kt +2 -0
  35. package/android/src/main/java/expo/modules/kotlin/functions/AnyFunction.kt +19 -14
  36. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunction.kt +29 -7
  37. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionBuilder.kt +13 -13
  38. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionComponent.kt +18 -0
  39. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionWithPromiseComponent.kt +18 -0
  40. package/android/src/main/java/expo/modules/kotlin/functions/SuspendFunctionComponent.kt +56 -0
  41. package/android/src/main/java/expo/modules/kotlin/functions/SyncFunctionComponent.kt +28 -0
  42. package/android/src/main/java/expo/modules/kotlin/jni/CppType.kt +18 -0
  43. package/android/src/main/java/expo/modules/kotlin/jni/JNIFunctionBody.kt +39 -0
  44. package/android/src/main/java/expo/modules/kotlin/jni/JSIInteropModuleRegistry.kt +89 -0
  45. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptModuleObject.kt +44 -0
  46. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptObject.kt +113 -0
  47. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptValue.kt +35 -0
  48. package/android/src/main/java/expo/modules/kotlin/modules/Module.kt +15 -5
  49. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionBuilder.kt +65 -111
  50. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionData.kt +35 -2
  51. package/android/src/main/java/expo/modules/kotlin/providers/AppContextProvider.kt +14 -0
  52. package/android/src/main/java/expo/modules/kotlin/providers/CurrentActivityProvider.kt +22 -0
  53. package/android/src/main/java/expo/modules/kotlin/records/RecordTypeConverter.kt +19 -2
  54. package/android/src/main/java/expo/modules/kotlin/types/AnyType.kt +3 -2
  55. package/android/src/main/java/expo/modules/kotlin/types/ArrayTypeConverter.kt +7 -2
  56. package/android/src/main/java/expo/modules/kotlin/types/BasicTypeConverters.kt +68 -20
  57. package/android/src/main/java/expo/modules/kotlin/types/EnumTypeConverter.kt +50 -22
  58. package/android/src/main/java/expo/modules/kotlin/types/ListTypeConverter.kt +18 -2
  59. package/android/src/main/java/expo/modules/kotlin/types/MapTypeConverter.kt +18 -2
  60. package/android/src/main/java/expo/modules/kotlin/types/PairTypeConverter.kt +17 -2
  61. package/android/src/main/java/expo/modules/kotlin/types/TypeConverter.kt +43 -3
  62. package/android/src/main/java/expo/modules/kotlin/types/TypeConverterProvider.kt +5 -0
  63. package/build/NativeModulesProxy.native.d.ts.map +1 -1
  64. package/build/NativeModulesProxy.native.js +9 -3
  65. package/build/NativeModulesProxy.native.js.map +1 -1
  66. package/ios/AppDelegates/EXAppDelegatesLoader.m +1 -2
  67. package/ios/ExpoModulesCore.podspec +1 -1
  68. package/ios/JSI/EXJSIConversions.mm +6 -0
  69. package/ios/JSI/EXJSIInstaller.h +15 -21
  70. package/ios/JSI/EXJSIInstaller.mm +39 -3
  71. package/ios/JSI/EXJSIUtils.h +47 -3
  72. package/ios/JSI/EXJSIUtils.mm +88 -4
  73. package/ios/JSI/EXJavaScriptObject.h +11 -18
  74. package/ios/JSI/EXJavaScriptObject.mm +37 -18
  75. package/ios/JSI/EXJavaScriptRuntime.h +43 -9
  76. package/ios/JSI/EXJavaScriptRuntime.mm +70 -27
  77. package/ios/JSI/EXJavaScriptTypedArray.h +30 -0
  78. package/ios/JSI/EXJavaScriptTypedArray.mm +29 -0
  79. package/ios/JSI/EXJavaScriptValue.h +3 -2
  80. package/ios/JSI/EXJavaScriptValue.mm +17 -20
  81. package/ios/JSI/EXJavaScriptWeakObject.h +23 -0
  82. package/ios/JSI/EXJavaScriptWeakObject.mm +53 -0
  83. package/ios/JSI/EXObjectDeallocator.h +27 -0
  84. package/ios/JSI/ExpoModulesHostObject.h +3 -3
  85. package/ios/JSI/ExpoModulesHostObject.mm +4 -4
  86. package/ios/JSI/JavaScriptRuntime.swift +38 -1
  87. package/ios/JSI/JavaScriptValue.swift +7 -0
  88. package/ios/JSI/TypedArray.cpp +67 -0
  89. package/ios/JSI/TypedArray.h +46 -0
  90. package/ios/ModuleRegistryAdapter/EXModuleRegistryAdapter.m +0 -11
  91. package/ios/NativeModulesProxy/EXNativeModulesProxy.h +17 -10
  92. package/ios/NativeModulesProxy/EXNativeModulesProxy.mm +88 -77
  93. package/ios/NativeModulesProxy/NativeModulesProxyModule.swift +17 -0
  94. package/ios/Services/EXReactNativeEventEmitter.h +2 -2
  95. package/ios/Services/EXReactNativeEventEmitter.m +11 -6
  96. package/ios/Swift/AppContext.swift +208 -28
  97. package/ios/Swift/Arguments/AnyArgument.swift +18 -0
  98. package/ios/Swift/Arguments/{Types/EnumArgumentType.swift → EnumArgument.swift} +2 -17
  99. package/ios/Swift/Classes/ClassComponent.swift +95 -0
  100. package/ios/Swift/Classes/ClassComponentElement.swift +33 -0
  101. package/ios/Swift/Classes/ClassComponentElementsBuilder.swift +34 -0
  102. package/ios/Swift/Classes/ClassComponentFactories.swift +96 -0
  103. package/ios/Swift/DynamicTypes/AnyDynamicType.swift +44 -0
  104. package/ios/Swift/DynamicTypes/DynamicArrayType.swift +56 -0
  105. package/ios/Swift/DynamicTypes/DynamicConvertibleType.swift +27 -0
  106. package/ios/Swift/DynamicTypes/DynamicEnumType.swift +27 -0
  107. package/ios/Swift/DynamicTypes/DynamicOptionalType.swift +63 -0
  108. package/ios/Swift/DynamicTypes/DynamicRawType.swift +33 -0
  109. package/ios/Swift/DynamicTypes/DynamicSharedObjectType.swift +37 -0
  110. package/ios/Swift/DynamicTypes/DynamicType.swift +39 -0
  111. package/ios/Swift/DynamicTypes/DynamicTypedArrayType.swift +46 -0
  112. package/ios/Swift/Exceptions/CodedError.swift +1 -1
  113. package/ios/Swift/Exceptions/Exception.swift +8 -6
  114. package/ios/Swift/Exceptions/UnexpectedException.swift +2 -1
  115. package/ios/Swift/ExpoBridgeModule.m +5 -0
  116. package/ios/Swift/ExpoBridgeModule.swift +65 -0
  117. package/ios/Swift/Functions/AnyFunction.swift +33 -31
  118. package/ios/Swift/Functions/AsyncFunctionComponent.swift +196 -59
  119. package/ios/Swift/Functions/SyncFunctionComponent.swift +142 -58
  120. package/ios/Swift/JavaScriptUtils.swift +32 -57
  121. package/ios/Swift/Logging/LogHandlers.swift +39 -0
  122. package/ios/Swift/Logging/LogType.swift +62 -0
  123. package/ios/Swift/Logging/Logger.swift +198 -0
  124. package/ios/Swift/ModuleHolder.swift +19 -54
  125. package/ios/Swift/ModuleRegistry.swift +7 -1
  126. package/ios/Swift/Modules/AnyModule.swift +3 -3
  127. package/ios/Swift/ModulesProvider.swift +2 -0
  128. package/ios/Swift/Objects/JavaScriptObjectBuilder.swift +37 -0
  129. package/ios/Swift/Objects/ObjectDefinition.swift +74 -1
  130. package/ios/Swift/Objects/ObjectDefinitionComponents.swift +77 -68
  131. package/ios/Swift/Objects/PropertyComponent.swift +147 -0
  132. package/ios/Swift/Promise.swift +12 -3
  133. package/ios/Swift/Records/Field.swift +2 -2
  134. package/ios/Swift/SharedObjects/SharedObject.swift +20 -0
  135. package/ios/Swift/SharedObjects/SharedObjectRegistry.swift +129 -0
  136. package/ios/Swift/TypedArrays/AnyTypedArray.swift +11 -0
  137. package/ios/Swift/TypedArrays/ConcreteTypedArrays.swift +56 -0
  138. package/ios/Swift/TypedArrays/GenericTypedArray.swift +49 -0
  139. package/ios/Swift/TypedArrays/TypedArray.swift +80 -0
  140. package/ios/Swift/Utilities.swift +28 -0
  141. package/ios/Swift/Views/ConcreteViewProp.swift +3 -3
  142. package/ios/Swift/Views/ViewManagerDefinitionComponents.swift +2 -2
  143. package/ios/Tests/ClassComponentSpec.swift +210 -0
  144. package/ios/Tests/DynamicTypeSpec.swift +336 -0
  145. package/ios/Tests/EnumArgumentSpec.swift +48 -0
  146. package/ios/Tests/ExpoModulesSpec.swift +17 -3
  147. package/ios/Tests/FunctionSpec.swift +167 -118
  148. package/ios/Tests/Mocks/ModuleMocks.swift +1 -1
  149. package/ios/Tests/PropertyComponentSpec.swift +95 -0
  150. package/ios/Tests/SharedObjectRegistrySpec.swift +109 -0
  151. package/ios/Tests/TypedArraysSpec.swift +136 -0
  152. package/package.json +2 -2
  153. package/src/NativeModulesProxy.native.ts +13 -3
  154. package/src/ts-declarations/ExpoModules.d.ts +7 -0
  155. package/tsconfig.json +1 -1
  156. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionWithPromise.kt +0 -15
  157. package/android/src/main/java/expo/modules/kotlin/functions/AsyncSuspendFunction.kt +0 -36
  158. package/ios/Swift/Arguments/AnyArgumentType.swift +0 -13
  159. package/ios/Swift/Arguments/ArgumentType.swift +0 -28
  160. package/ios/Swift/Arguments/Types/ArrayArgumentType.swift +0 -42
  161. package/ios/Swift/Arguments/Types/ConvertibleArgumentType.swift +0 -16
  162. package/ios/Swift/Arguments/Types/OptionalArgumentType.swift +0 -49
  163. package/ios/Swift/Arguments/Types/PromiseArgumentType.swift +0 -15
  164. package/ios/Swift/Arguments/Types/RawArgumentType.swift +0 -25
  165. package/ios/Swift/Functions/ConcreteFunction.swift +0 -103
  166. package/ios/Swift/SwiftInteropBridge.swift +0 -155
  167. package/ios/Tests/ArgumentTypeSpec.swift +0 -143
@@ -0,0 +1,29 @@
1
+ // Copyright © 2021-present 650 Industries, Inc. (aka Expo)
2
+
3
+ #include "JNIFunctionBody.h"
4
+
5
+ namespace jni = facebook::jni;
6
+ namespace react = facebook::react;
7
+
8
+ namespace expo {
9
+ jni::local_ref<react::ReadableNativeArray::javaobject>
10
+ JNIFunctionBody::invoke(react::ReadableNativeArray::javaobject &&args) {
11
+ static const auto method = getClass()->getMethod<
12
+ react::ReadableNativeArray::javaobject(react::ReadableNativeArray::javaobject)
13
+ >(
14
+ "invoke"
15
+ );
16
+
17
+ return method(this->self(), args);
18
+ }
19
+
20
+ void JNIAsyncFunctionBody::invoke(react::ReadableNativeArray::javaobject &&args, jobject promise) {
21
+ static const auto method = getClass()->getMethod<
22
+ void(react::ReadableNativeArray::javaobject, jobject)
23
+ >(
24
+ "invoke"
25
+ );
26
+
27
+ method(this->self(), args, promise);
28
+ }
29
+ } // namespace expo
@@ -0,0 +1,50 @@
1
+ // Copyright © 2021-present 650 Industries, Inc. (aka Expo)
2
+
3
+ #pragma once
4
+
5
+ #include <fbjni/fbjni.h>
6
+ #include <react/jni/ReadableNativeArray.h>
7
+
8
+ namespace jni = facebook::jni;
9
+ namespace react = facebook::react;
10
+
11
+ namespace expo {
12
+ /**
13
+ * A CPP part of the expo.modules.kotlin.jni.JNIFunctionBody class.
14
+ * It represents the Kotlin's promise-less function.
15
+ */
16
+ class JNIFunctionBody : public jni::JavaClass<JNIFunctionBody> {
17
+ public:
18
+ static auto constexpr kJavaDescriptor = "Lexpo/modules/kotlin/jni/JNIFunctionBody;";
19
+
20
+ /**
21
+ * Invokes a Kotlin's implementation of this function.
22
+ *
23
+ * @param args
24
+ * @return result of the Kotlin function
25
+ */
26
+ jni::local_ref<react::ReadableNativeArray::javaobject> invoke(
27
+ react::ReadableNativeArray::javaobject &&args
28
+ );
29
+ };
30
+
31
+ /**
32
+ * A CPP part of the expo.modules.kotlin.jni.JNIAsyncFunctionBody class.
33
+ * It represents the Kotlin's promise function.
34
+ */
35
+ class JNIAsyncFunctionBody : public jni::JavaClass<JNIAsyncFunctionBody> {
36
+ public:
37
+ static auto constexpr kJavaDescriptor = "Lexpo/modules/kotlin/jni/JNIAsyncFunctionBody;";
38
+
39
+ /**
40
+ * Invokes a Kotlin's implementation of this async function.
41
+ *
42
+ * @param args
43
+ * @param promise that will be resolve or rejected in the Kotlin's implementation
44
+ */
45
+ void invoke(
46
+ react::ReadableNativeArray::javaobject &&args,
47
+ jobject promise
48
+ );
49
+ };
50
+ } // namespace expo
@@ -0,0 +1,19 @@
1
+ // Copyright © 2021-present 650 Industries, Inc. (aka Expo)
2
+
3
+ #include "JSIInteropModuleRegistry.h"
4
+ #include "JavaScriptModuleObject.h"
5
+ #include "JavaScriptValue.h"
6
+ #include "JavaScriptObject.h"
7
+
8
+ #include <jni.h>
9
+ #include <fbjni/fbjni.h>
10
+
11
+ // Install all jni bindings
12
+ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *) {
13
+ return facebook::jni::initialize(vm, [] {
14
+ expo::JSIInteropModuleRegistry::registerNatives();
15
+ expo::JavaScriptModuleObject::registerNatives();
16
+ expo::JavaScriptValue::registerNatives();
17
+ expo::JavaScriptObject::registerNatives();
18
+ });
19
+ }
@@ -0,0 +1,122 @@
1
+ // Copyright © 2021-present 650 Industries, Inc. (aka Expo)
2
+
3
+ #include "JSIInteropModuleRegistry.h"
4
+ #include "ExpoModulesHostObject.h"
5
+
6
+ #include <fbjni/detail/Meta.h>
7
+ #include <fbjni/fbjni.h>
8
+
9
+ #include <memory>
10
+
11
+ namespace jni = facebook::jni;
12
+ namespace jsi = facebook::jsi;
13
+
14
+ namespace expo {
15
+ jni::local_ref<JSIInteropModuleRegistry::jhybriddata>
16
+ JSIInteropModuleRegistry::initHybrid(jni::alias_ref<jhybridobject> jThis) {
17
+ return makeCxxInstance(jThis);
18
+ }
19
+
20
+ void JSIInteropModuleRegistry::registerNatives() {
21
+ registerHybrid({
22
+ makeNativeMethod("initHybrid", JSIInteropModuleRegistry::initHybrid),
23
+ makeNativeMethod("installJSI", JSIInteropModuleRegistry::installJSI),
24
+ makeNativeMethod("installJSIForTests",
25
+ JSIInteropModuleRegistry::installJSIForTests),
26
+ makeNativeMethod("evaluateScript", JSIInteropModuleRegistry::evaluateScript),
27
+ makeNativeMethod("global", JSIInteropModuleRegistry::global),
28
+ makeNativeMethod("createObject", JSIInteropModuleRegistry::createObject),
29
+ makeNativeMethod("drainJSEventLoop", JSIInteropModuleRegistry::drainJSEventLoop),
30
+ });
31
+ }
32
+
33
+ JSIInteropModuleRegistry::JSIInteropModuleRegistry(jni::alias_ref<jhybridobject> jThis)
34
+ : javaPart_(jni::make_global(jThis)) {}
35
+
36
+ void JSIInteropModuleRegistry::installJSI(
37
+ jlong jsRuntimePointer,
38
+ jni::alias_ref<react::CallInvokerHolder::javaobject> jsInvokerHolder,
39
+ jni::alias_ref<react::CallInvokerHolder::javaobject> nativeInvokerHolder
40
+ ) {
41
+ auto runtime = reinterpret_cast<jsi::Runtime *>(jsRuntimePointer);
42
+ runtimeHolder = std::make_shared<JavaScriptRuntime>(
43
+ runtime,
44
+ jsInvokerHolder->cthis()->getCallInvoker(),
45
+ nativeInvokerHolder->cthis()->getCallInvoker()
46
+ );
47
+
48
+ auto expoModules = std::make_shared<ExpoModulesHostObject>(this);
49
+ auto expoModulesObject = jsi::Object::createFromHostObject(*runtime, expoModules);
50
+
51
+ runtime
52
+ ->global()
53
+ .setProperty(
54
+ *runtime,
55
+ "ExpoModules",
56
+ std::move(expoModulesObject)
57
+ );
58
+ }
59
+
60
+ void JSIInteropModuleRegistry::installJSIForTests() {
61
+ runtimeHolder = std::make_shared<JavaScriptRuntime>();
62
+ jsi::Runtime &jsiRuntime = *runtimeHolder->get();
63
+
64
+ auto expoModules = std::make_shared<ExpoModulesHostObject>(this);
65
+ auto expoModulesObject = jsi::Object::createFromHostObject(jsiRuntime, expoModules);
66
+
67
+ jsiRuntime
68
+ .global()
69
+ .setProperty(
70
+ jsiRuntime,
71
+ "ExpoModules",
72
+ std::move(expoModulesObject)
73
+ );
74
+ }
75
+
76
+ jni::local_ref<JavaScriptModuleObject::javaobject>
77
+ JSIInteropModuleRegistry::callGetJavaScriptModuleObjectMethod(const std::string &moduleName) const {
78
+ const static auto method = expo::JSIInteropModuleRegistry::javaClassLocal()
79
+ ->getMethod<jni::local_ref<JavaScriptModuleObject::javaobject>(
80
+ std::string)>(
81
+ "getJavaScriptModuleObject"
82
+ );
83
+
84
+ return method(javaPart_, moduleName);
85
+ }
86
+
87
+ jni::local_ref<jni::JArrayClass<jni::JString>>
88
+ JSIInteropModuleRegistry::callGetJavaScriptModulesNames() const {
89
+ const static auto method = expo::JSIInteropModuleRegistry::javaClassLocal()
90
+ ->getMethod<jni::local_ref<jni::JArrayClass<jni::JString>>()>(
91
+ "getJavaScriptModulesName"
92
+ );
93
+ return method(javaPart_);
94
+ }
95
+
96
+ jni::local_ref<JavaScriptModuleObject::javaobject>
97
+ JSIInteropModuleRegistry::getModule(const std::string &moduleName) const {
98
+ return callGetJavaScriptModuleObjectMethod(moduleName);
99
+ }
100
+
101
+ jni::local_ref<jni::JArrayClass<jni::JString>> JSIInteropModuleRegistry::getModulesName() const {
102
+ return callGetJavaScriptModulesNames();
103
+ }
104
+
105
+ jni::local_ref<JavaScriptValue::javaobject> JSIInteropModuleRegistry::evaluateScript(
106
+ jni::JString script
107
+ ) {
108
+ return runtimeHolder->evaluateScript(script.toStdString());
109
+ }
110
+
111
+ jni::local_ref<JavaScriptObject::javaobject> JSIInteropModuleRegistry::global() {
112
+ return runtimeHolder->global();
113
+ }
114
+
115
+ jni::local_ref<JavaScriptObject::javaobject> JSIInteropModuleRegistry::createObject() {
116
+ return runtimeHolder->createObject();
117
+ }
118
+
119
+ void JSIInteropModuleRegistry::drainJSEventLoop() {
120
+ runtimeHolder->drainJSEventLoop();
121
+ }
122
+ } // namespace expo
@@ -0,0 +1,96 @@
1
+ // Copyright © 2021-present 650 Industries, Inc. (aka Expo)
2
+
3
+ #pragma once
4
+
5
+ #include "JavaScriptRuntime.h"
6
+ #include "JavaScriptModuleObject.h"
7
+ #include "JavaScriptValue.h"
8
+ #include "JavaScriptObject.h"
9
+
10
+ #include <fbjni/fbjni.h>
11
+ #include <jsi/jsi.h>
12
+ #include <ReactCommon/CallInvokerHolder.h>
13
+ #include <ReactCommon/CallInvoker.h>
14
+
15
+ #include <memory>
16
+
17
+ namespace jni = facebook::jni;
18
+ namespace jsi = facebook::jsi;
19
+ namespace react = facebook::react;
20
+
21
+ namespace expo {
22
+ /**
23
+ * A JNI wrapper to initialize CPP part of modules and access all data from the module registry.
24
+ */
25
+ class JSIInteropModuleRegistry : public jni::HybridClass<JSIInteropModuleRegistry> {
26
+ public:
27
+ static auto constexpr
28
+ kJavaDescriptor = "Lexpo/modules/kotlin/jni/JSIInteropModuleRegistry;";
29
+ static auto constexpr TAG = "JSIInteropModuleRegistry";
30
+
31
+ static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject> jThis);
32
+
33
+ static void registerNatives();
34
+
35
+ /**
36
+ * Initializes the `ExpoModulesHostObject` and adds it to the global object.
37
+ */
38
+ void installJSI(
39
+ jlong jsRuntimePointer,
40
+ jni::alias_ref<react::CallInvokerHolder::javaobject> jsInvokerHolder,
41
+ jni::alias_ref<react::CallInvokerHolder::javaobject> nativeInvokerHolder
42
+ );
43
+
44
+ /**
45
+ * Initializes the test runtime. Shouldn't be used in the production.
46
+ */
47
+ void installJSIForTests();
48
+
49
+ /**
50
+ * Gets a module for a given name. It will throw an exception if the module doesn't exist.
51
+ *
52
+ * @param moduleName
53
+ * @return An instance of `JavaScriptModuleObject`
54
+ */
55
+ jni::local_ref<JavaScriptModuleObject::javaobject> getModule(const std::string &moduleName) const;
56
+
57
+ /**
58
+ * Gets names of all available modules.
59
+ */
60
+ jni::local_ref<jni::JArrayClass<jni::JString>> getModulesName() const;
61
+
62
+ /**
63
+ * Exposes a `JavaScriptRuntime::evaluateScript` function to Kotlin
64
+ */
65
+ jni::local_ref<JavaScriptValue::javaobject> evaluateScript(jni::JString script);
66
+
67
+ /**
68
+ * Exposes a `JavaScriptRuntime::global` function to Kotlin
69
+ */
70
+ jni::local_ref<JavaScriptObject::javaobject> global();
71
+
72
+ /**
73
+ * Exposes a `JavaScriptRuntime::createObject` function to Kotlin
74
+ */
75
+ jni::local_ref<JavaScriptObject::javaobject> createObject();
76
+
77
+ /**
78
+ * Exposes a `JavaScriptRuntime::drainJSEventLoop` function to Kotlin
79
+ */
80
+ void drainJSEventLoop();
81
+
82
+ std::shared_ptr<react::CallInvoker> jsInvoker;
83
+ std::shared_ptr<react::CallInvoker> nativeInvoker;
84
+ std::shared_ptr<JavaScriptRuntime> runtimeHolder;
85
+ private:
86
+ friend HybridBase;
87
+ jni::global_ref<JSIInteropModuleRegistry::javaobject> javaPart_;
88
+
89
+ explicit JSIInteropModuleRegistry(jni::alias_ref<jhybridobject> jThis);
90
+
91
+ inline jni::local_ref<JavaScriptModuleObject::javaobject>
92
+ callGetJavaScriptModuleObjectMethod(const std::string &moduleName) const;
93
+
94
+ inline jni::local_ref<jni::JArrayClass<jni::JString>> callGetJavaScriptModulesNames() const;
95
+ };
96
+ } // namespace expo
@@ -0,0 +1,33 @@
1
+ // Copyright © 2021-present 650 Industries, Inc. (aka Expo)
2
+
3
+ #pragma once
4
+
5
+ #include <jsi/jsi.h>
6
+
7
+ #include <memory>
8
+
9
+ namespace jsi = facebook::jsi;
10
+
11
+ namespace expo {
12
+ /**
13
+ * An interface for classes which wrap jsi::Object.
14
+ */
15
+ class JSIObjectWrapper {
16
+ public:
17
+ /**
18
+ * @return a pointer to the underlying jsi::Object.
19
+ */
20
+ virtual std::shared_ptr<jsi::Object> get() = 0;
21
+ };
22
+
23
+ /**
24
+ * An interface for classes which wrap jsi::Value.
25
+ */
26
+ class JSIValueWrapper {
27
+ public:
28
+ /**
29
+ * @return a pointer to the underlying jsi::Value.
30
+ */
31
+ virtual std::shared_ptr<jsi::Value> get() = 0;
32
+ };
33
+ } // namespace expo
@@ -0,0 +1,84 @@
1
+ // Copyright © 2021-present 650 Industries, Inc. (aka Expo)
2
+
3
+ #pragma once
4
+
5
+ #include "JSIObjectWrapper.h"
6
+
7
+ #include <fbjni/fbjni.h>
8
+ #include <jsi/jsi.h>
9
+
10
+ #include <type_traits>
11
+
12
+ namespace jni = facebook::jni;
13
+ namespace jsi = facebook::jsi;
14
+
15
+ namespace expo {
16
+ /**
17
+ * A base template of the jni to jsi types converter.
18
+ * To make this conversion as fast and easy as possible we used the type trait technic.
19
+ */
20
+ template<class T, typename = void>
21
+ struct jsi_type_converter {
22
+ static const bool isDefined = false;
23
+ };
24
+
25
+ /**
26
+ * Conversion from jni::alias_ref<T::javaobject> to jsi::Value where T extends JSIValueWrapper or JSIObjectWrapper.
27
+ */
28
+ template<class T>
29
+ struct jsi_type_converter<
30
+ jni::alias_ref<T>,
31
+ std::enable_if_t<
32
+ // jni::ReprType<T>::HybridType>::value if T looks like `R::javaobject`, it will return R
33
+ std::is_base_of<JSIValueWrapper, typename jni::ReprType<T>::HybridType>::value ||
34
+ std::is_base_of<JSIObjectWrapper, typename jni::ReprType<T>::HybridType>::value
35
+ >
36
+ > {
37
+ static const bool isDefined = true;
38
+
39
+ inline static jsi::Value convert(
40
+ jsi::Runtime &runtime,
41
+ jni::alias_ref<T> &value) {
42
+ if (value == nullptr) {
43
+ return jsi::Value::undefined();
44
+ }
45
+ return jsi::Value(runtime, *value->cthis()->get());
46
+ }
47
+ };
48
+
49
+ /**
50
+ * Conversion from primitive types from which jsi::Value can be constructed (like bool, double) to jsi::Value.
51
+ */
52
+ template<class T>
53
+ struct jsi_type_converter<
54
+ T,
55
+ std::enable_if_t<std::is_fundamental_v<T> && std::is_constructible_v<jsi::Value, T>>
56
+ > {
57
+ static const bool isDefined = true;
58
+
59
+ inline static jsi::Value convert(jsi::Runtime &runtime, T value) {
60
+ return jsi::Value(value);
61
+ }
62
+ };
63
+
64
+ /**
65
+ * Conversion from jni::alias_ref<jstring> to jsi::Value.
66
+ */
67
+ template<>
68
+ struct jsi_type_converter<jni::alias_ref<jstring>> {
69
+ static const bool isDefined = true;
70
+
71
+ inline static jsi::Value convert(jsi::Runtime &runtime, jni::alias_ref<jstring> &value) {
72
+ if (value == nullptr) {
73
+ return jsi::Value::undefined();
74
+ }
75
+ return jsi::Value(jsi::String::createFromUtf8(runtime, value->toStdString()));
76
+ }
77
+ };
78
+
79
+ /**
80
+ * Helper that checks if the type converter was defined for the given type.
81
+ */
82
+ template<class T>
83
+ inline constexpr bool is_jsi_type_converter_defined = jsi_type_converter<T>::isDefined;
84
+ } // namespace expo
@@ -0,0 +1,138 @@
1
+ // Copyright © 2021-present 650 Industries, Inc. (aka Expo)
2
+
3
+ #include "JavaScriptModuleObject.h"
4
+ #include "JSIInteropModuleRegistry.h"
5
+
6
+ #include <folly/dynamic.h>
7
+ #include <jsi/JSIDynamic.h>
8
+ #include <react/jni/ReadableNativeArray.h>
9
+ #include <fbjni/detail/Hybrid.h>
10
+ #include <ReactCommon/TurboModuleUtils.h>
11
+ #include <jni/JCallback.h>
12
+ #include <jsi/JSIDynamic.h>
13
+ #include <fbjni/fbjni.h>
14
+ #include <jsi/jsi.h>
15
+
16
+ #include <utility>
17
+ #include <tuple>
18
+ #include <algorithm>
19
+
20
+ namespace jni = facebook::jni;
21
+ namespace jsi = facebook::jsi;
22
+ namespace react = facebook::react;
23
+
24
+ namespace expo {
25
+
26
+ jni::local_ref<jni::HybridClass<JavaScriptModuleObject>::jhybriddata>
27
+ JavaScriptModuleObject::initHybrid(jni::alias_ref<jhybridobject> jThis) {
28
+ return makeCxxInstance(jThis);
29
+ }
30
+
31
+ void JavaScriptModuleObject::registerNatives() {
32
+ registerHybrid({
33
+ makeNativeMethod("initHybrid", JavaScriptModuleObject::initHybrid),
34
+ makeNativeMethod("exportConstants", JavaScriptModuleObject::exportConstants),
35
+ makeNativeMethod("registerSyncFunction",
36
+ JavaScriptModuleObject::registerSyncFunction),
37
+ makeNativeMethod("registerAsyncFunction",
38
+ JavaScriptModuleObject::registerAsyncFunction),
39
+ });
40
+ }
41
+
42
+ std::shared_ptr<jsi::Object> JavaScriptModuleObject::getJSIObject(jsi::Runtime &runtime) {
43
+ if (jsiObject == nullptr) {
44
+ auto hostObject = std::make_shared<JavaScriptModuleObject::HostObject>(this);
45
+ jsiObject = std::make_shared<jsi::Object>(
46
+ jsi::Object::createFromHostObject(runtime, hostObject));
47
+ }
48
+
49
+ return jsiObject;
50
+ }
51
+
52
+ void JavaScriptModuleObject::exportConstants(jni::alias_ref<react::NativeMap::javaobject>
53
+ constants) {
54
+ auto dynamic = constants->cthis()->consume();
55
+ assert(dynamic.isObject());
56
+
57
+ for (const auto &[key, value]: dynamic.items()) {
58
+ this->constants[key.asString()] = value;
59
+ }
60
+ }
61
+
62
+ void JavaScriptModuleObject::registerSyncFunction(
63
+ jni::alias_ref<jstring> name,
64
+ jint args,
65
+ jni::alias_ref<JNIFunctionBody::javaobject> body
66
+ ) {
67
+ auto cName = name->toStdString();
68
+ methodsMetadata.try_emplace(cName, cName, args, false, jni::make_global(body));
69
+ }
70
+
71
+ void JavaScriptModuleObject::registerAsyncFunction(
72
+ jni::alias_ref<jstring> name,
73
+ jint args,
74
+ jni::alias_ref<JNIAsyncFunctionBody::javaobject> body
75
+ ) {
76
+ auto cName = name->toStdString();
77
+ methodsMetadata.try_emplace(cName, cName, args, true, jni::make_global(body));
78
+ }
79
+
80
+ JavaScriptModuleObject::HostObject::HostObject(
81
+ JavaScriptModuleObject *jsModule) : jsModule(jsModule) {}
82
+
83
+ jsi::Value JavaScriptModuleObject::HostObject::get(jsi::Runtime &runtime,
84
+ const jsi::PropNameID &name) {
85
+ auto cName = name.utf8(runtime);
86
+
87
+ auto constantsRecord = jsModule->constants.find(cName);
88
+ if (constantsRecord != jsModule->constants.end()) {
89
+ auto dynamic = constantsRecord->second;
90
+ return jsi::valueFromDynamic(runtime, dynamic);
91
+ }
92
+
93
+ auto metadataRecord = jsModule->methodsMetadata.find(cName);
94
+ if (metadataRecord == jsModule->methodsMetadata.end()) {
95
+ return jsi::Value::undefined();
96
+ }
97
+ auto &metadata = metadataRecord->second;
98
+ return jsi::Value(runtime, *metadata.toJSFunction(runtime, jsModule->jsiInteropModuleRegistry));
99
+ }
100
+
101
+ void JavaScriptModuleObject::HostObject::set(
102
+ jsi::Runtime &runtime,
103
+ const jsi::PropNameID &name,
104
+ const jsi::Value &value
105
+ ) {
106
+ throw jsi::JSError(
107
+ runtime,
108
+ "RuntimeError: Cannot override the host object for expo module '" + name.utf8(runtime) + "'"
109
+ );
110
+ }
111
+
112
+ std::vector<jsi::PropNameID> JavaScriptModuleObject::HostObject::getPropertyNames(
113
+ jsi::Runtime &rt
114
+ ) {
115
+ auto &metadata = jsModule->methodsMetadata;
116
+ std::vector<jsi::PropNameID> result;
117
+ std::transform(
118
+ metadata.begin(),
119
+ metadata.end(),
120
+ std::back_inserter(result),
121
+ [&rt](const auto &kv) {
122
+ return jsi::PropNameID::forUtf8(rt, kv.first);
123
+ }
124
+ );
125
+
126
+ auto &constants = jsModule->constants;
127
+ std::transform(
128
+ constants.begin(),
129
+ constants.end(),
130
+ std::back_inserter(result),
131
+ [&rt](const auto &kv) {
132
+ return jsi::PropNameID::forUtf8(rt, kv.first);
133
+ }
134
+ );
135
+
136
+ return result;
137
+ }
138
+ } // namespace expo
@@ -0,0 +1,122 @@
1
+ // Copyright © 2021-present 650 Industries, Inc. (aka Expo)
2
+
3
+ #pragma once
4
+
5
+ #include <fbjni/fbjni.h>
6
+ #include <jsi/jsi.h>
7
+ #include <react/jni/ReadableNativeArray.h>
8
+ #include <jni/JCallback.h>
9
+
10
+ #include <map>
11
+
12
+ #include "MethodMetadata.h"
13
+ #include "JNIFunctionBody.h"
14
+
15
+ namespace jni = facebook::jni;
16
+ namespace jsi = facebook::jsi;
17
+ namespace react = facebook::react;
18
+
19
+ namespace expo {
20
+ class JSIInteropModuleRegistry;
21
+
22
+ /**
23
+ * A CPP part of the module.
24
+ *
25
+ * Right now objects of this class are stored by the ModuleHolder to ensure they will live
26
+ * as long as the RN context.
27
+ */
28
+ class JavaScriptModuleObject : public jni::HybridClass<JavaScriptModuleObject> {
29
+ public:
30
+ static auto constexpr
31
+ kJavaDescriptor = "Lexpo/modules/kotlin/jni/JavaScriptModuleObject;";
32
+ static auto constexpr TAG = "JavaScriptModuleObject";
33
+
34
+ static jni::local_ref<jhybriddata> initHybrid(jni::alias_ref<jhybridobject> jThis);
35
+
36
+ static void registerNatives();
37
+
38
+ /**
39
+ * Pointer to the module registry interop.
40
+ */
41
+ JSIInteropModuleRegistry *jsiInteropModuleRegistry;
42
+
43
+ /**
44
+ * Returns a cached instance of jsi::Object representing this module.
45
+ * @param runtime
46
+ * @return Wrapped instance of JavaScriptModuleObject::HostObject
47
+ */
48
+ std::shared_ptr<jsi::Object> getJSIObject(jsi::Runtime &runtime);
49
+
50
+ /**
51
+ * Exports constants that will be assigned to the underlying HostObject.
52
+ */
53
+ void exportConstants(jni::alias_ref<react::NativeMap::javaobject> constants);
54
+
55
+ /**
56
+ * Registers a sync function.
57
+ * That function can be called via the `JavaScriptModuleObject.callSyncMethod` method.
58
+ */
59
+ void registerSyncFunction(
60
+ jni::alias_ref<jstring> name,
61
+ jint args,
62
+ jni::alias_ref<JNIFunctionBody::javaobject> JSIFunctionBody
63
+ );
64
+
65
+ /**
66
+ * Registers a async function.
67
+ * That function can be called via the `JavaScriptModuleObject.callAsyncMethod` method.
68
+ */
69
+ void registerAsyncFunction(
70
+ jni::alias_ref<jstring> name,
71
+ jint args,
72
+ jni::alias_ref<JNIAsyncFunctionBody::javaobject> JSIAsyncFunctionBody
73
+ );
74
+
75
+ /**
76
+ * An inner class of the `JavaScriptModuleObject` that is exported to the JS.
77
+ * It's an additional communication layer between JS and Kotlin.
78
+ * So the high-level view on accessing the exported function will look like this:
79
+ * `JS` --get function--> `JavaScriptModuleObject::HostObject` --access module metadata--> `JavaScriptModuleObject`
80
+ * --create JSI function--> `MethodMetadata`
81
+ *
82
+ * This abstraction wasn't necessary. However, it makes the management of ownership much easier -
83
+ * `JavaScriptModuleObject` is held by the ModuleHolder and `JavaScriptModuleObject::HostObject` is stored in the JS runtime.
84
+ * Without this distinction the `JavaScriptModuleObject` would have to turn into `HostObject` and `HybridObject` at the same time.
85
+ */
86
+ class HostObject : public jsi::HostObject {
87
+ public:
88
+ HostObject(JavaScriptModuleObject *);
89
+
90
+ jsi::Value get(jsi::Runtime &, const jsi::PropNameID &name) override;
91
+
92
+ void set(jsi::Runtime &, const jsi::PropNameID &name, const jsi::Value &value) override;
93
+
94
+ std::vector<jsi::PropNameID> getPropertyNames(jsi::Runtime &rt) override;
95
+
96
+ private:
97
+ JavaScriptModuleObject *jsModule;
98
+ };
99
+
100
+ private:
101
+ friend HybridBase;
102
+ /**
103
+ * A reference to the `JavaScriptModuleObject::HostObject`.
104
+ * Simple we cached that value to return the same object each time.
105
+ */
106
+ std::shared_ptr<jsi::Object> jsiObject = nullptr;
107
+ jni::global_ref<JavaScriptModuleObject::javaobject> javaPart_;
108
+
109
+ /**
110
+ * Metadata map that stores information about all available methods on this module.
111
+ */
112
+ std::map<std::string, MethodMetadata> methodsMetadata;
113
+
114
+ /**
115
+ * A constants map.
116
+ */
117
+ std::map<std::string, folly::dynamic> constants;
118
+
119
+ explicit JavaScriptModuleObject(jni::alias_ref<jhybridobject> jThis)
120
+ : javaPart_(jni::make_global(jThis)) {}
121
+ };
122
+ } // namespace expo