expo-modules-core 0.9.2 → 0.11.1

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 (193) hide show
  1. package/CHANGELOG.md +33 -3
  2. package/README.md +3 -3
  3. package/android/CMakeLists.txt +163 -0
  4. package/android/build.gradle +343 -5
  5. package/android/src/main/cpp/CachedReferencesRegistry.cpp +65 -0
  6. package/android/src/main/cpp/CachedReferencesRegistry.h +80 -0
  7. package/android/src/main/cpp/Exceptions.cpp +22 -0
  8. package/android/src/main/cpp/Exceptions.h +38 -0
  9. package/android/src/main/cpp/ExpoModulesHostObject.cpp +47 -0
  10. package/android/src/main/cpp/ExpoModulesHostObject.h +32 -0
  11. package/android/src/main/cpp/JNIFunctionBody.cpp +44 -0
  12. package/android/src/main/cpp/JNIFunctionBody.h +50 -0
  13. package/android/src/main/cpp/JNIInjector.cpp +23 -0
  14. package/android/src/main/cpp/JSIInteropModuleRegistry.cpp +122 -0
  15. package/android/src/main/cpp/JSIInteropModuleRegistry.h +96 -0
  16. package/android/src/main/cpp/JSIObjectWrapper.h +33 -0
  17. package/android/src/main/cpp/JSITypeConverter.h +84 -0
  18. package/android/src/main/cpp/JavaScriptModuleObject.cpp +219 -0
  19. package/android/src/main/cpp/JavaScriptModuleObject.h +144 -0
  20. package/android/src/main/cpp/JavaScriptObject.cpp +125 -0
  21. package/android/src/main/cpp/JavaScriptObject.h +131 -0
  22. package/android/src/main/cpp/JavaScriptRuntime.cpp +127 -0
  23. package/android/src/main/cpp/JavaScriptRuntime.h +87 -0
  24. package/android/src/main/cpp/JavaScriptValue.cpp +172 -0
  25. package/android/src/main/cpp/JavaScriptValue.h +78 -0
  26. package/android/src/main/cpp/MethodMetadata.cpp +340 -0
  27. package/android/src/main/cpp/MethodMetadata.h +131 -0
  28. package/android/src/main/java/expo/modules/adapters/react/NativeModulesProxy.java +2 -0
  29. package/android/src/main/java/expo/modules/core/errors/ContextDestroyedException.kt +7 -0
  30. package/android/src/main/java/expo/modules/interfaces/permissions/Permissions.java +30 -0
  31. package/android/src/main/java/expo/modules/kotlin/AppContext.kt +98 -3
  32. package/android/src/main/java/expo/modules/kotlin/ConcatIterator.kt +18 -0
  33. package/android/src/main/java/expo/modules/kotlin/KotlinInteropModuleRegistry.kt +15 -12
  34. package/android/src/main/java/expo/modules/kotlin/ModuleHolder.kt +45 -3
  35. package/android/src/main/java/expo/modules/kotlin/activityaware/AppCompatActivityAware.kt +49 -0
  36. package/android/src/main/java/expo/modules/kotlin/activityaware/AppCompatActivityAwareHelper.kt +43 -0
  37. package/android/src/main/java/expo/modules/kotlin/activityaware/OnActivityAvailableListener.kt +18 -0
  38. package/android/src/main/java/expo/modules/kotlin/activityresult/ActivityResultsManager.kt +99 -0
  39. package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultCaller.kt +25 -0
  40. package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultContract.kt +27 -0
  41. package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultFallbackCallback.kt +17 -0
  42. package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultLauncher.kt +30 -0
  43. package/android/src/main/java/expo/modules/kotlin/activityresult/AppContextActivityResultRegistry.kt +358 -0
  44. package/android/src/main/java/expo/modules/kotlin/activityresult/DataPersistor.kt +135 -0
  45. package/android/src/main/java/expo/modules/kotlin/defaultmodules/ErrorManagerModule.kt +2 -2
  46. package/android/src/main/java/expo/modules/kotlin/exception/CodedException.kt +13 -0
  47. package/android/src/main/java/expo/modules/kotlin/exception/ExceptionDecorator.kt +2 -0
  48. package/android/src/main/java/expo/modules/kotlin/functions/AnyFunction.kt +53 -15
  49. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunction.kt +35 -7
  50. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionBuilder.kt +13 -121
  51. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionComponent.kt +21 -0
  52. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionWithPromiseComponent.kt +21 -0
  53. package/android/src/main/java/expo/modules/kotlin/functions/SuspendFunctionComponent.kt +63 -0
  54. package/android/src/main/java/expo/modules/kotlin/functions/SyncFunctionComponent.kt +36 -0
  55. package/android/src/main/java/expo/modules/kotlin/jni/CppType.kt +19 -0
  56. package/android/src/main/java/expo/modules/kotlin/jni/JNIFunctionBody.kt +39 -0
  57. package/android/src/main/java/expo/modules/kotlin/jni/JSIInteropModuleRegistry.kt +89 -0
  58. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptModuleObject.kt +46 -0
  59. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptObject.kt +113 -0
  60. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptValue.kt +35 -0
  61. package/android/src/main/java/expo/modules/kotlin/modules/Module.kt +16 -6
  62. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionBuilder.kt +5 -500
  63. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionData.kt +30 -5
  64. package/android/src/main/java/expo/modules/kotlin/objects/ObjectDefinitionBuilder.kt +271 -0
  65. package/android/src/main/java/expo/modules/kotlin/objects/ObjectDefinitionData.kt +21 -0
  66. package/android/src/main/java/expo/modules/kotlin/objects/PropertyComponent.kt +54 -0
  67. package/android/src/main/java/expo/modules/kotlin/objects/PropertyComponentBuilder.kt +32 -0
  68. package/android/src/main/java/expo/modules/kotlin/providers/AppContextProvider.kt +14 -0
  69. package/android/src/main/java/expo/modules/kotlin/providers/CurrentActivityProvider.kt +22 -0
  70. package/android/src/main/java/expo/modules/kotlin/records/RecordTypeConverter.kt +19 -2
  71. package/android/src/main/java/expo/modules/kotlin/types/AnyType.kt +3 -2
  72. package/android/src/main/java/expo/modules/kotlin/types/AnyTypeConverter.kt +36 -0
  73. package/android/src/main/java/expo/modules/kotlin/types/ArrayTypeConverter.kt +7 -2
  74. package/android/src/main/java/expo/modules/kotlin/types/BasicTypeConverters.kt +68 -20
  75. package/android/src/main/java/expo/modules/kotlin/types/EnumTypeConverter.kt +50 -22
  76. package/android/src/main/java/expo/modules/kotlin/types/ListTypeConverter.kt +18 -2
  77. package/android/src/main/java/expo/modules/kotlin/types/MapTypeConverter.kt +18 -2
  78. package/android/src/main/java/expo/modules/kotlin/types/PairTypeConverter.kt +17 -2
  79. package/android/src/main/java/expo/modules/kotlin/types/TypeConverter.kt +43 -3
  80. package/android/src/main/java/expo/modules/kotlin/types/TypeConverterProvider.kt +12 -0
  81. package/android/src/main/java/expo/modules/kotlin/views/ViewGroupDefinitionBuilder.kt +0 -41
  82. package/android/src/main/java/expo/modules/kotlin/views/ViewManagerDefinitionBuilder.kt +0 -33
  83. package/build/NativeModulesProxy.native.d.ts.map +1 -1
  84. package/build/NativeModulesProxy.native.js +9 -3
  85. package/build/NativeModulesProxy.native.js.map +1 -1
  86. package/build/PermissionsInterface.d.ts +29 -0
  87. package/build/PermissionsInterface.d.ts.map +1 -1
  88. package/build/PermissionsInterface.js +9 -0
  89. package/build/PermissionsInterface.js.map +1 -1
  90. package/ios/AppDelegates/EXAppDelegatesLoader.m +1 -2
  91. package/ios/ExpoModulesCore.podspec +3 -2
  92. package/ios/JSI/EXJSIConversions.mm +6 -0
  93. package/ios/JSI/EXJSIInstaller.h +15 -21
  94. package/ios/JSI/EXJSIInstaller.mm +39 -3
  95. package/ios/JSI/EXJSIUtils.h +48 -3
  96. package/ios/JSI/EXJSIUtils.mm +88 -4
  97. package/ios/JSI/EXJavaScriptObject.h +11 -18
  98. package/ios/JSI/EXJavaScriptObject.mm +37 -18
  99. package/ios/JSI/EXJavaScriptRuntime.h +43 -9
  100. package/ios/JSI/EXJavaScriptRuntime.mm +70 -27
  101. package/ios/JSI/EXJavaScriptTypedArray.h +30 -0
  102. package/ios/JSI/EXJavaScriptTypedArray.mm +29 -0
  103. package/ios/JSI/EXJavaScriptValue.h +3 -2
  104. package/ios/JSI/EXJavaScriptValue.mm +17 -20
  105. package/ios/JSI/EXJavaScriptWeakObject.h +23 -0
  106. package/ios/JSI/EXJavaScriptWeakObject.mm +53 -0
  107. package/ios/JSI/EXObjectDeallocator.h +27 -0
  108. package/ios/JSI/ExpoModulesHostObject.h +3 -3
  109. package/ios/JSI/ExpoModulesHostObject.mm +4 -4
  110. package/ios/JSI/JavaScriptRuntime.swift +38 -1
  111. package/ios/JSI/JavaScriptValue.swift +7 -0
  112. package/ios/JSI/TypedArray.cpp +67 -0
  113. package/ios/JSI/TypedArray.h +46 -0
  114. package/ios/ModuleRegistryAdapter/EXModuleRegistryAdapter.m +0 -11
  115. package/ios/NativeModulesProxy/EXNativeModulesProxy.h +17 -10
  116. package/ios/NativeModulesProxy/EXNativeModulesProxy.mm +86 -77
  117. package/ios/NativeModulesProxy/NativeModulesProxyModule.swift +17 -0
  118. package/ios/Services/EXReactNativeEventEmitter.h +2 -2
  119. package/ios/Services/EXReactNativeEventEmitter.m +11 -6
  120. package/ios/Swift/AppContext.swift +206 -28
  121. package/ios/Swift/Arguments/AnyArgument.swift +18 -0
  122. package/ios/Swift/Arguments/{Types/EnumArgumentType.swift → EnumArgument.swift} +2 -17
  123. package/ios/Swift/Classes/ClassComponent.swift +95 -0
  124. package/ios/Swift/Classes/ClassComponentElement.swift +33 -0
  125. package/ios/Swift/Classes/ClassComponentElementsBuilder.swift +34 -0
  126. package/ios/Swift/Classes/ClassComponentFactories.swift +96 -0
  127. package/ios/Swift/DynamicTypes/AnyDynamicType.swift +44 -0
  128. package/ios/Swift/DynamicTypes/DynamicArrayType.swift +56 -0
  129. package/ios/Swift/DynamicTypes/DynamicConvertibleType.swift +27 -0
  130. package/ios/Swift/DynamicTypes/DynamicEnumType.swift +27 -0
  131. package/ios/Swift/DynamicTypes/DynamicOptionalType.swift +63 -0
  132. package/ios/Swift/DynamicTypes/DynamicRawType.swift +33 -0
  133. package/ios/Swift/DynamicTypes/DynamicSharedObjectType.swift +37 -0
  134. package/ios/Swift/DynamicTypes/DynamicType.swift +39 -0
  135. package/ios/Swift/DynamicTypes/DynamicTypedArrayType.swift +46 -0
  136. package/ios/Swift/Exceptions/ChainableException.swift +3 -3
  137. package/ios/Swift/Exceptions/CodedError.swift +1 -1
  138. package/ios/Swift/Exceptions/Exception.swift +8 -6
  139. package/ios/Swift/Exceptions/UnexpectedException.swift +2 -1
  140. package/ios/Swift/ExpoBridgeModule.m +5 -0
  141. package/ios/Swift/ExpoBridgeModule.swift +79 -0
  142. package/ios/Swift/Functions/AnyFunction.swift +33 -31
  143. package/ios/Swift/Functions/AsyncFunctionComponent.swift +196 -59
  144. package/ios/Swift/Functions/SyncFunctionComponent.swift +142 -58
  145. package/ios/Swift/JavaScriptUtils.swift +32 -57
  146. package/ios/Swift/Logging/LogHandlers.swift +39 -0
  147. package/ios/Swift/Logging/LogType.swift +62 -0
  148. package/ios/Swift/Logging/Logger.swift +201 -0
  149. package/ios/Swift/ModuleHolder.swift +19 -54
  150. package/ios/Swift/ModuleRegistry.swift +7 -1
  151. package/ios/Swift/Modules/AnyModule.swift +3 -3
  152. package/ios/Swift/ModulesProvider.swift +2 -0
  153. package/ios/Swift/Objects/JavaScriptObjectBuilder.swift +37 -0
  154. package/ios/Swift/Objects/ObjectDefinition.swift +74 -1
  155. package/ios/Swift/Objects/ObjectDefinitionComponents.swift +77 -68
  156. package/ios/Swift/Objects/PropertyComponent.swift +147 -0
  157. package/ios/Swift/Promise.swift +17 -4
  158. package/ios/Swift/Records/Field.swift +2 -2
  159. package/ios/Swift/SharedObjects/SharedObject.swift +20 -0
  160. package/ios/Swift/SharedObjects/SharedObjectRegistry.swift +129 -0
  161. package/ios/Swift/TypedArrays/AnyTypedArray.swift +11 -0
  162. package/ios/Swift/TypedArrays/ConcreteTypedArrays.swift +56 -0
  163. package/ios/Swift/TypedArrays/GenericTypedArray.swift +49 -0
  164. package/ios/Swift/TypedArrays/TypedArray.swift +80 -0
  165. package/ios/Swift/Utilities.swift +28 -0
  166. package/ios/Swift/Views/ConcreteViewProp.swift +3 -3
  167. package/ios/Swift/Views/ViewManagerDefinitionComponents.swift +2 -2
  168. package/ios/Tests/ClassComponentSpec.swift +210 -0
  169. package/ios/Tests/DynamicTypeSpec.swift +336 -0
  170. package/ios/Tests/EnumArgumentSpec.swift +48 -0
  171. package/ios/Tests/ExpoModulesSpec.swift +17 -3
  172. package/ios/Tests/FunctionSpec.swift +167 -118
  173. package/ios/Tests/Mocks/ModuleMocks.swift +1 -1
  174. package/ios/Tests/PropertyComponentSpec.swift +95 -0
  175. package/ios/Tests/SharedObjectRegistrySpec.swift +109 -0
  176. package/ios/Tests/TypedArraysSpec.swift +136 -0
  177. package/package.json +2 -2
  178. package/src/NativeModulesProxy.native.ts +13 -3
  179. package/src/PermissionsInterface.ts +29 -0
  180. package/src/ts-declarations/ExpoModules.d.ts +7 -0
  181. package/tsconfig.json +1 -1
  182. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionWithPromise.kt +0 -15
  183. package/android/src/main/java/expo/modules/kotlin/functions/AsyncSuspendFunction.kt +0 -36
  184. package/ios/Swift/Arguments/AnyArgumentType.swift +0 -13
  185. package/ios/Swift/Arguments/ArgumentType.swift +0 -28
  186. package/ios/Swift/Arguments/Types/ArrayArgumentType.swift +0 -42
  187. package/ios/Swift/Arguments/Types/ConvertibleArgumentType.swift +0 -16
  188. package/ios/Swift/Arguments/Types/OptionalArgumentType.swift +0 -49
  189. package/ios/Swift/Arguments/Types/PromiseArgumentType.swift +0 -15
  190. package/ios/Swift/Arguments/Types/RawArgumentType.swift +0 -25
  191. package/ios/Swift/Functions/ConcreteFunction.swift +0 -103
  192. package/ios/Swift/SwiftInteropBridge.swift +0 -155
  193. package/ios/Tests/ArgumentTypeSpec.swift +0 -143
@@ -1,97 +1,72 @@
1
1
  // Copyright 2022-present 650 Industries. All rights reserved.
2
2
 
3
- // FIXME: Calling module's functions needs solid refactoring to not reference the module holder.
4
- // Instead, it should be possible to directly call the function instance from here. (added by @tsapeta)
5
-
6
- /**
7
- Creates a block that is executed when the module's async function is called.
8
- */
9
- internal func createAsyncFunctionBlock(holder: ModuleHolder, name functionName: String) -> JSAsyncFunctionBlock {
10
- let moduleName = holder.name
11
- return { [weak holder, moduleName] args, resolve, reject in
12
- guard let holder = holder else {
13
- let exception = ModuleUnavailableException(moduleName)
14
- reject(exception.code, exception.description, exception)
15
- return
16
- }
17
- holder.call(function: functionName, args: args) { result, error in
18
- if let error = error {
19
- reject(error.code, error.description, error)
20
- } else {
21
- resolve(result)
22
- }
23
- }
24
- }
25
- }
26
-
27
- /**
28
- Creates a block that is executed when the module's sync function is called.
29
- */
30
- internal func createSyncFunctionBlock(holder: ModuleHolder, name functionName: String) -> JSSyncFunctionBlock {
31
- return { [weak holder] args in
32
- guard let holder = holder else {
33
- return nil
34
- }
35
- return holder.callSync(function: functionName, args: args)
36
- }
37
- }
38
-
39
3
  // MARK: - Arguments
40
4
 
41
5
  /**
42
- Tries to cast given argument to the type that is wrapped by the argument type.
6
+ Tries to cast a given value to the type that is wrapped by the dynamic type.
43
7
  - Parameters:
44
- - argument: A value to be cast. If it's a ``JavaScriptValue``, it's first unpacked to the raw value.
45
- - argumentType: Something that implements ``AnyArgumentType`` and knows how to cast the argument.
46
- - Returns: A new value converted according to the argument type.
47
- - Throws: Rethrows various exceptions that could be thrown by the argument type wrappers.
8
+ - value: A value to be cast. If it's a ``JavaScriptValue``, it's first unpacked to the raw value.
9
+ - type: Something that implements ``AnyDynamicType`` and knows how to cast the argument.
10
+ - Returns: A new value converted according to the dynamic type.
11
+ - Throws: Rethrows various exceptions that could be thrown by the dynamic types.
48
12
  */
49
- internal func castArgument(_ argument: Any, toType argumentType: AnyArgumentType) throws -> Any {
13
+ internal func cast(_ value: Any, toType type: AnyDynamicType) throws -> Any {
50
14
  // TODO: Accept JavaScriptValue and JavaScriptObject as argument types.
51
- if let argument = argument as? JavaScriptValue {
52
- return try argumentType.cast(argument.getRaw())
15
+ if !(type is DynamicTypedArrayType), let value = value as? JavaScriptValue {
16
+ return try type.cast(value.getRaw())
53
17
  }
54
- return try argumentType.cast(argument)
18
+ return try type.cast(value)
55
19
  }
56
20
 
57
21
  /**
58
- Same as ``castArgument(_:argumentType:)`` but for an array of arguments.
22
+ Tries to cast the given arguments to the types expected by the function.
59
23
  - Parameters:
60
24
  - arguments: An array of arguments to be cast.
61
- - argumentTypes: An array of argument types in the same order as the array of arguments.
25
+ - function: A function for which to cast the arguments.
62
26
  - Returns: An array of arguments after casting. Its size is the same as the input arrays.
63
- - Throws: ``InvalidArgsNumberException`` when the sizes of arrays passed as parameters are not equal.
64
- Rethrows exceptions thrown by ``castArgument(_:argumentType:)``.
27
+ - Throws: `InvalidArgsNumberException` when the number of arguments is not equal to the actual number
28
+ of function's arguments (without an owner and promise). Rethrows exceptions thrown by `cast(_:toType:)`.
65
29
  */
66
- internal func castArguments(_ arguments: [Any], toTypes argumentTypes: [AnyArgumentType]) throws -> [Any] {
67
- if arguments.count != argumentTypes.count {
68
- throw InvalidArgsNumberException((received: arguments.count, expected: argumentTypes.count))
30
+ internal func cast(arguments: [Any], forFunction function: AnyFunction) throws -> [Any] {
31
+ if arguments.count != function.argumentsCount {
32
+ throw InvalidArgsNumberException((received: arguments.count, expected: function.argumentsCount))
69
33
  }
34
+ let argumentTypeOffset = function.takesOwner ? 1 : 0
70
35
  return try arguments.enumerated().map { index, argument in
71
- let argumentType = argumentTypes[index]
36
+ let argumentType = function.dynamicArgumentTypes[index + argumentTypeOffset]
72
37
 
73
38
  do {
74
- return try castArgument(argument, toType: argumentType)
39
+ return try cast(argument, toType: argumentType)
75
40
  } catch {
76
41
  throw ArgumentCastException((index: index, type: argumentType)).causedBy(error)
77
42
  }
78
43
  }
79
44
  }
80
45
 
46
+ /**
47
+ Prepends the owner to the array of arguments if the given function can take it.
48
+ */
49
+ internal func concat(arguments: [Any], withOwner owner: AnyObject?, forFunction function: AnyFunction) -> [Any] {
50
+ if function.takesOwner, let owner = try? function.dynamicArgumentTypes.first?.cast(owner) {
51
+ return [owner] + arguments
52
+ }
53
+ return arguments
54
+ }
55
+
56
+ // MARK: - Exceptions
57
+
81
58
  internal class InvalidArgsNumberException: GenericException<(received: Int, expected: Int)> {
82
59
  override var reason: String {
83
60
  "Received \(param.received) arguments, but \(param.expected) was expected"
84
61
  }
85
62
  }
86
63
 
87
- internal class ArgumentCastException: GenericException<(index: Int, type: AnyArgumentType)> {
64
+ internal class ArgumentCastException: GenericException<(index: Int, type: AnyDynamicType)> {
88
65
  override var reason: String {
89
66
  "Argument at index '\(param.index)' couldn't be cast to type \(param.type.description)"
90
67
  }
91
68
  }
92
69
 
93
- // MARK: - Exceptions
94
-
95
70
  private class ModuleUnavailableException: GenericException<String> {
96
71
  override var reason: String {
97
72
  "Module '\(param)' is no longer available"
@@ -0,0 +1,39 @@
1
+ // Copyright 2022-present 650 Industries. All rights reserved.
2
+
3
+ import os.log
4
+
5
+ /**
6
+ The protocol that needs to be implemented by log handlers.
7
+ */
8
+ internal protocol LogHandler {
9
+ init(category: String)
10
+
11
+ func log(type: LogType, _ message: String)
12
+ }
13
+
14
+ /**
15
+ The log handler that uses the new `os.Logger` API.
16
+ */
17
+ @available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *)
18
+ internal class OSLogHandler: LogHandler {
19
+ private let osLogger: os.Logger
20
+
21
+ required init(category: String) {
22
+ osLogger = os.Logger(subsystem: "dev.expo.modules", category: category)
23
+ }
24
+
25
+ func log(type: LogType, _ message: String) {
26
+ osLogger.log(level: type.toOSLogType(), "\(message)")
27
+ }
28
+ }
29
+
30
+ /**
31
+ Simple log handler that forwards all logs to `print` function.
32
+ */
33
+ internal class PrintLogHandler: LogHandler {
34
+ required init(category: String) {}
35
+
36
+ func log(type: LogType, _ message: String) {
37
+ print(message)
38
+ }
39
+ }
@@ -0,0 +1,62 @@
1
+ // Copyright 2022-present 650 Industries. All rights reserved.
2
+
3
+ import os.log
4
+
5
+ /**
6
+ An enum with available log types.
7
+ */
8
+ public enum LogType: Int {
9
+ case trace = 0
10
+ case timer = 1
11
+ case stacktrace = 2
12
+ case debug = 3
13
+ case info = 4
14
+ case warn = 5
15
+ case error = 6
16
+ case fatal = 7
17
+
18
+ /**
19
+ The string that is used to prefix the messages of this log type.
20
+ Logs in Xcode and Console apps are always with the white text,
21
+ so we use colored circle emojis to distinguish different types of logs.
22
+ */
23
+ var prefix: String {
24
+ switch self {
25
+ case .trace:
26
+ return "⚪️"
27
+ case .timer:
28
+ return "🟤"
29
+ case .stacktrace:
30
+ return "🟣"
31
+ case .debug:
32
+ return "🔵"
33
+ case .info:
34
+ return "🟢"
35
+ case .warn:
36
+ return "🟡"
37
+ case .error:
38
+ return "🟠"
39
+ case .fatal:
40
+ return "🔴"
41
+ }
42
+ }
43
+
44
+ /**
45
+ Maps the log types to the log types used by the `os.log` logger.
46
+ */
47
+ @available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *)
48
+ func toOSLogType() -> OSLogType {
49
+ switch self {
50
+ case .trace, .timer, .stacktrace, .debug:
51
+ return .debug
52
+ case .info:
53
+ return .info
54
+ case .warn:
55
+ return .default
56
+ case .error:
57
+ return .error
58
+ case .fatal:
59
+ return .fault
60
+ }
61
+ }
62
+ }
@@ -0,0 +1,201 @@
1
+ // Copyright 2021-present 650 Industries. All rights reserved.
2
+
3
+ import Dispatch
4
+
5
+ public let log = Logger(category: "expo")
6
+
7
+ public class Logger {
8
+ #if DEBUG || EXPO_CONFIGURATION_DEBUG
9
+ private var minLevel: LogType = .trace
10
+ #else
11
+ private var minLevel: LogType = .info
12
+ #endif
13
+
14
+ private let category: String
15
+
16
+ private var handlers: [LogHandler] = []
17
+
18
+ init(category: String = "main") {
19
+ self.category = category
20
+
21
+ if #available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *) {
22
+ addHandler(withType: OSLogHandler.self)
23
+ } else {
24
+ addHandler(withType: PrintLogHandler.self)
25
+ }
26
+ }
27
+
28
+ internal func addHandler<LogHandlerType: LogHandler>(_ handler: LogHandlerType) {
29
+ handlers.append(handler)
30
+ }
31
+
32
+ internal func addHandler<LogHandlerType: LogHandler>(withType: LogHandlerType.Type) {
33
+ addHandler(LogHandlerType(category: category))
34
+ }
35
+
36
+ // MARK: - Public logging functions
37
+
38
+ /**
39
+ The most verbose log level that captures all the details about the behavior of the implementation.
40
+ It is mostly diagnostic and is more granular and finer than `debug` log level.
41
+ These logs should not be committed to the repository and are ignored in the release builds.
42
+ */
43
+ public func trace(_ items: Any...) {
44
+ log(type: .trace, items)
45
+ }
46
+
47
+ /**
48
+ Used to log diagnostically helpful information. As opposed to `trace`,
49
+ it is acceptable to commit these logs to the repository. Ignored in the release builds.
50
+ */
51
+ public func debug(_ items: Any...) {
52
+ log(type: .debug, items)
53
+ }
54
+
55
+ /**
56
+ For information that should be logged under normal conditions such as successful initialization
57
+ and notable events that are not considered an error but might be useful for debugging purposes in the release builds.
58
+ */
59
+ public func info(_ items: Any...) {
60
+ log(type: .info, items)
61
+ }
62
+
63
+ /**
64
+ Used to log an unwanted state that has not much impact on the process so it can be continued, but could potentially become an error.
65
+ */
66
+ public func warn(_ items: Any...) {
67
+ log(type: .warn, items)
68
+ }
69
+
70
+ /**
71
+ Logs unwanted state that has an impact on the currently running process, but the entire app can continue to run.
72
+ */
73
+ public func error(_ items: Any...) {
74
+ log(type: .error, items)
75
+ }
76
+
77
+ /**
78
+ Logs critical error due to which the entire app cannot continue to run.
79
+ */
80
+ public func fatal(_ items: Any...) {
81
+ log(type: .fatal, items)
82
+ }
83
+
84
+ /**
85
+ Logs the stack of symbols on the current thread.
86
+ */
87
+ public func stacktrace(type: LogType = .stacktrace, file: String = #fileID, line: UInt = #line) {
88
+ guard type.rawValue >= minLevel.rawValue else {
89
+ return
90
+ }
91
+ let queueName = OperationQueue.current?.underlyingQueue?.label ?? "<unknown>"
92
+
93
+ // Get the call stack symbols without the first symbol as it points right here.
94
+ let symbols = Thread.callStackSymbols.dropFirst()
95
+
96
+ log(type: type, "The stacktrace from '\(file):\(line)' on queue '\(queueName)':")
97
+
98
+ symbols.forEach { symbol in
99
+ let formattedSymbol = reformatStackSymbol(symbol)
100
+ log(type: type, "≫ \(formattedSymbol)")
101
+ }
102
+ }
103
+
104
+ /**
105
+ Allows the logger instance to be called as a function. The same as `logger.debug(...)`.
106
+ */
107
+ public func callAsFunction(_ items: Any...) {
108
+ log(type: .debug, items)
109
+ }
110
+
111
+ // MARK: - Timers
112
+
113
+ /**
114
+ Stores the timers created by `timeStart` function.
115
+ */
116
+ private var timers: [String: DispatchTime] = [:]
117
+
118
+ /**
119
+ Starts the timer to measure how much time the following operations take.
120
+ */
121
+ public func timeStart(_ id: String) {
122
+ guard LogType.timer.rawValue >= minLevel.rawValue else {
123
+ return
124
+ }
125
+ log(type: .timer, "Starting timer '\(id)'")
126
+ timers[id] = DispatchTime.now()
127
+ }
128
+
129
+ /**
130
+ Stops the timer and logs how much time elapsed since it started.
131
+ */
132
+ public func timeEnd(_ id: String) {
133
+ guard LogType.timer.rawValue >= minLevel.rawValue else {
134
+ return
135
+ }
136
+ guard let startTime = timers[id] else {
137
+ log(type: .timer, "Timer '\(id)' has not been started!")
138
+ return
139
+ }
140
+ let endTime = DispatchTime.now()
141
+ let diff = Double(endTime.uptimeNanoseconds - startTime.uptimeNanoseconds) / 1_000_000
142
+ log(type: .timer, "Timer '\(id)' has finished in: \(diff) seconds")
143
+ timers.removeValue(forKey: id)
144
+ }
145
+
146
+ /**
147
+ Measures how much time it takes to run given closure. Returns the same value as the closure returned.
148
+ */
149
+ public func time<ReturnType>(_ id: String, _ closure: () -> ReturnType) -> ReturnType {
150
+ timeStart(id)
151
+ let result = closure()
152
+ timeEnd(id)
153
+ return result
154
+ }
155
+
156
+ // MARK: - Changing the category
157
+
158
+ public func category(_ category: String) -> Logger {
159
+ return Logger(category: category)
160
+ }
161
+
162
+ // MARK: - Private logging functions
163
+
164
+ private func log(type: LogType = .trace, _ items: [Any]) {
165
+ guard type.rawValue >= minLevel.rawValue else {
166
+ return
167
+ }
168
+ let messages = items
169
+ .map { describe(value: $0) }
170
+ .joined(separator: " ")
171
+ .split(whereSeparator: \.isNewline)
172
+ .map { "\(type.prefix) \($0)" }
173
+
174
+ handlers.forEach { handler in
175
+ messages.forEach { message in
176
+ handler.log(type: type, message)
177
+ }
178
+ }
179
+ }
180
+
181
+ private func log(type: LogType = .trace, _ items: Any...) {
182
+ log(type: type, items)
183
+ }
184
+ }
185
+
186
+ fileprivate func reformatStackSymbol(_ symbol: String) -> String {
187
+ return symbol.replacingOccurrences(of: #"^\d+\s+"#, with: "", options: .regularExpression)
188
+ }
189
+
190
+ fileprivate func describe(value: Any) -> String {
191
+ if let value = value as? String {
192
+ return value
193
+ }
194
+ if let value = value as? CustomDebugStringConvertible {
195
+ return value.debugDescription
196
+ }
197
+ if let value = value as? CustomStringConvertible {
198
+ return value.description
199
+ }
200
+ return String(describing: value)
201
+ }
@@ -56,54 +56,33 @@ public final class ModuleHolder {
56
56
  Merges all `constants` definitions into one dictionary.
57
57
  */
58
58
  func getConstants() -> [String: Any?] {
59
- return definition.constants.reduce(into: [String: Any?]()) { dict, definition in
60
- dict.merge(definition.body()) { $1 }
61
- }
59
+ return definition.getConstants()
62
60
  }
63
61
 
64
62
  // MARK: Calling functions
65
63
 
66
- func call(function functionName: String, args: [Any], promise: Promise) {
67
- do {
68
- guard let function = definition.functions[functionName] else {
69
- throw FunctionNotFoundException((functionName: functionName, moduleName: self.name))
70
- }
71
- let queue = function.queue ?? DispatchQueue.global(qos: .default)
72
-
73
- // Given arguments can be:
74
- // - Swift primitives when invoked through the bridge and in unit tests
75
- // - `JavaScriptValue`s when the function is called through the JSI
76
- // The latter need to be unpacked to Swift primitives on the JS thread,
77
- // so we do the casting before the function call is scheduled on the queue.
78
- let arguments = try castArguments(args, toTypes: function.argumentTypes)
79
-
80
- queue.async {
81
- function.call(args: arguments, promise: promise)
82
- }
83
- } catch let error as CodedError {
84
- promise.reject(error)
85
- } catch {
86
- promise.reject(UnexpectedException(error))
87
- }
88
- }
89
-
90
- func call(function functionName: String, args: [Any], _ callback: @escaping (Any?, CodedError?) -> Void = { _, _ in }) {
91
- let promise = Promise {
92
- callback($0, nil)
93
- } rejecter: {
94
- callback(nil, $0)
64
+ func call(function functionName: String, args: [Any], _ callback: @escaping (FunctionCallResult) -> () = { _ in }) {
65
+ guard let function = definition.functions[functionName] else {
66
+ callback(.failure(FunctionNotFoundException((functionName: functionName, moduleName: self.name))))
67
+ return
95
68
  }
96
- call(function: functionName, args: args, promise: promise)
69
+ function.call(by: self, withArguments: args, callback: callback)
97
70
  }
98
71
 
99
72
  @discardableResult
100
73
  func callSync(function functionName: String, args: [Any]) -> Any? {
101
- guard let function = definition.functions[functionName] else {
74
+ guard let function = definition.functions[functionName] as? AnySyncFunctionComponent else {
102
75
  return nil
103
76
  }
104
77
  do {
105
- let arguments = try castArguments(args, toTypes: function.argumentTypes)
106
- return function.callSync(args: arguments)
78
+ let arguments = try cast(arguments: args, forFunction: function)
79
+ let result = try function.call(by: self, withArguments: arguments)
80
+
81
+ if let result = result as? SharedObject {
82
+ let jsObject = SharedObjectRegistry.ensureSharedJavaScriptObject(runtime: appContext!.runtime!, nativeObject: result)
83
+ return jsObject
84
+ }
85
+ return result
107
86
  } catch {
108
87
  return error
109
88
  }
@@ -119,24 +98,10 @@ public final class ModuleHolder {
119
98
  */
120
99
  private func createJavaScriptModuleObject() -> JavaScriptObject? {
121
100
  // It might be impossible to create any object at the moment (e.g. remote debugging, app context destroyed)
122
- guard let object = appContext?.runtime?.createObject() else {
101
+ guard let runtime = appContext?.runtime else {
123
102
  return nil
124
103
  }
125
-
126
- // Fill in with constants
127
- for (key, value) in getConstants() {
128
- object.setProperty(key, value: value)
129
- }
130
-
131
- // Fill in with functions
132
- for (_, fn) in definition.functions {
133
- if fn.isAsync {
134
- object.setAsyncFunction(fn.name, argsCount: fn.argumentsCount, block: createAsyncFunctionBlock(holder: self, name: fn.name))
135
- } else {
136
- object.setSyncFunction(fn.name, argsCount: fn.argumentsCount, block: createSyncFunctionBlock(holder: self, name: fn.name))
137
- }
138
- }
139
- return object
104
+ return definition.build(inRuntime: runtime)
140
105
  }
141
106
 
142
107
  // MARK: Listening to native events
@@ -166,9 +131,9 @@ public final class ModuleHolder {
166
131
  */
167
132
  func modifyListenersCount(_ count: Int) {
168
133
  if count > 0 && listenersCount == 0 {
169
- _ = definition.functions["startObserving"]?.callSync(args: [])
134
+ definition.functions["startObserving"]?.call(withArguments: [])
170
135
  } else if count < 0 && listenersCount + count <= 0 {
171
- _ = definition.functions["stopObserving"]?.callSync(args: [])
136
+ definition.functions["stopObserving"]?.call(withArguments: [])
172
137
  }
173
138
  listenersCount = max(0, listenersCount + count)
174
139
  }
@@ -13,6 +13,7 @@ public final class ModuleRegistry: Sequence {
13
13
  Registers an instance of module holder.
14
14
  */
15
15
  internal func register(holder: ModuleHolder) {
16
+ log.info("Registering module '\(holder.name)'")
16
17
  registry[holder.name] = holder
17
18
  }
18
19
 
@@ -47,7 +48,10 @@ public final class ModuleRegistry: Sequence {
47
48
  }
48
49
 
49
50
  public func unregister(moduleName: String) {
50
- registry[moduleName] = nil
51
+ if registry[moduleName] != nil {
52
+ log.info("Unregistering module '\(moduleName)'")
53
+ registry[moduleName] = nil
54
+ }
51
55
  }
52
56
 
53
57
  public func has(moduleWithName moduleName: String) -> Bool {
@@ -71,12 +75,14 @@ public final class ModuleRegistry: Sequence {
71
75
  }
72
76
 
73
77
  internal func post(event: EventName) {
78
+ log.info("Posting '\(event)' event to registered modules")
74
79
  forEach { holder in
75
80
  holder.post(event: event)
76
81
  }
77
82
  }
78
83
 
79
84
  internal func post<PayloadType>(event: EventName, payload: PayloadType? = nil) {
85
+ log.info("Posting '\(event)' event to registered modules")
80
86
  forEach { holder in
81
87
  holder.post(event: event, payload: payload)
82
88
  }
@@ -16,8 +16,8 @@ public protocol AnyModule: AnyObject, AnyArgument {
16
16
 
17
17
  ```
18
18
  public func definition() -> ModuleDefinition {
19
- name("MyModule")
20
- function("myFunction") { (a: String, b: String) in
19
+ Name("MyModule")
20
+ AsyncFunction("myFunction") { (a: String, b: String) in
21
21
  "\(a) \(b)"
22
22
  }
23
23
  }
@@ -37,7 +37,7 @@ public protocol AnyModule: AnyObject, AnyArgument {
37
37
  just specify an argument of type `Promise` as the last one and use its `resolve` or `reject` functions.
38
38
 
39
39
  ```
40
- function("myFunction") { (promise: Promise) in
40
+ AsyncFunction("myFunction") { (promise: Promise) in
41
41
  DispatchQueue.main.async {
42
42
  promise.resolve("return value obtained in async callback")
43
43
  }
@@ -24,6 +24,8 @@ public protocol ModulesProviderProtocol {
24
24
  */
25
25
  @objc
26
26
  open class ModulesProvider: NSObject, ModulesProviderProtocol {
27
+ public override required init() {}
28
+
27
29
  open func getModuleClasses() -> [AnyModule.Type] {
28
30
  return []
29
31
  }
@@ -0,0 +1,37 @@
1
+ // Copyright 2022-present 650 Industries. All rights reserved.
2
+
3
+ /**
4
+ A type that can decorate a `JavaScriptObject` with some properties.
5
+ */
6
+ internal protocol JavaScriptObjectDecorator {
7
+ /**
8
+ Decorates an existing `JavaScriptObject`.
9
+ */
10
+ func decorate(object: JavaScriptObject, inRuntime runtime: JavaScriptRuntime)
11
+ }
12
+
13
+ /**
14
+ A type that can build and decorate a `JavaScriptObject` based on its attributes.
15
+ */
16
+ internal protocol JavaScriptObjectBuilder: JavaScriptObjectDecorator {
17
+ /**
18
+ Creates a decorated `JavaScriptObject` in the given runtime.
19
+ */
20
+ func build(inRuntime runtime: JavaScriptRuntime) -> JavaScriptObject
21
+ }
22
+
23
+ /**
24
+ Provides the default behavior of `JavaScriptObjectBuilder`.
25
+ The `build(inRuntime:)` creates a plain object and uses `decorate(object:)` for decoration.
26
+ */
27
+ extension JavaScriptObjectBuilder {
28
+ func build(inRuntime runtime: JavaScriptRuntime) -> JavaScriptObject {
29
+ let object = runtime.createObject()
30
+ decorate(object: object, inRuntime: runtime)
31
+ return object
32
+ }
33
+
34
+ func decorate(object: JavaScriptObject, inRuntime runtime: JavaScriptRuntime) {
35
+ // no-op by default
36
+ }
37
+ }