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
@@ -9,8 +9,8 @@ private const val onNewException = "ExpoModulesCoreErrorManager.onNewException"
9
9
 
10
10
  class ErrorManagerModule : Module() {
11
11
  override fun definition() = ModuleDefinition {
12
- name("ExpoModulesCoreErrorManager")
13
- events(onNewException)
12
+ Name("ExpoModulesCoreErrorManager")
13
+ Events(onNewException)
14
14
  }
15
15
 
16
16
  fun reportExceptionToLogBox(codedException: CodedException) {
@@ -1,6 +1,7 @@
1
1
  package expo.modules.kotlin.exception
2
2
 
3
3
  import com.facebook.react.bridge.ReadableType
4
+ import expo.modules.core.interfaces.DoNotStrip
4
5
  import java.util.*
5
6
  import kotlin.reflect.KProperty1
6
7
  import kotlin.reflect.KType
@@ -8,6 +9,7 @@ import kotlin.reflect.KType
8
9
  /**
9
10
  * A class for errors specifying its `code` and providing the `description`.
10
11
  */
12
+ @DoNotStrip
11
13
  open class CodedException(
12
14
  message: String?,
13
15
  cause: Throwable?
@@ -152,3 +154,14 @@ internal class CollectionElementCastException(
152
154
  message = "Cannot cast '${providedType.name}' to '$elementType' required by the collection of type: '$collectionType'.",
153
155
  cause
154
156
  )
157
+
158
+ @DoNotStrip
159
+ class JavaScriptEvaluateException(
160
+ message: String,
161
+ val jsStack: String
162
+ ) : CodedException(
163
+ message = """
164
+ Cannot evaluate JavaScript code: $message.
165
+ $jsStack
166
+ """.trimIndent()
167
+ )
@@ -5,6 +5,8 @@ internal inline fun <T> exceptionDecorator(decoratorBlock: (e: CodedException) -
5
5
  block()
6
6
  } catch (e: CodedException) {
7
7
  throw decoratorBlock(e)
8
+ } catch (e: expo.modules.core.errors.CodedException) {
9
+ throw decoratorBlock(CodedException(e.code, e.message, e.cause))
8
10
  } catch (e: Throwable) {
9
11
  throw decoratorBlock(UnexpectedException(e))
10
12
  }
@@ -1,37 +1,37 @@
1
1
  package expo.modules.kotlin.functions
2
2
 
3
3
  import com.facebook.react.bridge.ReadableArray
4
- import expo.modules.kotlin.ModuleHolder
5
- import expo.modules.kotlin.Promise
4
+ import expo.modules.kotlin.AppContext
6
5
  import expo.modules.kotlin.exception.ArgumentCastException
7
6
  import expo.modules.kotlin.exception.CodedException
8
7
  import expo.modules.kotlin.exception.InvalidArgsNumberException
9
8
  import expo.modules.kotlin.exception.exceptionDecorator
10
9
  import expo.modules.kotlin.iterator
10
+ import expo.modules.kotlin.jni.JavaScriptModuleObject
11
11
  import expo.modules.kotlin.recycle
12
12
  import expo.modules.kotlin.types.AnyType
13
13
 
14
+ /**
15
+ * Base class of all exported functions
16
+ */
14
17
  abstract class AnyFunction(
15
18
  protected val name: String,
16
19
  private val desiredArgsTypes: Array<AnyType>
17
20
  ) {
21
+ internal val argsCount get() = desiredArgsTypes.size
22
+
23
+ /**
24
+ * Tries to convert arguments from RN representation to expected types.
25
+ *
26
+ * @return An array of converted arguments
27
+ * @throws `CodedException` if conversion isn't possible
28
+ */
18
29
  @Throws(CodedException::class)
19
- fun call(module: ModuleHolder, args: ReadableArray, promise: Promise) {
30
+ protected fun convertArgs(args: ReadableArray): Array<out Any?> {
20
31
  if (desiredArgsTypes.size != args.size()) {
21
32
  throw InvalidArgsNumberException(args.size(), desiredArgsTypes.size)
22
33
  }
23
34
 
24
- val convertedArgs = convertArgs(args)
25
- callImplementation(module, convertedArgs, promise)
26
- }
27
-
28
- @Throws(CodedException::class)
29
- internal abstract fun callImplementation(holder: ModuleHolder, args: Array<out Any?>, promise: Promise)
30
-
31
- val argsCount get() = desiredArgsTypes.size
32
-
33
- @Throws(CodedException::class)
34
- private fun convertArgs(args: ReadableArray): Array<out Any?> {
35
35
  val finalArgs = Array<Any?>(desiredArgsTypes.size) { null }
36
36
  val argIterator = args.iterator()
37
37
  desiredArgsTypes
@@ -47,4 +47,9 @@ abstract class AnyFunction(
47
47
  }
48
48
  return finalArgs
49
49
  }
50
+
51
+ /**
52
+ * Attaches current function to the provided js object.
53
+ */
54
+ abstract fun attachToJSObject(appContext: AppContext, jsObject: JavaScriptModuleObject)
50
55
  }
@@ -1,15 +1,37 @@
1
1
  package expo.modules.kotlin.functions
2
2
 
3
- import expo.modules.kotlin.ModuleHolder
3
+ import com.facebook.react.bridge.ReadableArray
4
+ import expo.modules.kotlin.AppContext
5
+ import expo.modules.kotlin.KPromiseWrapper
4
6
  import expo.modules.kotlin.Promise
7
+ import expo.modules.kotlin.exception.CodedException
8
+ import expo.modules.kotlin.exception.UnexpectedException
9
+ import expo.modules.kotlin.jni.JavaScriptModuleObject
5
10
  import expo.modules.kotlin.types.AnyType
11
+ import kotlinx.coroutines.launch
6
12
 
7
- class AsyncFunction(
13
+ /**
14
+ * Base class of all function components that require a promise to be called.
15
+ */
16
+ abstract class AsyncFunction(
8
17
  name: String,
9
- argsType: Array<AnyType>,
10
- private val body: (args: Array<out Any?>) -> Any?
11
- ) : AnyFunction(name, argsType) {
12
- override fun callImplementation(holder: ModuleHolder, args: Array<out Any?>, promise: Promise) {
13
- promise.resolve(body(args))
18
+ desiredArgsTypes: Array<AnyType>
19
+ ) : AnyFunction(name, desiredArgsTypes) {
20
+ @Throws(CodedException::class)
21
+ abstract fun call(args: ReadableArray, promise: Promise)
22
+
23
+ override fun attachToJSObject(appContext: AppContext, jsObject: JavaScriptModuleObject) {
24
+ jsObject.registerAsyncFunction(name, argsCount) { args, bridgePromise ->
25
+ val kotlinPromise = KPromiseWrapper(bridgePromise as com.facebook.react.bridge.Promise)
26
+ appContext.modulesQueue.launch {
27
+ try {
28
+ call(args, kotlinPromise)
29
+ } catch (e: CodedException) {
30
+ kotlinPromise.reject(e)
31
+ } catch (e: Throwable) {
32
+ kotlinPromise.reject(UnexpectedException(e))
33
+ }
34
+ }
35
+ }
14
36
  }
15
37
  }
@@ -6,9 +6,9 @@ package expo.modules.kotlin.functions
6
6
  import expo.modules.kotlin.types.toAnyType
7
7
  import kotlin.reflect.typeOf
8
8
 
9
- class AsyncFunctionBuilder(val name: String) {
9
+ class AsyncFunctionBuilder(@PublishedApi internal val name: String) {
10
10
  @PublishedApi
11
- internal var function: AnyFunction? = null
11
+ internal var functionBuilder: SuspendFunctionComponentBuilder? = null
12
12
 
13
13
  @Deprecated(
14
14
  message = "The 'suspendBody' component was renamed to 'SuspendBody'.",
@@ -17,7 +17,7 @@ class AsyncFunctionBuilder(val name: String) {
17
17
  inline fun <reified R> suspendBody(crossinline block: suspend () -> R) = SuspendBody(block)
18
18
 
19
19
  inline fun <reified R> SuspendBody(crossinline block: suspend () -> R) {
20
- function = AsyncSuspendFunction(name, arrayOf()) { block() }
20
+ functionBuilder = SuspendFunctionComponentBuilder(name, arrayOf()) { block() }
21
21
  }
22
22
 
23
23
  @Deprecated(
@@ -27,7 +27,7 @@ class AsyncFunctionBuilder(val name: String) {
27
27
  inline fun <reified R, reified P0> suspendBody(crossinline block: suspend (p0: P0) -> R) = SuspendBody(block)
28
28
 
29
29
  inline fun <reified R, reified P0> SuspendBody(crossinline block: suspend (p0: P0) -> R) {
30
- function = AsyncSuspendFunction(name, arrayOf(typeOf<P0>().toAnyType())) { block(it[0] as P0) }
30
+ functionBuilder = SuspendFunctionComponentBuilder(name, arrayOf(typeOf<P0>().toAnyType())) { block(it[0] as P0) }
31
31
  }
32
32
 
33
33
  @Deprecated(
@@ -37,7 +37,7 @@ class AsyncFunctionBuilder(val name: String) {
37
37
  inline fun <reified R, reified P0, reified P1> suspendBody(crossinline block: suspend (p0: P0, p1: P1) -> R) = SuspendBody(block)
38
38
 
39
39
  inline fun <reified R, reified P0, reified P1> SuspendBody(crossinline block: suspend (p0: P0, p1: P1) -> R) {
40
- function = AsyncSuspendFunction(name, arrayOf(typeOf<P0>().toAnyType())) { block(it[0] as P0, it[0] as P1) }
40
+ functionBuilder = SuspendFunctionComponentBuilder(name, arrayOf(typeOf<P0>().toAnyType())) { block(it[0] as P0, it[0] as P1) }
41
41
  }
42
42
 
43
43
  @Deprecated(
@@ -47,7 +47,7 @@ class AsyncFunctionBuilder(val name: String) {
47
47
  inline fun <reified R, reified P0, reified P1, reified P2> suspendBody(crossinline block: suspend (p0: P0, p1: P1, p2: P2) -> R) = SuspendBody(block)
48
48
 
49
49
  inline fun <reified R, reified P0, reified P1, reified P2> SuspendBody(crossinline block: suspend (p0: P0, p1: P1, p2: P2) -> R) {
50
- function = AsyncSuspendFunction(name, arrayOf(typeOf<P0>().toAnyType())) { block(it[0] as P0, it[1] as P1, it[2] as P2) }
50
+ functionBuilder = SuspendFunctionComponentBuilder(name, arrayOf(typeOf<P0>().toAnyType())) { block(it[0] as P0, it[1] as P1, it[2] as P2) }
51
51
  }
52
52
 
53
53
  @Deprecated(
@@ -57,7 +57,7 @@ class AsyncFunctionBuilder(val name: String) {
57
57
  inline fun <reified R, reified P0, reified P1, reified P2, reified P3> suspendBody(crossinline block: suspend (p0: P0, p1: P1, p2: P2, p3: P3) -> R) = SuspendBody(block)
58
58
 
59
59
  inline fun <reified R, reified P0, reified P1, reified P2, reified P3> SuspendBody(crossinline block: suspend (p0: P0, p1: P1, p2: P2, p3: P3) -> R) {
60
- function = AsyncSuspendFunction(name, arrayOf(typeOf<P0>().toAnyType())) { block(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3) }
60
+ functionBuilder = SuspendFunctionComponentBuilder(name, arrayOf(typeOf<P0>().toAnyType())) { block(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3) }
61
61
  }
62
62
 
63
63
  @Deprecated(
@@ -67,7 +67,7 @@ class AsyncFunctionBuilder(val name: String) {
67
67
  inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4> suspendBody(crossinline block: suspend (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4) -> R) = SuspendBody(block)
68
68
 
69
69
  inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4> SuspendBody(crossinline block: suspend (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4) -> R) {
70
- function = AsyncSuspendFunction(name, arrayOf(typeOf<P0>().toAnyType())) { block(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3, it[4] as P4) }
70
+ functionBuilder = SuspendFunctionComponentBuilder(name, arrayOf(typeOf<P0>().toAnyType())) { block(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3, it[4] as P4) }
71
71
  }
72
72
 
73
73
  @Deprecated(
@@ -77,7 +77,7 @@ class AsyncFunctionBuilder(val name: String) {
77
77
  inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4, reified P5> suspendBody(crossinline block: suspend (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) -> R) = SuspendBody(block)
78
78
 
79
79
  inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4, reified P5> SuspendBody(crossinline block: suspend (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) -> R) {
80
- function = AsyncSuspendFunction(name, arrayOf(typeOf<P0>().toAnyType())) { block(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3, it[4] as P4, it[5] as P5) }
80
+ functionBuilder = SuspendFunctionComponentBuilder(name, arrayOf(typeOf<P0>().toAnyType())) { block(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3, it[4] as P4, it[5] as P5) }
81
81
  }
82
82
 
83
83
  @Deprecated(
@@ -87,7 +87,7 @@ class AsyncFunctionBuilder(val name: String) {
87
87
  inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4, reified P5, reified P6> suspendBody(crossinline block: suspend (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6) -> R) = SuspendBody(block)
88
88
 
89
89
  inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4, reified P5, reified P6> SuspendBody(crossinline block: suspend (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6) -> R) {
90
- function = AsyncSuspendFunction(name, arrayOf(typeOf<P0>().toAnyType())) { block(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3, it[4] as P4, it[5] as P5, it[6] as P6) }
90
+ functionBuilder = SuspendFunctionComponentBuilder(name, arrayOf(typeOf<P0>().toAnyType())) { block(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3, it[4] as P4, it[5] as P5, it[6] as P6) }
91
91
  }
92
92
 
93
93
  @Deprecated(
@@ -97,11 +97,11 @@ class AsyncFunctionBuilder(val name: String) {
97
97
  inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4, reified P5, reified P6, reified P7> suspendBody(crossinline block: suspend (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7) -> R) = SuspendBody(block)
98
98
 
99
99
  inline fun <reified R, reified P0, reified P1, reified P2, reified P3, reified P4, reified P5, reified P6, reified P7> SuspendBody(crossinline block: suspend (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7) -> R) {
100
- function = AsyncSuspendFunction(name, arrayOf(typeOf<P0>().toAnyType())) { block(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3, it[4] as P4, it[5] as P5, it[6] as P6, it[7] as P7) }
100
+ functionBuilder = SuspendFunctionComponentBuilder(name, arrayOf(typeOf<P0>().toAnyType())) { block(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3, it[4] as P4, it[5] as P5, it[6] as P6, it[7] as P7) }
101
101
  }
102
102
 
103
- internal fun build(): Pair<String, AnyFunction> {
104
- return name to requireNotNull(function)
103
+ internal fun build(): SuspendFunctionComponentBuilder {
104
+ return requireNotNull(functionBuilder)
105
105
  }
106
106
  }
107
107
 
@@ -0,0 +1,18 @@
1
+ package expo.modules.kotlin.functions
2
+
3
+ import com.facebook.react.bridge.ReadableArray
4
+ import expo.modules.kotlin.Promise
5
+ import expo.modules.kotlin.exception.CodedException
6
+ import expo.modules.kotlin.types.AnyType
7
+
8
+ class AsyncFunctionComponent(
9
+ name: String,
10
+ desiredArgsTypes: Array<AnyType>,
11
+ private val body: (args: Array<out Any?>) -> Any?
12
+ ) : AsyncFunction(name, desiredArgsTypes) {
13
+ @Throws(CodedException::class)
14
+ override fun call(args: ReadableArray, promise: Promise) {
15
+ val convertedArgs = convertArgs(args)
16
+ promise.resolve(body(convertedArgs))
17
+ }
18
+ }
@@ -0,0 +1,18 @@
1
+ package expo.modules.kotlin.functions
2
+
3
+ import com.facebook.react.bridge.ReadableArray
4
+ import expo.modules.kotlin.Promise
5
+ import expo.modules.kotlin.exception.CodedException
6
+ import expo.modules.kotlin.types.AnyType
7
+
8
+ class AsyncFunctionWithPromiseComponent(
9
+ name: String,
10
+ desiredArgsTypes: Array<AnyType>,
11
+ private val body: (args: Array<out Any?>, promise: Promise) -> Unit
12
+ ) : AsyncFunction(name, desiredArgsTypes) {
13
+ @Throws(CodedException::class)
14
+ override fun call(args: ReadableArray, promise: Promise) {
15
+ val convertedArgs = convertArgs(args)
16
+ body(convertedArgs, promise)
17
+ }
18
+ }
@@ -0,0 +1,56 @@
1
+ package expo.modules.kotlin.functions
2
+
3
+ import com.facebook.react.bridge.ReadableArray
4
+ import expo.modules.kotlin.ModuleHolder
5
+ import expo.modules.kotlin.Promise
6
+ import expo.modules.kotlin.exception.CodedException
7
+ import expo.modules.kotlin.exception.FunctionCallException
8
+ import expo.modules.kotlin.exception.UnexpectedException
9
+ import expo.modules.kotlin.exception.exceptionDecorator
10
+ import expo.modules.kotlin.types.AnyType
11
+ import kotlinx.coroutines.CoroutineScope
12
+ import kotlinx.coroutines.isActive
13
+ import kotlinx.coroutines.launch
14
+ import java.lang.ref.WeakReference
15
+ import kotlin.jvm.Throws
16
+
17
+ /**
18
+ * We can't construct a `SuspendFunctionComponent` in the build phase, because it has to have access to module coroutine scope.
19
+ * So we create another builder to store needed information and build it later - during the holder initialization.
20
+ */
21
+ class SuspendFunctionComponentBuilder(
22
+ internal val name: String,
23
+ private val desiredArgsTypes: Array<AnyType>,
24
+ private val body: suspend CoroutineScope.(args: Array<out Any?>) -> Any?,
25
+ ) {
26
+ fun build(moduleHolder: ModuleHolder) =
27
+ SuspendFunctionComponent(name, desiredArgsTypes, WeakReference(moduleHolder), body)
28
+ }
29
+
30
+ class SuspendFunctionComponent(
31
+ name: String,
32
+ desiredArgsTypes: Array<AnyType>,
33
+ private val moduleHolderRef: WeakReference<ModuleHolder>,
34
+ private val body: suspend CoroutineScope.(args: Array<out Any?>) -> Any?
35
+ ) : AsyncFunction(name, desiredArgsTypes) {
36
+ @Throws(CodedException::class)
37
+ override fun call(args: ReadableArray, promise: Promise) {
38
+ val holder = moduleHolderRef.get() ?: return
39
+ val scope = holder.module.coroutineScopeDelegate.value
40
+ val convertedArgs = convertArgs(args)
41
+ scope.launch {
42
+ try {
43
+ val result = exceptionDecorator({ cause -> FunctionCallException(name, holder.name, cause) }) {
44
+ body.invoke(scope, convertedArgs)
45
+ }
46
+ if (isActive) {
47
+ promise.resolve(result)
48
+ }
49
+ } catch (e: CodedException) {
50
+ promise.reject(e)
51
+ } catch (e: Throwable) {
52
+ promise.reject(UnexpectedException(e))
53
+ }
54
+ }
55
+ }
56
+ }
@@ -0,0 +1,28 @@
1
+ package expo.modules.kotlin.functions
2
+
3
+ import com.facebook.react.bridge.Arguments
4
+ import com.facebook.react.bridge.ReadableArray
5
+ import expo.modules.kotlin.AppContext
6
+ import expo.modules.kotlin.exception.CodedException
7
+ import expo.modules.kotlin.jni.JavaScriptModuleObject
8
+ import expo.modules.kotlin.types.AnyType
9
+ import expo.modules.kotlin.types.JSTypeConverter
10
+
11
+ class SyncFunctionComponent(
12
+ name: String,
13
+ desiredArgsTypes: Array<AnyType>,
14
+ private val body: (args: Array<out Any?>) -> Any?
15
+ ) : AnyFunction(name, desiredArgsTypes) {
16
+ @Throws(CodedException::class)
17
+ fun call(args: ReadableArray): Any? {
18
+ return body(convertArgs(args))
19
+ }
20
+
21
+ override fun attachToJSObject(appContext: AppContext, jsObject: JavaScriptModuleObject) {
22
+ jsObject.registerSyncFunction(name, argsCount) { args ->
23
+ val result = call(args)
24
+ val convertedResult = JSTypeConverter.convertToJSValue(result)
25
+ return@registerSyncFunction Arguments.fromJavaArgs(arrayOf(convertedResult))
26
+ }
27
+ }
28
+ }
@@ -0,0 +1,18 @@
1
+ package expo.modules.kotlin.jni
2
+
3
+ private var nextValue = 0
4
+
5
+ private fun nextValue(): Int = (1 shl nextValue).also { nextValue++ }
6
+
7
+ /**
8
+ * Enum that represents Cpp types. Objects of those types can be obtained via JNI.
9
+ */
10
+ enum class CppType(val value: Int) {
11
+ DOUBLE(nextValue()),
12
+ BOOLEAN(nextValue()),
13
+ STRING(nextValue()),
14
+ JS_OBJECT(nextValue()),
15
+ JS_VALUE(nextValue()),
16
+ READABLE_ARRAY(nextValue()),
17
+ READABLE_MAP(nextValue());
18
+ }
@@ -0,0 +1,39 @@
1
+ package expo.modules.kotlin.jni
2
+
3
+ import com.facebook.react.bridge.ReadableNativeArray
4
+ import expo.modules.core.interfaces.DoNotStrip
5
+
6
+ /**
7
+ * It's a wrapper for a promise-less function that will be invoked from JS.
8
+ * This interface is intended to be passed to cpp code.
9
+ * If you want to modify it, please don't forget to change the corresponding jni::JavaClass.
10
+ */
11
+ @DoNotStrip
12
+ fun interface JNIFunctionBody {
13
+ /**
14
+ * Invokes the Kotlin part of the JNI function.
15
+ *
16
+ * We used a [com.facebook.react.bridge.ReadableNativeArray] to communicate with CPP, because
17
+ * right now it's the only object which is recognizable by those two worlds.
18
+ * In the future, we may want to swap it for something else.
19
+ */
20
+ @DoNotStrip
21
+ fun invoke(args: ReadableNativeArray): ReadableNativeArray?
22
+ }
23
+
24
+ /**
25
+ * It's a wrapper for a promise function that will be invoked from JS.
26
+ * This interface is intended to be passed to cpp code.
27
+ * If you want to modify it, please don't forget to change the corresponding jni::JavaClass.
28
+ */
29
+ @DoNotStrip
30
+ fun interface JNIAsyncFunctionBody {
31
+ /**
32
+ * Invokes the Kotlin part of the JNI function.
33
+ *
34
+ * Note: that the `bridgePromise` has type of [Any], but it should be an instance of [com.facebook.react.bridge.Promise].
35
+ * This is dictated by the fact that [com.facebook.react.bridge.Promise] isn't a hybrid object of jni::HybridClass.
36
+ */
37
+ @DoNotStrip
38
+ fun invoke(args: ReadableNativeArray, bridgePromise: Any)
39
+ }
@@ -0,0 +1,89 @@
1
+ package expo.modules.kotlin.jni
2
+
3
+ import com.facebook.jni.HybridData
4
+ import com.facebook.react.turbomodule.core.CallInvokerHolderImpl
5
+ import expo.modules.core.interfaces.DoNotStrip
6
+ import expo.modules.kotlin.AppContext
7
+ import expo.modules.kotlin.exception.JavaScriptEvaluateException
8
+ import java.lang.ref.WeakReference
9
+
10
+ @Suppress("KotlinJniMissingFunction")
11
+ @DoNotStrip
12
+ class JSIInteropModuleRegistry(appContext: AppContext) {
13
+ private val appContextHolder = WeakReference(appContext)
14
+
15
+ // Has to be called "mHybridData" - fbjni uses it via reflection
16
+ @DoNotStrip
17
+ private val mHybridData = initHybrid()
18
+
19
+ private external fun initHybrid(): HybridData
20
+
21
+ /**
22
+ * Initializes the `ExpoModulesHostObject` and adds it to the global object.
23
+ */
24
+ external fun installJSI(
25
+ jsRuntimePointer: Long,
26
+ jsInvokerHolder: CallInvokerHolderImpl,
27
+ nativeInvokerHolder: CallInvokerHolderImpl
28
+ )
29
+
30
+ /**
31
+ * Initializes the test runtime. Shouldn't be used in the production.
32
+ */
33
+ external fun installJSIForTests()
34
+
35
+ /**
36
+ * Evaluates given JavaScript source code.
37
+ * @throws JavaScriptEvaluateException if the input format is unknown or evaluation causes an error
38
+ */
39
+ @Throws(JavaScriptEvaluateException::class)
40
+ external fun evaluateScript(script: String): JavaScriptValue
41
+
42
+ /**
43
+ * Returns the runtime global object
44
+ */
45
+ external fun global(): JavaScriptObject
46
+
47
+ /**
48
+ * Returns a new instance of [JavaScriptObject]
49
+ */
50
+ external fun createObject(): JavaScriptObject
51
+
52
+ /**
53
+ * Drains the JavaScript VM internal Microtask (a.k.a. event loop) queue.
54
+ */
55
+ external fun drainJSEventLoop()
56
+
57
+ /**
58
+ * Returns a `JavaScriptModuleObject` that is a bridge between [expo.modules.kotlin.modules.Module]
59
+ * and HostObject exported via JSI.
60
+ *
61
+ * This function will be called from the CPP implementation.
62
+ * It doesn't make sense to call it from Kotlin.
63
+ */
64
+ @Suppress("unused")
65
+ @DoNotStrip
66
+ fun getJavaScriptModuleObject(name: String): JavaScriptModuleObject? {
67
+ return appContextHolder.get()?.registry?.getModuleHolder(name)?.jsObject
68
+ }
69
+
70
+ /**
71
+ * Returns an array that contains names of available modules.
72
+ */
73
+ @Suppress("unused")
74
+ @DoNotStrip
75
+ fun getJavaScriptModulesName(): Array<String> {
76
+ return appContextHolder.get()?.registry?.registry?.keys?.toTypedArray() ?: emptyArray()
77
+ }
78
+
79
+ @Throws(Throwable::class)
80
+ protected fun finalize() {
81
+ mHybridData.resetNative()
82
+ }
83
+
84
+ companion object {
85
+ init {
86
+ System.loadLibrary("expo-modules-core")
87
+ }
88
+ }
89
+ }
@@ -0,0 +1,44 @@
1
+ package expo.modules.kotlin.jni
2
+
3
+ import com.facebook.jni.HybridData
4
+ import com.facebook.react.bridge.NativeMap
5
+ import expo.modules.core.interfaces.DoNotStrip
6
+
7
+ /**
8
+ * A class to communicate with CPP part of the [expo.modules.kotlin.modules.Module] class.
9
+ * Used to register exported JSI functions.
10
+ * The lifetime of instances of this class should be in sync with the lifetime of the bridge.
11
+ * All exported functions/objects will have a reference to the `JavaScriptModuleObject`,
12
+ * so it must outlive the current RN context.
13
+ */
14
+ @Suppress("KotlinJniMissingFunction")
15
+ @DoNotStrip
16
+ class JavaScriptModuleObject {
17
+ // Has to be called "mHybridData" - fbjni uses it via reflection
18
+ @DoNotStrip
19
+ private val mHybridData = initHybrid()
20
+
21
+ private external fun initHybrid(): HybridData
22
+
23
+ /**
24
+ * Exports constants
25
+ */
26
+ external fun exportConstants(constants: NativeMap)
27
+
28
+ /**
29
+ * Register a promise-less function on the CPP module representation.
30
+ * After calling this function, user can access the exported function in the JS code.
31
+ */
32
+ external fun registerSyncFunction(name: String, args: Int, body: JNIFunctionBody)
33
+
34
+ /**
35
+ * Register a promise function on the CPP module representation.
36
+ * After calling this function, user can access the exported function in the JS code.
37
+ */
38
+ external fun registerAsyncFunction(name: String, args: Int, body: JNIAsyncFunctionBody)
39
+
40
+ @Throws(Throwable::class)
41
+ protected fun finalize() {
42
+ mHybridData.resetNative()
43
+ }
44
+ }