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
@@ -28,15 +28,22 @@ public final class ConcurrentFunctionDefinition<Args, FirstArgType, ReturnType>:
28
28
 
29
29
  var takesOwner: Bool = false
30
30
 
31
- func call(by owner: AnyObject?, withArguments args: [Any], callback: @escaping (FunctionCallResult) -> Void) {
32
- let arguments: [Any]
31
+ func call(by owner: AnyObject?, withArguments args: [Any], appContext: AppContext, callback: @escaping (FunctionCallResult) -> Void) {
32
+ var arguments: [Any]
33
33
 
34
34
  do {
35
+ try validateArgumentsNumber(function: self, received: args.count)
36
+
35
37
  arguments = concat(
36
- arguments: try cast(arguments: args, forFunction: self),
38
+ arguments: args,
37
39
  withOwner: owner,
38
- forFunction: self
40
+ withPromise: nil,
41
+ forFunction: self,
42
+ appContext: appContext
39
43
  )
44
+
45
+ // All `JavaScriptValue` args must be preliminarly converted on the JS thread, before we jump to the function's queue.
46
+ arguments = try cast(jsValues: args, forFunction: self, appContext: appContext)
40
47
  } catch let error as Exception {
41
48
  callback(.failure(error))
42
49
  return
@@ -50,9 +57,12 @@ public final class ConcurrentFunctionDefinition<Args, FirstArgType, ReturnType>:
50
57
  let result: Result<Any, Exception>
51
58
 
52
59
  do {
60
+ // Convert arguments to the types desired by the function.
61
+ let finalArguments = try cast(arguments: arguments, forFunction: self, appContext: appContext)
62
+
53
63
  // TODO: Right now we force cast the tuple in all types of functions, but we should throw another exception here.
54
64
  // swiftlint:disable force_cast
55
- let argumentsTuple = try Conversions.toTuple(arguments) as! Args
65
+ let argumentsTuple = try Conversions.toTuple(finalArguments) as! Args
56
66
  let returnValue = try await body(argumentsTuple)
57
67
 
58
68
  result = .success(returnValue)
@@ -68,18 +78,24 @@ public final class ConcurrentFunctionDefinition<Args, FirstArgType, ReturnType>:
68
78
 
69
79
  // MARK: - JavaScriptObjectBuilder
70
80
 
71
- func build(inRuntime runtime: JavaScriptRuntime) -> JavaScriptObject {
72
- return runtime.createAsyncFunction(name, argsCount: argumentsCount) { [weak self, name] this, args, resolve, reject in
73
- guard let self = self else {
81
+ func build(appContext: AppContext) throws -> JavaScriptObject {
82
+ return try appContext.runtime.createAsyncFunction(name, argsCount: argumentsCount) {
83
+ [weak appContext, weak self, name] this, args, resolve, reject in
84
+
85
+ guard let appContext else {
86
+ let exception = Exceptions.AppContextLost()
87
+ return reject(exception.code, exception.description, nil)
88
+ }
89
+ guard let self else {
74
90
  let exception = NativeFunctionUnavailableException(name)
75
91
  return reject(exception.code, exception.description, nil)
76
92
  }
77
- self.call(by: this, withArguments: args) { result in
93
+ self.call(by: this, withArguments: args, appContext: appContext) { result in
78
94
  switch result {
79
- case .failure(let error):
80
- reject(error.code, error.description, nil)
81
- case .success(let value):
82
- resolve(value)
95
+ case .failure(let error):
96
+ reject(error.code, error.description, nil)
97
+ case .success(let value):
98
+ resolve(value)
83
99
  }
84
100
  }
85
101
  }
@@ -10,9 +10,10 @@ internal protocol AnySyncFunctionComponent: AnyFunction {
10
10
  - owner: An object that calls this function. If the `takesOwner` property is true
11
11
  and type of the first argument matches the owner type, it's being passed as the argument.
12
12
  - args: An array of arguments to pass to the function. The arguments must be of the same type as in the underlying closure.
13
+ - appContext: An app context where the function is executed.
13
14
  - Returns: A value returned by the called function when succeeded or an error when it failed.
14
15
  */
15
- func call(by owner: AnyObject?, withArguments args: [Any]) throws -> Any
16
+ func call(by owner: AnyObject?, withArguments args: [Any], appContext: AppContext) throws -> Any
16
17
  }
17
18
 
18
19
  /**
@@ -49,9 +50,9 @@ public final class SyncFunctionComponent<Args, FirstArgType, ReturnType>: AnySyn
49
50
 
50
51
  var takesOwner: Bool = false
51
52
 
52
- func call(by owner: AnyObject?, withArguments args: [Any], callback: @escaping (FunctionCallResult) -> ()) {
53
+ func call(by owner: AnyObject?, withArguments args: [Any], appContext: AppContext, callback: @escaping (FunctionCallResult) -> ()) {
53
54
  do {
54
- let result = try call(by: owner, withArguments: args)
55
+ let result = try call(by: owner, withArguments: args, appContext: appContext)
55
56
  callback(.success(Conversions.convertFunctionResult(result)))
56
57
  } catch let error as Exception {
57
58
  callback(.failure(error))
@@ -62,16 +63,26 @@ public final class SyncFunctionComponent<Args, FirstArgType, ReturnType>: AnySyn
62
63
 
63
64
  // MARK: - AnySyncFunctionComponent
64
65
 
65
- func call(by owner: AnyObject?, withArguments args: [Any]) throws -> Any {
66
+ func call(by owner: AnyObject?, withArguments args: [Any], appContext: AppContext) throws -> Any {
66
67
  do {
67
- let arguments = concat(
68
- arguments: try cast(arguments: args, forFunction: self),
68
+ try validateArgumentsNumber(function: self, received: args.count)
69
+
70
+ var arguments = concat(
71
+ arguments: args,
69
72
  withOwner: owner,
70
- forFunction: self
73
+ withPromise: nil,
74
+ forFunction: self,
75
+ appContext: appContext
71
76
  )
77
+
78
+ // Convert JS values to non-JS native types.
79
+ arguments = try cast(jsValues: arguments, forFunction: self, appContext: appContext)
80
+
81
+ // Convert arguments to the types desired by the function.
82
+ arguments = try cast(arguments: arguments, forFunction: self, appContext: appContext)
83
+
72
84
  let argumentsTuple = try Conversions.toTuple(arguments) as! Args
73
- let result = try body(argumentsTuple)
74
- return Conversions.convertFunctionResult(result)
85
+ return try body(argumentsTuple)
75
86
  } catch let error as Exception {
76
87
  throw FunctionCallException(name).causedBy(error)
77
88
  } catch {
@@ -81,17 +92,17 @@ public final class SyncFunctionComponent<Args, FirstArgType, ReturnType>: AnySyn
81
92
 
82
93
  // MARK: - JavaScriptObjectBuilder
83
94
 
84
- func build(inRuntime runtime: JavaScriptRuntime) -> JavaScriptObject {
95
+ func build(appContext: AppContext) throws -> JavaScriptObject {
85
96
  // We intentionally capture a strong reference to `self`, otherwise the "detached" objects would
86
97
  // immediately lose the reference to the definition and thus the underlying native function.
87
98
  // It may potentially cause memory leaks, but at the time of writing this comment,
88
99
  // the native definition instance deallocates correctly when the JS VM triggers the garbage collector.
89
- return runtime.createSyncFunction(name, argsCount: argumentsCount) { [weak runtime, self] this, args in
90
- guard let runtime = runtime else {
91
- throw Exceptions.RuntimeLost()
100
+ return try appContext.runtime.createSyncFunction(name, argsCount: argumentsCount) { [weak appContext, self] this, args in
101
+ guard let appContext else {
102
+ throw Exceptions.AppContextLost()
92
103
  }
93
- let result = try self.call(by: this, withArguments: args)
94
- return Conversions.convertFunctionResult(result, runtime: runtime)
104
+ let result = try self.call(by: this, withArguments: args, appContext: appContext)
105
+ return Conversions.convertFunctionResult(result, appContext: appContext, dynamicType: ~ReturnType.self)
95
106
  }
96
107
  }
97
108
  }
@@ -0,0 +1,68 @@
1
+ // Copyright 2023-present 650 Industries. All rights reserved.
2
+
3
+ /**
4
+ Represents a JavaScript function that can be called by the native code and that must return the given generic `ReturnType`.
5
+ */
6
+ public final class JavaScriptFunction<ReturnType>: AnyArgument, AnyJavaScriptValue {
7
+ /**
8
+ Raw representation of the JavaScript function that doesn't impose any restrictions on the returned type.
9
+ */
10
+ private let rawFunction: RawJavaScriptFunction
11
+
12
+ /**
13
+ Weak reference to the app context that is necessary to convert some arguments associated with the context (e.g. shared objects).
14
+ */
15
+ private weak var appContext: AppContext?
16
+
17
+ init(rawFunction: RawJavaScriptFunction, appContext: AppContext) {
18
+ self.rawFunction = rawFunction
19
+ self.appContext = appContext
20
+ }
21
+
22
+ // MARK: - Calling
23
+
24
+ /**
25
+ Calls the function with the given `this` object and arguments.
26
+ */
27
+ public func call(_ arguments: Any..., usingThis this: JavaScriptObject? = nil) throws -> ReturnType {
28
+ return try call(withArguments: arguments, asConstructor: false, usingThis: this)
29
+ }
30
+
31
+ /**
32
+ Calls the function as a constructor with the given arguments. It's like calling a function with the `new` keyword.
33
+ */
34
+ public func callAsConstructor(_ arguments: Any...) throws -> ReturnType {
35
+ return try call(withArguments: arguments, asConstructor: true, usingThis: nil)
36
+ }
37
+
38
+ /**
39
+ Universal function that calls the function with given arguments, this object and whether to call it as a constructor.
40
+ */
41
+ private func call(withArguments arguments: [Any] = [], asConstructor: Bool = false, usingThis this: JavaScriptObject? = nil) throws -> ReturnType {
42
+ guard let appContext else {
43
+ throw AppContextLostException()
44
+ }
45
+ let value = rawFunction.call(withArguments: arguments, thisObject: this, asConstructor: false)
46
+ let dynamicType = ~ReturnType.self
47
+
48
+ guard let result = try dynamicType.cast(jsValue: value, appContext: appContext) as? ReturnType else {
49
+ throw UnexpectedReturnType(dynamicType.description)
50
+ }
51
+ return result
52
+ }
53
+
54
+ // MARK: - AnyJavaScriptValue
55
+
56
+ internal static func convert(from value: JavaScriptValue, appContext: AppContext) throws -> Self {
57
+ guard value.kind == .function else {
58
+ throw Conversions.ConvertingException<JavaScriptFunction<ReturnType>>(value)
59
+ }
60
+ return Self(rawFunction: value.getFunction(), appContext: appContext)
61
+ }
62
+ }
63
+
64
+ private final class UnexpectedReturnType: GenericException<String> {
65
+ override var reason: String {
66
+ return "The function returned a value that cannot be converted to \(param)"
67
+ }
68
+ }
@@ -10,12 +10,12 @@
10
10
  - Returns: A new value converted according to the dynamic type.
11
11
  - Throws: Rethrows various exceptions that could be thrown by the dynamic types.
12
12
  */
13
- internal func cast(_ value: Any, toType type: AnyDynamicType) throws -> Any {
13
+ internal func cast(_ value: Any, toType type: AnyDynamicType, appContext: AppContext) throws -> Any {
14
14
  // TODO: Accept JavaScriptValue and JavaScriptObject as argument types.
15
15
  if !(type is DynamicTypedArrayType), let value = value as? JavaScriptValue {
16
- return try type.cast(value.getRaw())
16
+ return try type.cast(value.getRaw(), appContext: appContext)
17
17
  }
18
- return try type.cast(value)
18
+ return try type.cast(value, appContext: appContext)
19
19
  }
20
20
 
21
21
  /**
@@ -23,46 +23,85 @@ internal func cast(_ value: Any, toType type: AnyDynamicType) throws -> Any {
23
23
  - Parameters:
24
24
  - arguments: An array of arguments to be cast.
25
25
  - function: A function for which to cast the arguments.
26
+ - appContext: A context of the app.
26
27
  - Returns: An array of arguments after casting. Its size is the same as the input arrays.
27
28
  - Throws: `InvalidArgsNumberException` when the number of arguments is not equal to the actual number
28
29
  of function's arguments (without an owner and promise). Rethrows exceptions thrown by `cast(_:toType:)`.
29
30
  */
30
- internal func cast(arguments: [Any], forFunction function: AnyFunction) throws -> [Any] {
31
- let requiredArgumentsCount = function.requiredArgumentsCount
32
- let argumentTypeOffset = function.takesOwner ? 1 : 0
33
-
34
- if arguments.count < requiredArgumentsCount || arguments.count > function.argumentsCount {
35
- throw InvalidArgsNumberException((
36
- received: arguments.count,
37
- expected: function.argumentsCount,
38
- required: requiredArgumentsCount
39
- ))
40
- }
31
+ internal func cast(arguments: [Any], forFunction function: AnyFunction, appContext: AppContext) throws -> [Any] {
41
32
  return try arguments.enumerated().map { index, argument in
42
- let argumentType = function.dynamicArgumentTypes[index + argumentTypeOffset]
33
+ let argumentType = function.dynamicArgumentTypes[index]
43
34
 
44
35
  do {
45
- return try cast(argument, toType: argumentType)
36
+ return try cast(argument, toType: argumentType, appContext: appContext)
46
37
  } catch {
47
38
  throw ArgumentCastException((index: index, type: argumentType)).causedBy(error)
48
39
  }
49
40
  }
50
41
  }
51
42
 
43
+ /**
44
+ Casts an array of JavaScript values to non-JavaScript types.
45
+ */
46
+ internal func cast(jsValues: [Any], forFunction function: AnyFunction, appContext: AppContext) throws -> [Any] {
47
+ // TODO: Replace `[Any]` with `[JavaScriptValue]` once we make sure only JS values are passed here
48
+ return try jsValues.enumerated().map { index, jsValue in
49
+ let type = function.dynamicArgumentTypes[index]
50
+
51
+ do {
52
+ // Temporarily some values might already be cast to primitive types, so make sure we cast only `JavaScriptValue` and leave the others as they are.
53
+ if let jsValue = jsValue as? JavaScriptValue {
54
+ return try type.cast(jsValue: jsValue, appContext: appContext)
55
+ } else {
56
+ return jsValue
57
+ }
58
+ } catch {
59
+ throw ArgumentCastException((index: index, type: type)).causedBy(error)
60
+ }
61
+ }
62
+ }
63
+
64
+ /**
65
+ Validates whether the number of received arguments is enough to call the given function.
66
+ Throws `InvalidArgsNumberException` otherwise.
67
+ */
68
+ internal func validateArgumentsNumber(function: AnyFunction, received: Int) throws {
69
+ let argumentsCount = function.argumentsCount
70
+ let requiredArgumentsCount = function.requiredArgumentsCount
71
+
72
+ if received < requiredArgumentsCount || received > argumentsCount {
73
+ throw InvalidArgsNumberException((
74
+ received: received,
75
+ expected: argumentsCount,
76
+ required: requiredArgumentsCount
77
+ ))
78
+ }
79
+ }
80
+
52
81
  /**
53
82
  Ensures the provided array of arguments matches the number of arguments expected by the function.
54
83
  - If the function takes the owner, it's added to the beginning.
55
84
  - If the array is still too small, missing arguments are very likely to be optional so it puts `nil` in their place.
56
85
  */
57
- internal func concat(arguments: [Any], withOwner owner: AnyObject?, forFunction function: AnyFunction) -> [Any] {
86
+ internal func concat(
87
+ arguments: [Any],
88
+ withOwner owner: AnyObject?,
89
+ withPromise promise: Promise?,
90
+ forFunction function: AnyFunction,
91
+ appContext: AppContext
92
+ ) -> [Any] {
58
93
  var result = arguments
59
94
 
60
- if function.takesOwner, let owner = try? function.dynamicArgumentTypes.first?.cast(owner) {
95
+ if function.takesOwner {
61
96
  result = [owner] + arguments
62
97
  }
63
98
  if arguments.count < function.argumentsCount {
64
99
  result += Array(repeating: Any?.none as Any, count: function.argumentsCount - arguments.count)
65
100
  }
101
+ // Add promise to the array of arguments if necessary.
102
+ if let promise {
103
+ result += [promise]
104
+ }
66
105
  return result
67
106
  }
68
107
 
@@ -62,24 +62,28 @@ public final class ModuleHolder {
62
62
  // MARK: Calling functions
63
63
 
64
64
  func call(function functionName: String, args: [Any], _ callback: @escaping (FunctionCallResult) -> () = { _ in }) {
65
+ guard let appContext else {
66
+ callback(.failure(Exceptions.AppContextLost()))
67
+ return
68
+ }
65
69
  guard let function = definition.functions[functionName] else {
66
70
  callback(.failure(FunctionNotFoundException((functionName: functionName, moduleName: self.name))))
67
71
  return
68
72
  }
69
- function.call(by: self, withArguments: args, callback: callback)
73
+ function.call(by: self, withArguments: args, appContext: appContext, callback: callback)
70
74
  }
71
75
 
72
76
  @discardableResult
73
77
  func callSync(function functionName: String, args: [Any]) -> Any? {
74
- guard let function = definition.functions[functionName] as? AnySyncFunctionComponent else {
78
+ guard let appContext, let function = definition.functions[functionName] as? AnySyncFunctionComponent else {
75
79
  return nil
76
80
  }
77
81
  do {
78
- let arguments = try cast(arguments: args, forFunction: function)
79
- let result = try function.call(by: self, withArguments: arguments)
82
+ let arguments = try cast(arguments: args, forFunction: function, appContext: appContext)
83
+ let result = try function.call(by: self, withArguments: arguments, appContext: appContext)
80
84
 
81
85
  if let result = result as? SharedObject {
82
- let jsObject = SharedObjectRegistry.ensureSharedJavaScriptObject(runtime: appContext!.runtime!, nativeObject: result)
86
+ let jsObject = SharedObjectRegistry.ensureSharedJavaScriptObject(runtime: try appContext.runtime, nativeObject: result)
83
87
  return jsObject
84
88
  }
85
89
  return result
@@ -98,11 +102,16 @@ public final class ModuleHolder {
98
102
  */
99
103
  private func createJavaScriptModuleObject() -> JavaScriptObject? {
100
104
  // It might be impossible to create any object at the moment (e.g. remote debugging, app context destroyed)
101
- guard let runtime = appContext?.runtime else {
105
+ guard let appContext else {
106
+ return nil
107
+ }
108
+ do {
109
+ log.info("Creating JS object for module '\(name)'")
110
+ return try definition.build(appContext: appContext)
111
+ } catch {
112
+ log.error("Building the module object failed: \(error)")
102
113
  return nil
103
114
  }
104
- log.info("Creating JS object for module '\(name)'")
105
- return definition.build(inRuntime: runtime)
106
115
  }
107
116
 
108
117
  // MARK: Listening to native events
@@ -131,10 +140,13 @@ public final class ModuleHolder {
131
140
  Modifies module's listeners count and calls `onStartObserving` or `onStopObserving` accordingly.
132
141
  */
133
142
  func modifyListenersCount(_ count: Int) {
143
+ guard let appContext else {
144
+ return
145
+ }
134
146
  if count > 0 && listenersCount == 0 {
135
- definition.functions["startObserving"]?.call(withArguments: [])
147
+ definition.functions["startObserving"]?.call(withArguments: [], appContext: appContext)
136
148
  } else if count < 0 && listenersCount + count <= 0 {
137
- definition.functions["stopObserving"]?.call(withArguments: [])
149
+ definition.functions["stopObserving"]?.call(withArguments: [], appContext: appContext)
138
150
  }
139
151
  listenersCount = max(0, listenersCount + count)
140
152
  }
@@ -65,8 +65,14 @@ public final class ModuleDefinition: ObjectDefinition {
65
65
  return self
66
66
  }
67
67
 
68
- public override func build(inRuntime runtime: JavaScriptRuntime) -> JavaScriptObject {
69
- let object = super.build(inRuntime: runtime)
68
+ public override func build(appContext: AppContext) throws -> JavaScriptObject {
69
+ let object = try super.build(appContext: appContext)
70
+
71
+ if let viewManager {
72
+ let reactComponentPrototype = try appContext.runtime.createObject()
73
+ try viewManager.decorateWithFunctions(object: reactComponentPrototype, appContext: appContext)
74
+ object.setProperty("ViewPrototype", value: reactComponentPrototype)
75
+ }
70
76
 
71
77
  // Give the module object a name. It's used for compatibility reasons, see `EventEmitter.ts`.
72
78
  object.defineProperty("__expo_module_name__", value: name, options: [])
@@ -7,7 +7,7 @@ internal protocol JavaScriptObjectDecorator {
7
7
  /**
8
8
  Decorates an existing `JavaScriptObject`.
9
9
  */
10
- func decorate(object: JavaScriptObject, inRuntime runtime: JavaScriptRuntime)
10
+ func decorate(object: JavaScriptObject, appContext: AppContext) throws
11
11
  }
12
12
 
13
13
  /**
@@ -15,23 +15,23 @@ internal protocol JavaScriptObjectDecorator {
15
15
  */
16
16
  internal protocol JavaScriptObjectBuilder: JavaScriptObjectDecorator {
17
17
  /**
18
- Creates a decorated `JavaScriptObject` in the given runtime.
18
+ Creates a decorated `JavaScriptObject` in the given app context.
19
19
  */
20
- func build(inRuntime runtime: JavaScriptRuntime) -> JavaScriptObject
20
+ func build(appContext: AppContext) throws -> JavaScriptObject
21
21
  }
22
22
 
23
23
  /**
24
24
  Provides the default behavior of `JavaScriptObjectBuilder`.
25
- The `build(inRuntime:)` creates a plain object and uses `decorate(object:)` for decoration.
25
+ The `build(appContext:)` creates a plain object and uses `decorate(object:appContext:)` for decoration.
26
26
  */
27
27
  extension JavaScriptObjectBuilder {
28
- func build(inRuntime runtime: JavaScriptRuntime) -> JavaScriptObject {
29
- let object = runtime.createObject()
30
- decorate(object: object, inRuntime: runtime)
28
+ func build(appContext: AppContext) throws -> JavaScriptObject {
29
+ let object = try appContext.runtime.createObject()
30
+ try decorate(object: object, appContext: appContext)
31
31
  return object
32
32
  }
33
33
 
34
- func decorate(object: JavaScriptObject, inRuntime runtime: JavaScriptRuntime) {
34
+ func decorate(object: JavaScriptObject, appContext: AppContext) throws {
35
35
  // no-op by default
36
36
  }
37
37
  }
@@ -61,43 +61,45 @@ public class ObjectDefinition: AnyDefinition, JavaScriptObjectBuilder {
61
61
 
62
62
  // MARK: - JavaScriptObjectBuilder
63
63
 
64
- public func build(inRuntime runtime: JavaScriptRuntime) -> JavaScriptObject {
65
- let object = runtime.createObject()
66
- decorate(object: object, inRuntime: runtime)
64
+ public func build(appContext: AppContext) throws -> JavaScriptObject {
65
+ let object = try appContext.runtime.createObject()
66
+ try decorate(object: object, appContext: appContext)
67
67
  return object
68
68
  }
69
69
 
70
- public func decorate(object: JavaScriptObject, inRuntime runtime: JavaScriptRuntime) {
71
- decorateWithConstants(runtime: runtime, object: object)
72
- decorateWithFunctions(runtime: runtime, object: object)
73
- decorateWithProperties(runtime: runtime, object: object)
74
- decorateWithClasses(runtime: runtime, object: object)
70
+ public func decorate(object: JavaScriptObject, appContext: AppContext) throws {
71
+ let runtime = try appContext.runtime
72
+
73
+ decorateWithConstants(object: object)
74
+ try decorateWithFunctions(object: object, appContext: appContext)
75
+ try decorateWithProperties(object: object, appContext: appContext)
76
+ try decorateWithClasses(object: object, appContext: appContext)
75
77
  }
76
78
 
77
79
  // MARK: - Internals
78
80
 
79
- internal func decorateWithConstants(runtime: JavaScriptRuntime, object: JavaScriptObject) {
81
+ internal func decorateWithConstants(object: JavaScriptObject) {
80
82
  for (key, value) in getConstants() {
81
83
  object.setProperty(key, value: value)
82
84
  }
83
85
  }
84
86
 
85
- internal func decorateWithFunctions(runtime: JavaScriptRuntime, object: JavaScriptObject) {
87
+ internal func decorateWithFunctions(object: JavaScriptObject, appContext: AppContext) throws {
86
88
  for fn in functions.values {
87
- object.setProperty(fn.name, value: fn.build(inRuntime: runtime))
89
+ object.setProperty(fn.name, value: try fn.build(appContext: appContext))
88
90
  }
89
91
  }
90
92
 
91
- internal func decorateWithProperties(runtime: JavaScriptRuntime, object: JavaScriptObject) {
93
+ internal func decorateWithProperties(object: JavaScriptObject, appContext: AppContext) throws {
92
94
  for property in properties.values {
93
- let descriptor = property.buildDescriptor(inRuntime: runtime)
95
+ let descriptor = try property.buildDescriptor(appContext: appContext)
94
96
  object.defineProperty(property.name, descriptor: descriptor)
95
97
  }
96
98
  }
97
99
 
98
- internal func decorateWithClasses(runtime: JavaScriptRuntime, object: JavaScriptObject) {
100
+ internal func decorateWithClasses(object: JavaScriptObject, appContext: AppContext) throws {
99
101
  for klass in classes.values {
100
- object.setProperty(klass.name, value: klass.build(inRuntime: runtime))
102
+ object.setProperty(klass.name, value: try klass.build(appContext: appContext))
101
103
  }
102
104
  }
103
105
  }
@@ -9,7 +9,7 @@ protocol AnyPropertyComponent {
9
9
  /**
10
10
  Creates the JavaScript object representing the property descriptor.
11
11
  */
12
- func buildDescriptor(inRuntime runtime: JavaScriptRuntime) -> JavaScriptObject
12
+ func buildDescriptor(appContext: AppContext) throws -> JavaScriptObject
13
13
  }
14
14
 
15
15
  public final class PropertyComponent<OwnerType>: AnyDefinition, AnyPropertyComponent {
@@ -119,60 +119,66 @@ public final class PropertyComponent<OwnerType>: AnyDefinition, AnyPropertyCompo
119
119
 
120
120
  // MARK: - Internals
121
121
 
122
- internal func getValue<ValueType>(owner: OwnerType? = nil) throws -> ValueType? {
122
+ internal func getValue<ValueType>(owner: OwnerType? = nil, appContext: AppContext) throws -> ValueType? {
123
123
  let owner = owner as? AnyObject
124
- let value = try getter?.call(by: owner, withArguments: [])
124
+ let value = try getter?.call(by: owner, withArguments: [], appContext: appContext)
125
125
  return value as? ValueType
126
126
  }
127
127
 
128
- internal func setValue(_ value: Any, owner: OwnerType? = nil) {
128
+ internal func setValue(_ value: Any, owner: OwnerType? = nil, appContext: AppContext) {
129
129
  let owner = owner as? AnyObject
130
- _ = try? setter?.call(by: owner, withArguments: [value])
130
+ _ = try? setter?.call(by: owner, withArguments: [value], appContext: appContext)
131
131
  }
132
132
 
133
133
  /**
134
134
  Creates the JavaScript function that will be used as a getter of the property.
135
135
  */
136
- internal func buildGetter(inRuntime runtime: JavaScriptRuntime) -> JavaScriptObject {
137
- return runtime.createSyncFunction(name, argsCount: 0) { [weak self, name] this, args in
138
- guard let self = self else {
136
+ internal func buildGetter(appContext: AppContext) throws -> JavaScriptObject {
137
+ return try appContext.runtime.createSyncFunction(name, argsCount: 0) { [weak appContext, weak self, name] this, args in
138
+ guard let appContext else {
139
+ throw Exceptions.AppContextLost()
140
+ }
141
+ guard let self else {
139
142
  throw NativePropertyUnavailableException(name)
140
143
  }
141
144
  guard let getter = self.getter else {
142
145
  return
143
146
  }
144
- return try getter.call(by: this, withArguments: args)
147
+ return try getter.call(by: this, withArguments: args, appContext: appContext)
145
148
  }
146
149
  }
147
150
 
148
151
  /**
149
152
  Creates the JavaScript function that will be used as a setter of the property.
150
153
  */
151
- internal func buildSetter(inRuntime runtime: JavaScriptRuntime) -> JavaScriptObject {
152
- return runtime.createSyncFunction(name, argsCount: 1) { [weak self, name] this, args in
153
- guard let self = self else {
154
+ internal func buildSetter(appContext: AppContext) throws -> JavaScriptObject {
155
+ return try appContext.runtime.createSyncFunction(name, argsCount: 1) { [weak appContext, weak self, name] this, args in
156
+ guard let appContext else {
157
+ throw Exceptions.AppContextLost()
158
+ }
159
+ guard let self else {
154
160
  throw NativePropertyUnavailableException(name)
155
161
  }
156
162
  guard let setter = self.setter else {
157
163
  return
158
164
  }
159
- return try setter.call(by: this, withArguments: args)
165
+ return try setter.call(by: this, withArguments: args, appContext: appContext)
160
166
  }
161
167
  }
162
168
 
163
169
  /**
164
170
  Creates the JavaScript object representing the property descriptor.
165
171
  */
166
- internal func buildDescriptor(inRuntime runtime: JavaScriptRuntime) -> JavaScriptObject {
167
- let descriptor = runtime.createObject()
172
+ internal func buildDescriptor(appContext: AppContext) throws -> JavaScriptObject {
173
+ let descriptor = try appContext.runtime.createObject()
168
174
 
169
175
  descriptor.setProperty("enumerable", value: true)
170
176
 
171
177
  if getter != nil {
172
- descriptor.setProperty("get", value: buildGetter(inRuntime: runtime))
178
+ descriptor.setProperty("get", value: try buildGetter(appContext: appContext))
173
179
  }
174
180
  if setter != nil {
175
- descriptor.setProperty("set", value: buildSetter(inRuntime: runtime))
181
+ descriptor.setProperty("set", value: try buildSetter(appContext: appContext))
176
182
  }
177
183
  return descriptor
178
184
  }
@@ -19,5 +19,5 @@ internal protocol AnyFieldInternal: AnyField {
19
19
  */
20
20
  var isRequired: Bool { get }
21
21
 
22
- func set(_ newValue: Any?) throws
22
+ func set(_ newValue: Any?, appContext: AppContext) throws
23
23
  }
@@ -74,12 +74,12 @@ public final class Field<Type>: AnyFieldInternal {
74
74
  /**
75
75
  Sets the wrapped value with a value of `Any` type.
76
76
  */
77
- internal func set(_ newValue: Any?) throws {
77
+ internal func set(_ newValue: Any?, appContext: AppContext) throws {
78
78
  if newValue == nil && (!isOptional || isRequired) {
79
79
  throw FieldRequiredException(key!)
80
80
  }
81
81
  do {
82
- if let value = try fieldType.cast(newValue) as? Type {
82
+ if let value = try fieldType.cast(newValue, appContext: appContext) as? Type {
83
83
  wrappedValue = value
84
84
  }
85
85
  } catch {