expo-modules-core 1.2.6 → 1.3.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 (180) hide show
  1. package/CHANGELOG.md +33 -4
  2. package/ExpoModulesCore.podspec +1 -0
  3. package/README.md +1 -1
  4. package/android/ExpoModulesCorePlugin.gradle +16 -0
  5. package/android/build.gradle +3 -2
  6. package/android/src/main/cpp/Exceptions.cpp +8 -0
  7. package/android/src/main/cpp/Exceptions.h +11 -0
  8. package/android/src/main/cpp/ExpoModulesHostObject.cpp +22 -5
  9. package/android/src/main/cpp/ExpoModulesHostObject.h +5 -0
  10. package/android/src/main/cpp/JNIInjector.cpp +2 -0
  11. package/android/src/main/cpp/JSIInteropModuleRegistry.cpp +25 -1
  12. package/android/src/main/cpp/JSIInteropModuleRegistry.h +14 -0
  13. package/android/src/main/cpp/JSIObjectWrapper.h +15 -4
  14. package/android/src/main/cpp/JSITypeConverter.h +3 -2
  15. package/android/src/main/cpp/JavaReferencesCache.cpp +2 -0
  16. package/android/src/main/cpp/JavaScriptFunction.cpp +56 -0
  17. package/android/src/main/cpp/JavaScriptFunction.h +54 -0
  18. package/android/src/main/cpp/JavaScriptModuleObject.cpp +225 -105
  19. package/android/src/main/cpp/JavaScriptModuleObject.h +67 -34
  20. package/android/src/main/cpp/JavaScriptObject.cpp +55 -1
  21. package/android/src/main/cpp/JavaScriptObject.h +17 -13
  22. package/android/src/main/cpp/JavaScriptRuntime.cpp +12 -3
  23. package/android/src/main/cpp/JavaScriptRuntime.h +9 -1
  24. package/android/src/main/cpp/JavaScriptValue.cpp +9 -0
  25. package/android/src/main/cpp/JavaScriptValue.h +4 -0
  26. package/android/src/main/cpp/MethodMetadata.cpp +66 -87
  27. package/android/src/main/cpp/MethodMetadata.h +18 -16
  28. package/android/src/main/cpp/ObjectDeallocator.h +25 -0
  29. package/android/src/main/cpp/WeakRuntimeHolder.cpp +7 -0
  30. package/android/src/main/cpp/WeakRuntimeHolder.h +4 -0
  31. package/android/src/main/cpp/types/CppType.h +4 -1
  32. package/android/src/main/cpp/types/FrontendConverter.cpp +58 -0
  33. package/android/src/main/cpp/types/FrontendConverter.h +45 -0
  34. package/android/src/main/cpp/types/FrontendConverterProvider.cpp +3 -0
  35. package/android/src/main/cpp/types/JNIToJSIConverter.cpp +88 -0
  36. package/android/src/main/cpp/types/JNIToJSIConverter.h +22 -0
  37. package/android/src/main/java/com/facebook/react/uimanager/ReactStylesDiffMapHelper.kt +10 -0
  38. package/android/src/main/java/expo/modules/kotlin/AppContext.kt +18 -25
  39. package/android/src/main/java/expo/modules/kotlin/FilteredIterator.kt +37 -0
  40. package/android/src/main/java/expo/modules/kotlin/KotlinInteropModuleRegistry.kt +1 -1
  41. package/android/src/main/java/expo/modules/kotlin/ModuleHolder.kt +34 -21
  42. package/android/src/main/java/expo/modules/kotlin/ModuleRegistry.kt +13 -3
  43. package/android/src/main/java/expo/modules/kotlin/Utils.kt +21 -0
  44. package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultCaller.kt +21 -1
  45. package/android/src/main/java/expo/modules/kotlin/classcomponent/ClassComponentBuilder.kt +112 -0
  46. package/android/src/main/java/expo/modules/kotlin/classcomponent/ClassDefinitionData.kt +10 -0
  47. package/android/src/main/java/expo/modules/kotlin/exception/CodedException.kt +21 -0
  48. package/android/src/main/java/expo/modules/kotlin/exception/CommonExceptions.kt +15 -0
  49. package/android/src/main/java/expo/modules/kotlin/functions/AnyFunction.kt +17 -4
  50. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunction.kt +38 -8
  51. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionComponent.kt +3 -2
  52. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionWithPromiseComponent.kt +3 -2
  53. package/android/src/main/java/expo/modules/kotlin/functions/SuspendFunctionComponent.kt +1 -0
  54. package/android/src/main/java/expo/modules/kotlin/functions/SyncFunctionComponent.kt +18 -11
  55. package/android/src/main/java/expo/modules/kotlin/jni/CppType.kt +4 -1
  56. package/android/src/main/java/expo/modules/kotlin/jni/JNIDeallocator.kt +73 -0
  57. package/android/src/main/java/expo/modules/kotlin/jni/JSIInteropModuleRegistry.kt +28 -2
  58. package/android/src/main/java/expo/modules/kotlin/jni/JavaCallback.kt +8 -1
  59. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptFunction.kt +48 -0
  60. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptModuleObject.kt +40 -3
  61. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptObject.kt +23 -3
  62. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptValue.kt +26 -1
  63. package/android/src/main/java/expo/modules/kotlin/modules/Module.kt +0 -11
  64. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionBuilder.kt +26 -16
  65. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionData.kt +3 -1
  66. package/android/src/main/java/expo/modules/kotlin/sharedobjects/SharedObject.kt +12 -0
  67. package/android/src/main/java/expo/modules/kotlin/sharedobjects/SharedObjectRegistry.kt +62 -0
  68. package/android/src/main/java/expo/modules/kotlin/sharedobjects/SharedObjectTypeConverter.kt +27 -0
  69. package/android/src/main/java/expo/modules/kotlin/types/AnyType.kt +2 -1
  70. package/android/src/main/java/expo/modules/kotlin/types/EitherTypeConverter.kt +7 -6
  71. package/android/src/main/java/expo/modules/kotlin/types/JavaScriptFunctionTypeConverter.kt +22 -0
  72. package/android/src/main/java/expo/modules/kotlin/types/TypeConverter.kt +30 -24
  73. package/android/src/main/java/expo/modules/kotlin/types/TypeConverterProvider.kt +45 -1
  74. package/android/src/main/java/expo/modules/kotlin/types/TypedArrayTypeConverter.kt +3 -2
  75. package/android/src/main/java/expo/modules/kotlin/views/AnyViewProp.kt +3 -1
  76. package/android/src/main/java/expo/modules/kotlin/views/ConcreteViewProp.kt +3 -3
  77. package/android/src/main/java/expo/modules/kotlin/views/FilteredReadableMap.kt +53 -0
  78. package/android/src/main/java/expo/modules/kotlin/views/GroupViewManagerWrapper.kt +25 -5
  79. package/android/src/main/java/expo/modules/kotlin/views/SimpleViewManagerWrapper.kt +25 -5
  80. package/android/src/main/java/expo/modules/kotlin/views/ViewDefinitionBuilder.kt +161 -10
  81. package/android/src/main/java/expo/modules/kotlin/views/ViewGroupDefinitionBuilder.kt +0 -67
  82. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinition.kt +7 -8
  83. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerWrapperDelegate.kt +40 -3
  84. package/android/src/main/java/expo/modules/kotlin/views/ViewTypeConverter.kt +44 -0
  85. package/android-annotation/build.gradle +45 -0
  86. package/android-annotation/src/main/java/expo/modules/annotation/Config.kt +7 -0
  87. package/android-annotation/src/main/java/expo/modules/annotation/ConverterBinder.kt +7 -0
  88. package/android-annotation-processor/build.gradle +51 -0
  89. package/android-annotation-processor/src/main/java/expo/modules/annotationprocessor/ExpoSymbolProcessor.kt +175 -0
  90. package/android-annotation-processor/src/main/java/expo/modules/annotationprocessor/ExpoSymbolProcessorProvider.kt +10 -0
  91. package/android-annotation-processor/src/main/resources/META-INF/services/com.google.devtools.ksp.processing.SymbolProcessorProvider +1 -0
  92. package/build/NativeViewManagerAdapter.native.d.ts.map +1 -1
  93. package/build/NativeViewManagerAdapter.native.js +36 -23
  94. package/build/NativeViewManagerAdapter.native.js.map +1 -1
  95. package/build/requireNativeModule.js +2 -2
  96. package/build/requireNativeModule.js.map +1 -1
  97. package/common/cpp/fabric/ExpoViewProps.cpp +18 -3
  98. package/common/cpp/fabric/ExpoViewProps.h +4 -1
  99. package/ios/Fabric/ExpoFabricView.swift +10 -10
  100. package/ios/Fabric/ExpoFabricViewObjC.h +2 -0
  101. package/ios/Fabric/ExpoFabricViewObjC.mm +17 -2
  102. package/ios/JSI/EXJSIInstaller.mm +1 -1
  103. package/ios/JSI/EXJSIUtils.h +5 -0
  104. package/ios/JSI/EXJSIUtils.mm +17 -0
  105. package/ios/JSI/EXJavaScriptRuntime.h +5 -0
  106. package/ios/JSI/EXJavaScriptRuntime.mm +6 -0
  107. package/ios/JSI/EXJavaScriptValue.h +2 -0
  108. package/ios/JSI/EXJavaScriptValue.mm +8 -0
  109. package/ios/JSI/EXJavaScriptWeakObject.mm +29 -8
  110. package/ios/JSI/EXRawJavaScriptFunction.h +24 -0
  111. package/ios/JSI/EXRawJavaScriptFunction.mm +52 -0
  112. package/ios/JSI/ExpoModulesHostObject.mm +1 -1
  113. package/ios/JSI/JavaScriptValue.swift +28 -1
  114. package/ios/ModuleRegistry/EXModuleRegistry.h +0 -4
  115. package/ios/ModuleRegistry/EXModuleRegistry.m +0 -23
  116. package/ios/ModuleRegistryAdapter/EXModuleRegistryAdapter.m +0 -16
  117. package/ios/ModuleRegistryProvider/EXModuleRegistryProvider.m +0 -6
  118. package/ios/NativeModulesProxy/EXNativeModulesProxy.mm +1 -31
  119. package/ios/Swift/AppContext.swift +46 -6
  120. package/ios/Swift/Arguments/Convertible.swift +3 -3
  121. package/ios/Swift/Arguments/Convertibles.swift +5 -5
  122. package/ios/Swift/Classes/ClassComponent.swift +18 -12
  123. package/ios/Swift/Classes/ClassRegistry.swift +31 -0
  124. package/ios/Swift/Conversions.swift +19 -3
  125. package/ios/Swift/Convertibles/Convertibles+Color.swift +3 -3
  126. package/ios/Swift/Convertibles/Either.swift +6 -4
  127. package/ios/Swift/DynamicTypes/AnyDynamicType.swift +18 -2
  128. package/ios/Swift/DynamicTypes/DynamicArrayType.swift +3 -3
  129. package/ios/Swift/DynamicTypes/DynamicConvertibleType.swift +2 -2
  130. package/ios/Swift/DynamicTypes/DynamicEnumType.swift +1 -1
  131. package/ios/Swift/DynamicTypes/DynamicJavaScriptType.swift +27 -0
  132. package/ios/Swift/DynamicTypes/DynamicOptionalType.swift +9 -2
  133. package/ios/Swift/DynamicTypes/DynamicRawType.swift +1 -1
  134. package/ios/Swift/DynamicTypes/DynamicSharedObjectType.swift +16 -2
  135. package/ios/Swift/DynamicTypes/DynamicType.swift +6 -0
  136. package/ios/Swift/DynamicTypes/DynamicTypedArrayType.swift +15 -4
  137. package/ios/Swift/DynamicTypes/DynamicViewType.swift +68 -0
  138. package/ios/Swift/ExpoBridgeModule.swift +1 -1
  139. package/ios/Swift/Functions/AnyFunction.swift +5 -4
  140. package/ios/Swift/Functions/AsyncFunctionComponent.swift +22 -19
  141. package/ios/Swift/Functions/ConcurrentFunctionDefinition.swift +29 -13
  142. package/ios/Swift/Functions/SyncFunctionComponent.swift +26 -15
  143. package/ios/Swift/JavaScriptFunction.swift +68 -0
  144. package/ios/Swift/JavaScriptUtils.swift +57 -18
  145. package/ios/Swift/ModuleHolder.swift +22 -10
  146. package/ios/Swift/Modules/ModuleDefinition.swift +8 -2
  147. package/ios/Swift/Objects/JavaScriptObjectBuilder.swift +8 -8
  148. package/ios/Swift/Objects/ObjectDefinition.swift +17 -15
  149. package/ios/Swift/Objects/PropertyComponent.swift +23 -17
  150. package/ios/Swift/Records/AnyField.swift +1 -1
  151. package/ios/Swift/Records/Field.swift +2 -2
  152. package/ios/Swift/Records/Record.swift +5 -5
  153. package/ios/Swift/SharedObjects/SharedObjectRegistry.swift +4 -0
  154. package/ios/Swift/Views/AnyViewProp.swift +1 -1
  155. package/ios/Swift/Views/ComponentData.swift +37 -2
  156. package/ios/Swift/Views/ConcreteViewProp.swift +2 -2
  157. package/ios/Swift/Views/ViewDefinition.swift +39 -0
  158. package/ios/Swift/Views/ViewModuleWrapper.swift +0 -29
  159. package/ios/Tests/ClassComponentSpec.swift +39 -27
  160. package/ios/Tests/ConvertiblesSpec.swift +75 -49
  161. package/ios/Tests/DynamicTypeSpec.swift +29 -27
  162. package/ios/Tests/EitherSpec.swift +9 -7
  163. package/ios/Tests/ExpoModulesSpec.swift +13 -13
  164. package/ios/Tests/FunctionSpec.swift +38 -22
  165. package/ios/Tests/JavaScriptRuntimeSpec.swift +4 -0
  166. package/ios/Tests/PropertyComponentSpec.swift +33 -30
  167. package/ios/Tests/RecordSpec.swift +7 -5
  168. package/ios/Tests/SharedObjectRegistrySpec.swift +12 -12
  169. package/ios/Tests/TypedArraysSpec.swift +1 -1
  170. package/ios/Tests/ViewDefinitionSpec.swift +4 -2
  171. package/package.json +2 -2
  172. package/src/NativeViewManagerAdapter.native.tsx +33 -29
  173. package/src/requireNativeModule.ts +2 -2
  174. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinitionBuilder.kt +0 -132
  175. package/ios/EXViewManager.h +0 -21
  176. package/ios/EXViewManager.m +0 -128
  177. package/ios/ModuleRegistryAdapter/EXViewManagerAdapterClassesRegistry.h +0 -17
  178. package/ios/ModuleRegistryAdapter/EXViewManagerAdapterClassesRegistry.m +0 -67
  179. package/ios/ViewManagerAdapter/EXViewManagerAdapter.h +0 -17
  180. package/ios/ViewManagerAdapter/EXViewManagerAdapter.m +0 -45
@@ -0,0 +1,112 @@
1
+ @file:Suppress("FunctionName")
2
+
3
+ package expo.modules.kotlin.classcomponent
4
+
5
+ import expo.modules.kotlin.functions.SyncFunctionComponent
6
+ import expo.modules.kotlin.objects.ObjectDefinitionBuilder
7
+ import expo.modules.kotlin.types.toAnyType
8
+ import kotlin.reflect.KClass
9
+ import kotlin.reflect.KType
10
+ import kotlin.reflect.typeOf
11
+
12
+ class ClassComponentBuilder<SharedObjectType : Any>(
13
+ val name: String,
14
+ private val ownerClass: KClass<SharedObjectType>,
15
+ private val ownerType: KType
16
+ ) : ObjectDefinitionBuilder() {
17
+ var constructor: SyncFunctionComponent? = null
18
+
19
+ fun buildClass(): ClassDefinitionData {
20
+ val objectData = buildObject()
21
+ objectData.functions.forEach {
22
+ it.ownerType = ownerType
23
+ it.canTakeOwner = true
24
+ }
25
+
26
+ val hasSharedObject = ownerClass !== Unit::class
27
+ if (hasSharedObject && constructor == null) {
28
+ throw IllegalArgumentException("constructor cannot be null")
29
+ }
30
+
31
+ val constructor = constructor ?: SyncFunctionComponent("constructor", arrayOf()) {}
32
+ constructor.canTakeOwner = true
33
+
34
+ return ClassDefinitionData(
35
+ name,
36
+ constructor,
37
+ objectData,
38
+ )
39
+ }
40
+
41
+ inline fun Constructor(
42
+ crossinline body: () -> SharedObjectType
43
+ ): SyncFunctionComponent {
44
+ return SyncFunctionComponent("constructor", arrayOf()) { body() }.also {
45
+ constructor = it
46
+ }
47
+ }
48
+
49
+ inline fun <reified P0> Constructor(
50
+ crossinline body: (p0: P0) -> SharedObjectType
51
+ ): SyncFunctionComponent {
52
+ return SyncFunctionComponent("constructor", arrayOf(typeOf<P0>().toAnyType())) { body(it[0] as P0) }.also {
53
+ constructor = it
54
+ }
55
+ }
56
+
57
+ inline fun <reified P0, reified P1> Constructor(
58
+ crossinline body: (p0: P0, p1: P1) -> SharedObjectType
59
+ ): SyncFunctionComponent {
60
+ return SyncFunctionComponent("constructor", arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType())) { body(it[0] as P0, it[1] as P1) }.also {
61
+ constructor = it
62
+ }
63
+ }
64
+
65
+ inline fun <reified P0, reified P1, reified P2> Constructor(
66
+ crossinline body: (p0: P0, p1: P1, p2: P2) -> SharedObjectType
67
+ ): SyncFunctionComponent {
68
+ return SyncFunctionComponent("constructor", arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType())) { body(it[0] as P0, it[1] as P1, it[2] as P2) }.also {
69
+ constructor = it
70
+ }
71
+ }
72
+
73
+ inline fun <reified P0, reified P1, reified P2, reified P3> Constructor(
74
+ crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3) -> SharedObjectType
75
+ ): SyncFunctionComponent {
76
+ return SyncFunctionComponent("constructor", arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType())) { body(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3) }.also {
77
+ constructor = it
78
+ }
79
+ }
80
+
81
+ inline fun <reified P0, reified P1, reified P2, reified P3, reified P4> Constructor(
82
+ crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4) -> SharedObjectType
83
+ ): SyncFunctionComponent {
84
+ return SyncFunctionComponent("constructor", arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType())) { body(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3, it[4] as P4) }.also {
85
+ constructor = it
86
+ }
87
+ }
88
+
89
+ inline fun <reified P0, reified P1, reified P2, reified P3, reified P4, reified P5> Constructor(
90
+ crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5) -> SharedObjectType
91
+ ): SyncFunctionComponent {
92
+ return SyncFunctionComponent("constructor", arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType(), typeOf<P5>().toAnyType())) { body(it[0] as P0, it[1] as P1, it[2] as P2, it[3] as P3, it[4] as P4, it[5] as P5) }.also {
93
+ constructor = it
94
+ }
95
+ }
96
+
97
+ inline fun <reified P0, reified P1, reified P2, reified P3, reified P4, reified P5, reified P6> Constructor(
98
+ crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6) -> SharedObjectType
99
+ ): SyncFunctionComponent {
100
+ return SyncFunctionComponent("constructor", arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType(), typeOf<P5>().toAnyType(), typeOf<P6>().toAnyType())) { body(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) }.also {
101
+ constructor = it
102
+ }
103
+ }
104
+
105
+ inline fun <reified P0, reified P1, reified P2, reified P3, reified P4, reified P5, reified P6, reified P7> Constructor(
106
+ crossinline body: (p0: P0, p1: P1, p2: P2, p3: P3, p4: P4, p5: P5, p6: P6, p7: P7) -> SharedObjectType
107
+ ): SyncFunctionComponent {
108
+ return SyncFunctionComponent("constructor", arrayOf(typeOf<P0>().toAnyType(), typeOf<P1>().toAnyType(), typeOf<P2>().toAnyType(), typeOf<P3>().toAnyType(), typeOf<P4>().toAnyType(), typeOf<P5>().toAnyType(), typeOf<P6>().toAnyType(), typeOf<P7>().toAnyType())) { body(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) }.also {
109
+ constructor = it
110
+ }
111
+ }
112
+ }
@@ -0,0 +1,10 @@
1
+ package expo.modules.kotlin.classcomponent
2
+
3
+ import expo.modules.kotlin.functions.SyncFunctionComponent
4
+ import expo.modules.kotlin.objects.ObjectDefinitionData
5
+
6
+ class ClassDefinitionData(
7
+ val name: String,
8
+ val constructor: SyncFunctionComponent,
9
+ val objectDefinition: ObjectDefinitionData
10
+ )
@@ -7,6 +7,12 @@ import kotlin.reflect.KClass
7
7
  import kotlin.reflect.KProperty1
8
8
  import kotlin.reflect.KType
9
9
 
10
+ @Suppress("NOTHING_TO_INLINE")
11
+ inline fun Throwable.toCodedException() = when (this) {
12
+ is CodedException -> this
13
+ else -> UnexpectedException(this)
14
+ }
15
+
10
16
  /**
11
17
  * A class for errors specifying its `code` and providing the `description`.
12
18
  */
@@ -85,6 +91,7 @@ internal class MissingTypeConverter(
85
91
  message = "Cannot find type converter for '$forType'.",
86
92
  )
87
93
 
94
+ @DoNotStrip
88
95
  internal class InvalidArgsNumberException(received: Int, expected: Int, required: Int = expected) :
89
96
  CodedException(
90
97
  message = if (required < expected) {
@@ -145,6 +152,14 @@ internal class PropSetException(
145
152
  cause,
146
153
  )
147
154
 
155
+ internal class OnViewDidUpdatePropsException(
156
+ viewType: KClass<*>,
157
+ cause: CodedException
158
+ ) : DecoratedException(
159
+ message = "Error occurred when invoking 'onViewDidUpdateProps' on '${viewType.simpleName}'",
160
+ cause
161
+ )
162
+
148
163
  internal class ArgumentCastException(
149
164
  argDesiredType: KType,
150
165
  argIndex: Int,
@@ -165,6 +180,12 @@ internal class ArgumentCastException(
165
180
  }
166
181
  }
167
182
 
183
+ internal class InvalidSharedObjectException(
184
+ sharedType: KType,
185
+ ) : CodedException(
186
+ message = "Cannot convert provided JavaScriptObject to the '$sharedType', because it doesn't contain valid id"
187
+ )
188
+
168
189
  internal class FieldCastException(
169
190
  fieldName: String,
170
191
  fieldType: KType,
@@ -14,6 +14,11 @@ class Exceptions {
14
14
  viewTag: Int
15
15
  ) : CodedException(message = "Unable to find the $viewType view with tag $viewTag")
16
16
 
17
+ /**
18
+ * The app context is no longer available.
19
+ */
20
+ class AppContextLost : CodedException(message = "The app context has been lost")
21
+
17
22
  /**
18
23
  * The react app context is no longer available.
19
24
  */
@@ -43,4 +48,14 @@ class Exceptions {
43
48
  * An exception to throw when the current Android activity is not longer available.
44
49
  */
45
50
  class MissingActivity : CodedException(message = "The current activity is no longer available")
51
+
52
+ /**
53
+ * An exception to throw when function was called on the incorrect thread.
54
+ */
55
+ class IncorrectThreadException(
56
+ currentThreadName: String,
57
+ expectedThreadName: String
58
+ ) : CodedException(
59
+ message = "Expected to run on $expectedThreadName thread, but was run on $currentThreadName"
60
+ )
46
61
  }
@@ -1,7 +1,6 @@
1
1
  package expo.modules.kotlin.functions
2
2
 
3
3
  import com.facebook.react.bridge.ReadableArray
4
- import com.facebook.react.bridge.ReadableType
5
4
  import expo.modules.kotlin.AppContext
6
5
  import expo.modules.kotlin.exception.ArgumentCastException
7
6
  import expo.modules.kotlin.exception.CodedException
@@ -10,8 +9,12 @@ import expo.modules.kotlin.exception.exceptionDecorator
10
9
  import expo.modules.kotlin.iterator
11
10
  import expo.modules.kotlin.jni.ExpectedType
12
11
  import expo.modules.kotlin.jni.JavaScriptModuleObject
12
+ import expo.modules.kotlin.jni.JavaScriptObject
13
13
  import expo.modules.kotlin.recycle
14
14
  import expo.modules.kotlin.types.AnyType
15
+ import kotlin.reflect.KType
16
+ import kotlin.reflect.full.isSubtypeOf
17
+ import kotlin.reflect.typeOf
15
18
 
16
19
  /**
17
20
  * Base class of all exported functions
@@ -22,6 +25,16 @@ abstract class AnyFunction(
22
25
  ) {
23
26
  internal val argsCount get() = desiredArgsTypes.size
24
27
 
28
+ internal var canTakeOwner: Boolean = false
29
+
30
+ @PublishedApi
31
+ internal var ownerType: KType? = null
32
+
33
+ internal val takesOwner: Boolean
34
+ get() = canTakeOwner &&
35
+ desiredArgsTypes.firstOrNull()?.kType?.isSubtypeOf(typeOf<JavaScriptObject>()) == true ||
36
+ (ownerType != null && desiredArgsTypes.firstOrNull()?.kType?.isSubtypeOf(ownerType!!) == true)
37
+
25
38
  /**
26
39
  * A minimum number of arguments the functions needs which equals to `argumentsCount` reduced by the number of trailing optional arguments.
27
40
  */
@@ -30,7 +43,7 @@ abstract class AnyFunction(
30
43
  .reversed()
31
44
  .indexOfFirst { !it.kType.isMarkedNullable }
32
45
  if (nonNullableArgIndex < 0) {
33
- return@run desiredArgsTypes.size
46
+ return@run 0
34
47
  }
35
48
 
36
49
  return@run desiredArgsTypes.size - nonNullableArgIndex
@@ -70,7 +83,7 @@ abstract class AnyFunction(
70
83
  * @throws `CodedException` if conversion isn't possible
71
84
  */
72
85
  @Throws(CodedException::class)
73
- protected fun convertArgs(args: Array<Any?>): Array<out Any?> {
86
+ protected fun convertArgs(args: Array<Any?>, appContext: AppContext? = null): Array<out Any?> {
74
87
  if (requiredArgumentsCount > args.size || args.size > desiredArgsTypes.size) {
75
88
  throw InvalidArgsNumberException(args.size, desiredArgsTypes.size, requiredArgumentsCount)
76
89
  }
@@ -83,7 +96,7 @@ abstract class AnyFunction(
83
96
  exceptionDecorator({ cause ->
84
97
  ArgumentCastException(desiredType.kType, index, element?.javaClass.toString(), cause)
85
98
  }) {
86
- finalArgs[index] = desiredType.convert(element)
99
+ finalArgs[index] = desiredType.convert(element, appContext)
87
100
  }
88
101
  }
89
102
  return finalArgs
@@ -1,10 +1,16 @@
1
1
  package expo.modules.kotlin.functions
2
2
 
3
+ import com.facebook.react.bridge.ReactContext
3
4
  import com.facebook.react.bridge.ReadableArray
5
+ import com.facebook.react.uimanager.UIManagerHelper
6
+ import com.facebook.react.uimanager.UIManagerModule
7
+ import com.facebook.react.uimanager.common.UIManagerType
8
+ import expo.modules.BuildConfig
4
9
  import expo.modules.kotlin.AppContext
5
10
  import expo.modules.kotlin.ModuleHolder
6
11
  import expo.modules.kotlin.Promise
7
12
  import expo.modules.kotlin.exception.CodedException
13
+ import expo.modules.kotlin.exception.Exceptions
8
14
  import expo.modules.kotlin.exception.FunctionCallException
9
15
  import expo.modules.kotlin.exception.UnexpectedException
10
16
  import expo.modules.kotlin.exception.exceptionDecorator
@@ -48,25 +54,21 @@ abstract class AsyncFunction(
48
54
  @Throws(CodedException::class)
49
55
  internal abstract fun callUserImplementation(args: ReadableArray, promise: Promise)
50
56
 
51
- internal abstract fun callUserImplementation(args: Array<Any?>, promise: Promise)
57
+ internal abstract fun callUserImplementation(args: Array<Any?>, promise: Promise, appContext: AppContext)
52
58
 
53
59
  override fun attachToJSObject(appContext: AppContext, jsObject: JavaScriptModuleObject) {
54
60
  jsObject.registerAsyncFunction(
55
61
  name,
62
+ takesOwner,
56
63
  argsCount,
57
64
  desiredArgsTypes.map { it.getCppRequiredTypes() }.toTypedArray()
58
65
  ) { args, bridgePromise ->
59
- val queue = when (queue) {
60
- Queues.MAIN -> appContext.mainQueue
61
- Queues.DEFAULT -> appContext.modulesQueue
62
- }
63
-
64
- queue.launch {
66
+ val functionBody = {
65
67
  try {
66
68
  exceptionDecorator({
67
69
  FunctionCallException(name, jsObject.name, it)
68
70
  }) {
69
- callUserImplementation(args, bridgePromise)
71
+ callUserImplementation(args, bridgePromise, appContext)
70
72
  }
71
73
  } catch (e: CodedException) {
72
74
  bridgePromise.reject(e)
@@ -74,6 +76,34 @@ abstract class AsyncFunction(
74
76
  bridgePromise.reject(UnexpectedException(e))
75
77
  }
76
78
  }
79
+
80
+ if (queue == Queues.MAIN) {
81
+ if (!BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
82
+ // On certain occasions, invoking a function on a view could lead to an error
83
+ // because of the asynchronous communication between the JavaScript and native components.
84
+ // In such cases, the native view may not have been mounted yet,
85
+ // but the JavaScript code has already received the future tag of the view.
86
+ // To avoid this issue, we have decided to temporarily utilize
87
+ // the UIManagerModule for dispatching functions on the main thread.
88
+ val uiManager = UIManagerHelper.getUIManagerForReactTag(
89
+ appContext.reactContext as? ReactContext ?: throw Exceptions.ReactContextLost(),
90
+ UIManagerType.DEFAULT
91
+ ) as UIManagerModule
92
+
93
+ uiManager.addUIBlock {
94
+ functionBody()
95
+ }
96
+ return@registerAsyncFunction
97
+ }
98
+
99
+ appContext.mainQueue.launch {
100
+ functionBody()
101
+ }
102
+ } else {
103
+ appContext.modulesQueue.launch {
104
+ functionBody()
105
+ }
106
+ }
77
107
  }
78
108
  }
79
109
  }
@@ -1,6 +1,7 @@
1
1
  package expo.modules.kotlin.functions
2
2
 
3
3
  import com.facebook.react.bridge.ReadableArray
4
+ import expo.modules.kotlin.AppContext
4
5
  import expo.modules.kotlin.Promise
5
6
  import expo.modules.kotlin.exception.CodedException
6
7
  import expo.modules.kotlin.types.AnyType
@@ -15,7 +16,7 @@ class AsyncFunctionComponent(
15
16
  promise.resolve(body(convertArgs(args)))
16
17
  }
17
18
 
18
- override fun callUserImplementation(args: Array<Any?>, promise: Promise) {
19
- promise.resolve(body(convertArgs(args)))
19
+ override fun callUserImplementation(args: Array<Any?>, promise: Promise, appContext: AppContext) {
20
+ promise.resolve(body(convertArgs(args, appContext)))
20
21
  }
21
22
  }
@@ -1,6 +1,7 @@
1
1
  package expo.modules.kotlin.functions
2
2
 
3
3
  import com.facebook.react.bridge.ReadableArray
4
+ import expo.modules.kotlin.AppContext
4
5
  import expo.modules.kotlin.Promise
5
6
  import expo.modules.kotlin.exception.CodedException
6
7
  import expo.modules.kotlin.types.AnyType
@@ -15,7 +16,7 @@ class AsyncFunctionWithPromiseComponent(
15
16
  body(convertArgs(args), promise)
16
17
  }
17
18
 
18
- override fun callUserImplementation(args: Array<Any?>, promise: Promise) {
19
- body(convertArgs(args), promise)
19
+ override fun callUserImplementation(args: Array<Any?>, promise: Promise, appContext: AppContext) {
20
+ body(convertArgs(args, appContext), promise)
20
21
  }
21
22
  }
@@ -48,6 +48,7 @@ class SuspendFunctionComponent(
48
48
  override fun attachToJSObject(appContext: AppContext, jsObject: JavaScriptModuleObject) {
49
49
  jsObject.registerAsyncFunction(
50
50
  name,
51
+ takesOwner,
51
52
  argsCount,
52
53
  desiredArgsTypes.map { it.getCppRequiredTypes() }.toTypedArray()
53
54
  ) { args, bridgePromise ->
@@ -5,6 +5,7 @@ import expo.modules.kotlin.AppContext
5
5
  import expo.modules.kotlin.exception.CodedException
6
6
  import expo.modules.kotlin.exception.FunctionCallException
7
7
  import expo.modules.kotlin.exception.exceptionDecorator
8
+ import expo.modules.kotlin.jni.JNIFunctionBody
8
9
  import expo.modules.kotlin.jni.JavaScriptModuleObject
9
10
  import expo.modules.kotlin.types.AnyType
10
11
  import expo.modules.kotlin.types.JSTypeConverter
@@ -19,22 +20,28 @@ class SyncFunctionComponent(
19
20
  return body(convertArgs(args))
20
21
  }
21
22
 
22
- fun call(args: Array<Any?>): Any? {
23
- return body(convertArgs(args))
23
+ fun call(args: Array<Any?>, appContext: AppContext? = null): Any? {
24
+ return body(convertArgs(args, appContext))
24
25
  }
25
26
 
26
- override fun attachToJSObject(appContext: AppContext, jsObject: JavaScriptModuleObject) {
27
- jsObject.registerSyncFunction(
28
- name,
29
- argsCount,
30
- getCppRequiredTypes().toTypedArray()
31
- ) { args ->
32
- return@registerSyncFunction exceptionDecorator({
33
- FunctionCallException(name, jsObject.name, it)
27
+ internal fun getJNIFunctionBody(moduleName: String, appContext: AppContext?): JNIFunctionBody {
28
+ return JNIFunctionBody { args ->
29
+ return@JNIFunctionBody exceptionDecorator({
30
+ FunctionCallException(name, moduleName, it)
34
31
  }) {
35
- val result = call(args)
32
+ val result = call(args, appContext)
36
33
  return@exceptionDecorator JSTypeConverter.convertToJSValue(result)
37
34
  }
38
35
  }
39
36
  }
37
+
38
+ override fun attachToJSObject(appContext: AppContext, jsObject: JavaScriptModuleObject) {
39
+ jsObject.registerSyncFunction(
40
+ name,
41
+ takesOwner,
42
+ argsCount,
43
+ getCppRequiredTypes().toTypedArray(),
44
+ getJNIFunctionBody(jsObject.name, appContext)
45
+ )
46
+ }
40
47
  }
@@ -27,5 +27,8 @@ enum class CppType(val clazz: KClass<*>, val value: Int = nextValue()) {
27
27
  TYPED_ARRAY(TypedArray::class),
28
28
  PRIMITIVE_ARRAY(Array::class),
29
29
  LIST(List::class),
30
- MAP(Map::class);
30
+ MAP(Map::class),
31
+ VIEW_TAG(Int::class),
32
+ SHARED_OBJECT_ID(Int::class),
33
+ JS_FUNCTION(JavaScriptFunction::class);
31
34
  }
@@ -0,0 +1,73 @@
1
+ package expo.modules.kotlin.jni
2
+
3
+ import java.lang.ref.PhantomReference
4
+ import java.lang.ref.ReferenceQueue
5
+ import java.lang.ref.WeakReference
6
+
7
+ interface Destructible {
8
+ fun deallocate()
9
+ }
10
+
11
+ object JNIDeallocator {
12
+ /**
13
+ * A [PhantomReference] queue managed by JVM
14
+ */
15
+ private val referenceQueue = ReferenceQueue<Destructible>()
16
+
17
+ /**
18
+ * A registry to keep all active [Destructible] objects and their [PhantomReference]s
19
+ */
20
+ private val destructorMap = mutableMapOf<PhantomReference<Destructible>, WeakReference<Destructible>>()
21
+
22
+ /**
23
+ * A thread that clears your registry when an object has been garbage collected
24
+ * to not store invalid references to every created object.
25
+ */
26
+ private val destructorThread = object : Thread("Expo JNI deallocator") {
27
+ override fun run() {
28
+ while (true) {
29
+ try {
30
+ // Referent of PhantomReference were garbage collected so we can remove it from our registry.
31
+ // Note that we don't have to call `deallocate` method - it was called [com.facebook.jni.HybridData].
32
+ val current = referenceQueue.remove()
33
+ synchronized(this) {
34
+ destructorMap.remove(current)
35
+ }
36
+ } catch (e: InterruptedException) {
37
+ // Continue. This thread should never be terminated.
38
+ }
39
+ }
40
+ }
41
+ }.also {
42
+ it.start()
43
+ }
44
+
45
+ /**
46
+ * Adds reference to the internal registry.
47
+ * That reference will be deallocated when [JNIDeallocator.deallocate] is called or
48
+ * when the reference won't be reachable by the GC.
49
+ */
50
+ internal fun addReference(destructible: Destructible) = synchronized(this) {
51
+ val weakRef = WeakReference(destructible)
52
+ val phantomRef = PhantomReference(destructible, referenceQueue)
53
+ destructorMap[phantomRef] = weakRef
54
+ }
55
+
56
+ /**
57
+ * Deallocates valid references and clears the internal registry.
58
+ */
59
+ internal fun deallocate() = synchronized(this) {
60
+ destructorMap.values.forEach {
61
+ it.get()?.deallocate()
62
+ }
63
+ destructorMap.clear()
64
+ }
65
+
66
+ /**
67
+ * Returns references to all hybrid objects that contain references to the jsi value
68
+ * and are present in the memory.
69
+ */
70
+ fun inspectMemory() = synchronized(this) {
71
+ destructorMap.values.mapNotNull { it.get() }
72
+ }
73
+ }
@@ -6,12 +6,19 @@ import com.facebook.soloader.SoLoader
6
6
  import expo.modules.core.interfaces.DoNotStrip
7
7
  import expo.modules.kotlin.AppContext
8
8
  import expo.modules.kotlin.exception.JavaScriptEvaluateException
9
+ import expo.modules.kotlin.sharedobjects.SharedObject
9
10
  import java.lang.ref.WeakReference
10
11
 
12
+ /**
13
+ * Despite the fact that this class is marked as [Destructible], it is not included in the [JNIDeallocator].
14
+ * The deallocation of the [JSIInteropModuleRegistry] should be performed at the very end
15
+ * to prevent the destructor of the [Destructible] object from accessing data that has already been freed.
16
+ */
11
17
  @Suppress("KotlinJniMissingFunction")
12
18
  @DoNotStrip
13
- class JSIInteropModuleRegistry(appContext: AppContext) {
14
- private val appContextHolder = WeakReference(appContext)
19
+ class JSIInteropModuleRegistry(appContext: AppContext) : Destructible {
20
+
21
+ internal val appContextHolder = WeakReference(appContext)
15
22
 
16
23
  // Has to be called "mHybridData" - fbjni uses it via reflection
17
24
  @DoNotStrip
@@ -68,6 +75,12 @@ class JSIInteropModuleRegistry(appContext: AppContext) {
68
75
  return appContextHolder.get()?.registry?.getModuleHolder(name)?.jsObject
69
76
  }
70
77
 
78
+ @Suppress("unused")
79
+ @DoNotStrip
80
+ fun hasModule(name: String): Boolean {
81
+ return appContextHolder.get()?.registry?.hasModule(name) ?: false
82
+ }
83
+
71
84
  /**
72
85
  * Returns an array that contains names of available modules.
73
86
  */
@@ -77,8 +90,21 @@ class JSIInteropModuleRegistry(appContext: AppContext) {
77
90
  return appContextHolder.get()?.registry?.registry?.keys?.toTypedArray() ?: emptyArray()
78
91
  }
79
92
 
93
+ @Suppress("unused")
94
+ @DoNotStrip
95
+ fun registerSharedObject(native: Any, js: JavaScriptObject) {
96
+ appContextHolder
97
+ .get()
98
+ ?.sharedObjectRegistry
99
+ ?.add(native as SharedObject, js)
100
+ }
101
+
80
102
  @Throws(Throwable::class)
81
103
  protected fun finalize() {
104
+ deallocate()
105
+ }
106
+
107
+ override fun deallocate() {
82
108
  mHybridData.resetNative()
83
109
  }
84
110
 
@@ -8,7 +8,10 @@ import expo.modules.kotlin.exception.UnexpectedException
8
8
 
9
9
  @Suppress("KotlinJniMissingFunction")
10
10
  @DoNotStrip
11
- class JavaCallback @DoNotStrip internal constructor(@DoNotStrip private val mHybridData: HybridData) {
11
+ class JavaCallback @DoNotStrip internal constructor(@DoNotStrip private val mHybridData: HybridData) : Destructible {
12
+ init {
13
+ JNIDeallocator.addReference(this)
14
+ }
12
15
 
13
16
  operator fun invoke(result: Any?) {
14
17
  if (result == null) {
@@ -38,6 +41,10 @@ class JavaCallback @DoNotStrip internal constructor(@DoNotStrip private val mHyb
38
41
 
39
42
  @Throws(Throwable::class)
40
43
  protected fun finalize() {
44
+ deallocate()
45
+ }
46
+
47
+ override fun deallocate() {
41
48
  mHybridData.resetNative()
42
49
  }
43
50
  }
@@ -0,0 +1,48 @@
1
+ package expo.modules.kotlin.jni
2
+
3
+ import com.facebook.jni.HybridData
4
+ import expo.modules.core.interfaces.DoNotStrip
5
+ import expo.modules.kotlin.AppContext
6
+ import expo.modules.kotlin.types.JSTypeConverter
7
+ import expo.modules.kotlin.types.TypeConverterProviderImpl
8
+ import kotlin.reflect.KType
9
+ import kotlin.reflect.typeOf
10
+
11
+ @Suppress("KotlinJniMissingFunction")
12
+ @DoNotStrip
13
+ class JavaScriptFunction<ReturnType : Any?> @DoNotStrip private constructor(@DoNotStrip private val mHybridData: HybridData) : Destructible {
14
+ init {
15
+ JNIDeallocator.addReference(this)
16
+ }
17
+
18
+ @PublishedApi
19
+ internal var returnType: KType? = null
20
+
21
+ fun isValid() = mHybridData.isValid
22
+
23
+ private external fun invoke(args: Array<Any?>, expectedReturnType: ExpectedType): Any?
24
+
25
+ operator fun invoke(vararg args: Any?, appContext: AppContext? = null): ReturnType {
26
+ // TODO(@lukmccall): check current thread
27
+ val convertedArgs = args
28
+ .map { JSTypeConverter.convertToJSValue(it) }
29
+ .toTypedArray()
30
+
31
+ val converter = TypeConverterProviderImpl
32
+ .obtainTypeConverter(returnType ?: typeOf<Unit>())
33
+
34
+ val expectedReturnType = converter.getCppRequiredTypes()
35
+ val result = invoke(convertedArgs, expectedReturnType)
36
+ @Suppress("UNCHECKED_CAST")
37
+ return converter.convert(result, appContext) as ReturnType
38
+ }
39
+
40
+ @Throws(Throwable::class)
41
+ protected fun finalize() {
42
+ deallocate()
43
+ }
44
+
45
+ override fun deallocate() {
46
+ mHybridData.resetNative()
47
+ }
48
+ }