expo-modules-core 0.9.2 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (167) hide show
  1. package/CHANGELOG.md +18 -5
  2. package/android/CMakeLists.txt +154 -0
  3. package/android/build.gradle +293 -5
  4. package/android/src/main/cpp/Exceptions.cpp +22 -0
  5. package/android/src/main/cpp/Exceptions.h +38 -0
  6. package/android/src/main/cpp/ExpoModulesHostObject.cpp +47 -0
  7. package/android/src/main/cpp/ExpoModulesHostObject.h +32 -0
  8. package/android/src/main/cpp/JNIFunctionBody.cpp +29 -0
  9. package/android/src/main/cpp/JNIFunctionBody.h +50 -0
  10. package/android/src/main/cpp/JNIInjector.cpp +19 -0
  11. package/android/src/main/cpp/JSIInteropModuleRegistry.cpp +122 -0
  12. package/android/src/main/cpp/JSIInteropModuleRegistry.h +96 -0
  13. package/android/src/main/cpp/JSIObjectWrapper.h +33 -0
  14. package/android/src/main/cpp/JSITypeConverter.h +84 -0
  15. package/android/src/main/cpp/JavaScriptModuleObject.cpp +138 -0
  16. package/android/src/main/cpp/JavaScriptModuleObject.h +122 -0
  17. package/android/src/main/cpp/JavaScriptObject.cpp +125 -0
  18. package/android/src/main/cpp/JavaScriptObject.h +131 -0
  19. package/android/src/main/cpp/JavaScriptRuntime.cpp +127 -0
  20. package/android/src/main/cpp/JavaScriptRuntime.h +87 -0
  21. package/android/src/main/cpp/JavaScriptValue.cpp +172 -0
  22. package/android/src/main/cpp/JavaScriptValue.h +78 -0
  23. package/android/src/main/cpp/MethodMetadata.cpp +230 -0
  24. package/android/src/main/cpp/MethodMetadata.h +92 -0
  25. package/android/src/main/java/expo/modules/adapters/react/NativeModulesProxy.java +2 -0
  26. package/android/src/main/java/expo/modules/core/errors/ContextDestroyedException.kt +7 -0
  27. package/android/src/main/java/expo/modules/interfaces/permissions/Permissions.java +30 -0
  28. package/android/src/main/java/expo/modules/kotlin/AppContext.kt +49 -1
  29. package/android/src/main/java/expo/modules/kotlin/ConcatIterator.kt +18 -0
  30. package/android/src/main/java/expo/modules/kotlin/KotlinInteropModuleRegistry.kt +15 -12
  31. package/android/src/main/java/expo/modules/kotlin/ModuleHolder.kt +39 -3
  32. package/android/src/main/java/expo/modules/kotlin/defaultmodules/ErrorManagerModule.kt +2 -2
  33. package/android/src/main/java/expo/modules/kotlin/exception/CodedException.kt +13 -0
  34. package/android/src/main/java/expo/modules/kotlin/exception/ExceptionDecorator.kt +2 -0
  35. package/android/src/main/java/expo/modules/kotlin/functions/AnyFunction.kt +19 -14
  36. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunction.kt +29 -7
  37. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionBuilder.kt +13 -13
  38. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionComponent.kt +18 -0
  39. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionWithPromiseComponent.kt +18 -0
  40. package/android/src/main/java/expo/modules/kotlin/functions/SuspendFunctionComponent.kt +56 -0
  41. package/android/src/main/java/expo/modules/kotlin/functions/SyncFunctionComponent.kt +28 -0
  42. package/android/src/main/java/expo/modules/kotlin/jni/CppType.kt +18 -0
  43. package/android/src/main/java/expo/modules/kotlin/jni/JNIFunctionBody.kt +39 -0
  44. package/android/src/main/java/expo/modules/kotlin/jni/JSIInteropModuleRegistry.kt +89 -0
  45. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptModuleObject.kt +44 -0
  46. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptObject.kt +113 -0
  47. package/android/src/main/java/expo/modules/kotlin/jni/JavaScriptValue.kt +35 -0
  48. package/android/src/main/java/expo/modules/kotlin/modules/Module.kt +15 -5
  49. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionBuilder.kt +65 -111
  50. package/android/src/main/java/expo/modules/kotlin/modules/ModuleDefinitionData.kt +35 -2
  51. package/android/src/main/java/expo/modules/kotlin/providers/AppContextProvider.kt +14 -0
  52. package/android/src/main/java/expo/modules/kotlin/providers/CurrentActivityProvider.kt +22 -0
  53. package/android/src/main/java/expo/modules/kotlin/records/RecordTypeConverter.kt +19 -2
  54. package/android/src/main/java/expo/modules/kotlin/types/AnyType.kt +3 -2
  55. package/android/src/main/java/expo/modules/kotlin/types/ArrayTypeConverter.kt +7 -2
  56. package/android/src/main/java/expo/modules/kotlin/types/BasicTypeConverters.kt +68 -20
  57. package/android/src/main/java/expo/modules/kotlin/types/EnumTypeConverter.kt +50 -22
  58. package/android/src/main/java/expo/modules/kotlin/types/ListTypeConverter.kt +18 -2
  59. package/android/src/main/java/expo/modules/kotlin/types/MapTypeConverter.kt +18 -2
  60. package/android/src/main/java/expo/modules/kotlin/types/PairTypeConverter.kt +17 -2
  61. package/android/src/main/java/expo/modules/kotlin/types/TypeConverter.kt +43 -3
  62. package/android/src/main/java/expo/modules/kotlin/types/TypeConverterProvider.kt +5 -0
  63. package/build/NativeModulesProxy.native.d.ts.map +1 -1
  64. package/build/NativeModulesProxy.native.js +9 -3
  65. package/build/NativeModulesProxy.native.js.map +1 -1
  66. package/ios/AppDelegates/EXAppDelegatesLoader.m +1 -2
  67. package/ios/ExpoModulesCore.podspec +1 -1
  68. package/ios/JSI/EXJSIConversions.mm +6 -0
  69. package/ios/JSI/EXJSIInstaller.h +15 -21
  70. package/ios/JSI/EXJSIInstaller.mm +39 -3
  71. package/ios/JSI/EXJSIUtils.h +47 -3
  72. package/ios/JSI/EXJSIUtils.mm +88 -4
  73. package/ios/JSI/EXJavaScriptObject.h +11 -18
  74. package/ios/JSI/EXJavaScriptObject.mm +37 -18
  75. package/ios/JSI/EXJavaScriptRuntime.h +43 -9
  76. package/ios/JSI/EXJavaScriptRuntime.mm +70 -27
  77. package/ios/JSI/EXJavaScriptTypedArray.h +30 -0
  78. package/ios/JSI/EXJavaScriptTypedArray.mm +29 -0
  79. package/ios/JSI/EXJavaScriptValue.h +3 -2
  80. package/ios/JSI/EXJavaScriptValue.mm +17 -20
  81. package/ios/JSI/EXJavaScriptWeakObject.h +23 -0
  82. package/ios/JSI/EXJavaScriptWeakObject.mm +53 -0
  83. package/ios/JSI/EXObjectDeallocator.h +27 -0
  84. package/ios/JSI/ExpoModulesHostObject.h +3 -3
  85. package/ios/JSI/ExpoModulesHostObject.mm +4 -4
  86. package/ios/JSI/JavaScriptRuntime.swift +38 -1
  87. package/ios/JSI/JavaScriptValue.swift +7 -0
  88. package/ios/JSI/TypedArray.cpp +67 -0
  89. package/ios/JSI/TypedArray.h +46 -0
  90. package/ios/ModuleRegistryAdapter/EXModuleRegistryAdapter.m +0 -11
  91. package/ios/NativeModulesProxy/EXNativeModulesProxy.h +17 -10
  92. package/ios/NativeModulesProxy/EXNativeModulesProxy.mm +85 -77
  93. package/ios/NativeModulesProxy/NativeModulesProxyModule.swift +17 -0
  94. package/ios/Services/EXReactNativeEventEmitter.h +2 -2
  95. package/ios/Services/EXReactNativeEventEmitter.m +11 -6
  96. package/ios/Swift/AppContext.swift +208 -28
  97. package/ios/Swift/Arguments/AnyArgument.swift +18 -0
  98. package/ios/Swift/Arguments/{Types/EnumArgumentType.swift → EnumArgument.swift} +2 -17
  99. package/ios/Swift/Classes/ClassComponent.swift +95 -0
  100. package/ios/Swift/Classes/ClassComponentElement.swift +33 -0
  101. package/ios/Swift/Classes/ClassComponentElementsBuilder.swift +34 -0
  102. package/ios/Swift/Classes/ClassComponentFactories.swift +96 -0
  103. package/ios/Swift/DynamicTypes/AnyDynamicType.swift +44 -0
  104. package/ios/Swift/DynamicTypes/DynamicArrayType.swift +56 -0
  105. package/ios/Swift/DynamicTypes/DynamicConvertibleType.swift +27 -0
  106. package/ios/Swift/DynamicTypes/DynamicEnumType.swift +27 -0
  107. package/ios/Swift/DynamicTypes/DynamicOptionalType.swift +63 -0
  108. package/ios/Swift/DynamicTypes/DynamicRawType.swift +33 -0
  109. package/ios/Swift/DynamicTypes/DynamicSharedObjectType.swift +37 -0
  110. package/ios/Swift/DynamicTypes/DynamicType.swift +39 -0
  111. package/ios/Swift/DynamicTypes/DynamicTypedArrayType.swift +46 -0
  112. package/ios/Swift/Exceptions/CodedError.swift +1 -1
  113. package/ios/Swift/Exceptions/Exception.swift +8 -6
  114. package/ios/Swift/Exceptions/UnexpectedException.swift +2 -1
  115. package/ios/Swift/ExpoBridgeModule.m +5 -0
  116. package/ios/Swift/ExpoBridgeModule.swift +65 -0
  117. package/ios/Swift/Functions/AnyFunction.swift +33 -31
  118. package/ios/Swift/Functions/AsyncFunctionComponent.swift +196 -59
  119. package/ios/Swift/Functions/SyncFunctionComponent.swift +142 -58
  120. package/ios/Swift/JavaScriptUtils.swift +32 -57
  121. package/ios/Swift/Logging/LogHandlers.swift +39 -0
  122. package/ios/Swift/Logging/LogType.swift +62 -0
  123. package/ios/Swift/Logging/Logger.swift +198 -0
  124. package/ios/Swift/ModuleHolder.swift +19 -54
  125. package/ios/Swift/ModuleRegistry.swift +7 -1
  126. package/ios/Swift/Modules/AnyModule.swift +3 -3
  127. package/ios/Swift/ModulesProvider.swift +2 -0
  128. package/ios/Swift/Objects/JavaScriptObjectBuilder.swift +37 -0
  129. package/ios/Swift/Objects/ObjectDefinition.swift +74 -1
  130. package/ios/Swift/Objects/ObjectDefinitionComponents.swift +77 -68
  131. package/ios/Swift/Objects/PropertyComponent.swift +147 -0
  132. package/ios/Swift/Promise.swift +12 -3
  133. package/ios/Swift/Records/Field.swift +2 -2
  134. package/ios/Swift/SharedObjects/SharedObject.swift +20 -0
  135. package/ios/Swift/SharedObjects/SharedObjectRegistry.swift +129 -0
  136. package/ios/Swift/TypedArrays/AnyTypedArray.swift +11 -0
  137. package/ios/Swift/TypedArrays/ConcreteTypedArrays.swift +56 -0
  138. package/ios/Swift/TypedArrays/GenericTypedArray.swift +49 -0
  139. package/ios/Swift/TypedArrays/TypedArray.swift +80 -0
  140. package/ios/Swift/Utilities.swift +28 -0
  141. package/ios/Swift/Views/ConcreteViewProp.swift +3 -3
  142. package/ios/Swift/Views/ViewManagerDefinitionComponents.swift +2 -2
  143. package/ios/Tests/ClassComponentSpec.swift +210 -0
  144. package/ios/Tests/DynamicTypeSpec.swift +336 -0
  145. package/ios/Tests/EnumArgumentSpec.swift +48 -0
  146. package/ios/Tests/ExpoModulesSpec.swift +17 -3
  147. package/ios/Tests/FunctionSpec.swift +167 -118
  148. package/ios/Tests/Mocks/ModuleMocks.swift +1 -1
  149. package/ios/Tests/PropertyComponentSpec.swift +95 -0
  150. package/ios/Tests/SharedObjectRegistrySpec.swift +109 -0
  151. package/ios/Tests/TypedArraysSpec.swift +136 -0
  152. package/package.json +2 -2
  153. package/src/NativeModulesProxy.native.ts +13 -3
  154. package/src/ts-declarations/ExpoModules.d.ts +7 -0
  155. package/tsconfig.json +1 -1
  156. package/android/src/main/java/expo/modules/kotlin/functions/AsyncFunctionWithPromise.kt +0 -15
  157. package/android/src/main/java/expo/modules/kotlin/functions/AsyncSuspendFunction.kt +0 -36
  158. package/ios/Swift/Arguments/AnyArgumentType.swift +0 -13
  159. package/ios/Swift/Arguments/ArgumentType.swift +0 -28
  160. package/ios/Swift/Arguments/Types/ArrayArgumentType.swift +0 -42
  161. package/ios/Swift/Arguments/Types/ConvertibleArgumentType.swift +0 -16
  162. package/ios/Swift/Arguments/Types/OptionalArgumentType.swift +0 -49
  163. package/ios/Swift/Arguments/Types/PromiseArgumentType.swift +0 -15
  164. package/ios/Swift/Arguments/Types/RawArgumentType.swift +0 -25
  165. package/ios/Swift/Functions/ConcreteFunction.swift +0 -103
  166. package/ios/Swift/SwiftInteropBridge.swift +0 -155
  167. package/ios/Tests/ArgumentTypeSpec.swift +0 -143
@@ -0,0 +1,49 @@
1
+ // Copyright 2022-present 650 Industries. All rights reserved.
2
+
3
+ /**
4
+ Generic TypedArray with an associated numeric ContentType (e.g. UInt8, Int16, Double).
5
+ */
6
+ public class GenericTypedArray<ContentType: Numeric>: TypedArray {
7
+ /**
8
+ The unsafe mutable typed buffer that shares the same memory as the underlying JavaScript `ArrayBuffer`.
9
+ */
10
+ lazy var buffer = UnsafeMutableBufferPointer<ContentType>(start: pointer, count: length)
11
+
12
+ /**
13
+ The unsafe mutable typed pointer to the start of the array buffer.
14
+ */
15
+ lazy var pointer = rawPointer.bindMemory(to: ContentType.self, capacity: length)
16
+
17
+ public subscript(index: Int) -> ContentType {
18
+ get {
19
+ return buffer[index]
20
+ }
21
+ set {
22
+ buffer[index] = newValue
23
+ }
24
+ }
25
+
26
+ subscript(range: Range<Int>) -> [ContentType] {
27
+ get {
28
+ return Array(buffer[range])
29
+ }
30
+ set {
31
+ var newValues = newValue
32
+ newValues.withUnsafeMutableBufferPointer { newValuesBuffer in
33
+ buffer[range] = newValuesBuffer[0..<(range.upperBound - range.lowerBound)]
34
+ }
35
+ }
36
+ }
37
+
38
+ subscript(range: ClosedRange<Int>) -> [ContentType] {
39
+ get {
40
+ return Array(buffer[range])
41
+ }
42
+ set {
43
+ var newValues = newValue
44
+ newValues.withUnsafeMutableBufferPointer { newValuesBuffer in
45
+ buffer[range] = newValuesBuffer[0...(range.upperBound - range.lowerBound)]
46
+ }
47
+ }
48
+ }
49
+ }
@@ -0,0 +1,80 @@
1
+ // Copyright 2022-present 650 Industries. All rights reserved.
2
+
3
+ /**
4
+ The base class for any type of the typed array.
5
+ */
6
+ public class TypedArray: AnyTypedArray {
7
+ /**
8
+ Creates a concrete TypedArray from the given JavaScriptTypedArray
9
+ */
10
+ internal static func create(from jsTypedArray: JavaScriptTypedArray) -> TypedArray {
11
+ switch jsTypedArray.kind {
12
+ case .Int8Array:
13
+ return Int8Array(jsTypedArray)
14
+ case .Int16Array:
15
+ return Int16Array(jsTypedArray)
16
+ case .Int32Array:
17
+ return Int32Array(jsTypedArray)
18
+ case .Uint8Array:
19
+ return Uint8Array(jsTypedArray)
20
+ case .Uint8ClampedArray:
21
+ return Uint8ClampedArray(jsTypedArray)
22
+ case .Uint16Array:
23
+ return Uint16Array(jsTypedArray)
24
+ case .Uint32Array:
25
+ return Uint32Array(jsTypedArray)
26
+ case .Float32Array:
27
+ return Float32Array(jsTypedArray)
28
+ case .Float64Array:
29
+ return Float64Array(jsTypedArray)
30
+ case .BigInt64Array:
31
+ return BigInt64Array(jsTypedArray)
32
+ case .BigUint64Array:
33
+ return BigUint64Array(jsTypedArray)
34
+ @unknown default:
35
+ fatalError("Unknown kind of the TypedArray")
36
+ }
37
+ }
38
+
39
+ /**
40
+ A JavaScript object of the underlying typed array.
41
+ */
42
+ let jsTypedArray: JavaScriptTypedArray
43
+
44
+ /**
45
+ The length in bytes from the start of the underlying ArrayBuffer.
46
+ Fixed at construction time and thus read-only.
47
+ */
48
+ public lazy var byteLength: Int = jsTypedArray.getProperty("byteLength").getInt()
49
+
50
+ /**
51
+ The offset in bytes from the start of the underlying ArrayBuffer.
52
+ Fixed at construction time and thus read-only.
53
+ */
54
+ public lazy var byteOffset: Int = jsTypedArray.getProperty("byteOffset").getInt()
55
+
56
+ /**
57
+ Returns the number of elements held in the typed array.
58
+ Fixed at construction time and thus read only.
59
+ */
60
+ public lazy var length: Int = jsTypedArray.getProperty("length").getInt()
61
+
62
+ /**
63
+ The unsafe mutable raw pointer to the start of the array buffer.
64
+ */
65
+ public lazy var rawPointer: UnsafeMutableRawPointer = jsTypedArray.getUnsafeMutableRawPointer()
66
+
67
+ /**
68
+ Returns the kind of the typed array, such as `Int8Array` or `Float32Array`.
69
+ */
70
+ public var kind: TypedArrayKind {
71
+ return jsTypedArray.kind
72
+ }
73
+
74
+ /**
75
+ Initializes the typed array with the given JS typed array.
76
+ */
77
+ required init(_ jsTypedArray: JavaScriptTypedArray) {
78
+ self.jsTypedArray = jsTypedArray
79
+ }
80
+ }
@@ -0,0 +1,28 @@
1
+ // Copyright 2022-present 650 Industries. All rights reserved.
2
+
3
+ /**
4
+ Executes the given Swift closure and if it throws, the `NSError` is set on the given `NSErrorPointer` and the original error is rethrown.
5
+ This is especially useful for ObjC<->Swift interop, specifically when the ObjC needs to catch errors thrown by Swift closure.
6
+ */
7
+ internal func runWithErrorPointer<R>(_ errorPointer: NSErrorPointer, _ closure: () throws -> R) rethrows -> R? {
8
+ do {
9
+ return try closure()
10
+ } catch {
11
+ errorPointer?.pointee = toNSError(error)
12
+ throw error
13
+ }
14
+ }
15
+
16
+ /**
17
+ Converts Swift errors to `NSError` so that they can be handled from the ObjC code.
18
+ */
19
+ internal func toNSError(_ error: Error) -> NSError {
20
+ if let error = error as? Exception {
21
+ return NSError(domain: "dev.expo.modules", code: 0, userInfo: [
22
+ "name": error.name,
23
+ "code": error.code,
24
+ "message": error.debugDescription,
25
+ ])
26
+ }
27
+ return error as NSError
28
+ }
@@ -14,16 +14,16 @@ public final class ConcreteViewProp<ViewType: UIView, PropType: AnyArgument>: An
14
14
  public let name: String
15
15
 
16
16
  /**
17
- An argument type wrapper for the prop's value type.
17
+ A dynamic type for the prop's value type.
18
18
  */
19
- private let propType: AnyArgumentType
19
+ private let propType: AnyDynamicType
20
20
 
21
21
  /**
22
22
  Closure to call to set the actual property on the given view.
23
23
  */
24
24
  private let setter: SetterType
25
25
 
26
- internal init(name: String, propType: AnyArgumentType, setter: @escaping SetterType) {
26
+ internal init(name: String, propType: AnyDynamicType, setter: @escaping SetterType) {
27
27
  self.name = name
28
28
  self.propType = propType
29
29
  self.setter = setter
@@ -29,7 +29,7 @@ public func prop<ViewType: UIView, PropType: AnyArgument>(
29
29
  ) -> ViewManagerDefinitionComponent {
30
30
  return ConcreteViewProp(
31
31
  name: name,
32
- propType: ArgumentType(PropType.self),
32
+ propType: ~PropType.self,
33
33
  setter: setter
34
34
  )
35
35
  }
@@ -43,7 +43,7 @@ public func Prop<ViewType: UIView, PropType: AnyArgument>(
43
43
  ) -> ViewManagerDefinitionComponent {
44
44
  return ConcreteViewProp(
45
45
  name: name,
46
- propType: ArgumentType(PropType.self),
46
+ propType: ~PropType.self,
47
47
  setter: setter
48
48
  )
49
49
  }
@@ -0,0 +1,210 @@
1
+ import ExpoModulesTestCore
2
+
3
+ @testable import ExpoModulesCore
4
+
5
+ class ClassComponentSpec: ExpoSpec {
6
+ override func spec() {
7
+ describe("basic") {
8
+ it("factory returns a component") {
9
+ let klass = Class("") {}
10
+ expect(klass).to(beAnInstanceOf(ClassComponent.self))
11
+ }
12
+
13
+ it("has a name") {
14
+ let expoClassName = "ExpoClass"
15
+ let klass = Class(expoClassName) {}
16
+ expect(klass.name) == expoClassName
17
+ }
18
+
19
+ it("is without native constructor") {
20
+ let klass = Class("") {}
21
+ expect(klass.constructor).to(beNil())
22
+ }
23
+
24
+ it("has native constructor") {
25
+ let klass = Class("") { Constructor {} }
26
+ expect(klass.constructor).notTo(beNil())
27
+ }
28
+
29
+ it("ignores constructor as function") {
30
+ let klass = Class("") { Constructor {} }
31
+ expect(klass.functions["constructor"]).to(beNil())
32
+ }
33
+
34
+ it("builds a class") {
35
+ let runtime = JavaScriptRuntime()
36
+ let klass = Class("") {}
37
+ let object = klass.build(inRuntime: runtime)
38
+
39
+ expect(object.hasProperty("prototype")) == true
40
+ expect(object.getProperty("prototype").kind) == .object
41
+ }
42
+ }
43
+
44
+ describe("module") {
45
+ let appContext = AppContext.create()
46
+ let runtime = appContext.runtime
47
+
48
+ beforeSuite {
49
+ class ClassTestModule: Module {
50
+ func definition() -> ModuleDefinition {
51
+ Name("ClassTest")
52
+
53
+ Class("MyClass") {
54
+ Constructor {}
55
+
56
+ Function("myFunction") {
57
+ return "foobar"
58
+ }
59
+
60
+ Property("foo") {
61
+ return "bar"
62
+ }
63
+ }
64
+ }
65
+ }
66
+ appContext.moduleRegistry.register(moduleType: ClassTestModule.self)
67
+ }
68
+
69
+ it("is a function") {
70
+ let klass = try runtime?.eval("ExpoModules.ClassTest.MyClass")
71
+ expect(klass?.isFunction()) == true
72
+ }
73
+
74
+ it("has a name") {
75
+ let klass = try runtime?.eval("ExpoModules.ClassTest.MyClass.name")
76
+ expect(klass?.getString()) == "MyClass"
77
+ }
78
+
79
+ it("has a prototype") {
80
+ let prototype = try runtime?.eval("ExpoModules.ClassTest.MyClass.prototype")
81
+ expect(prototype?.isObject()) == true
82
+ }
83
+
84
+ it("has keys in prototype") {
85
+ let prototypeKeys = try runtime?.eval("Object.keys(ExpoModules.ClassTest.MyClass.prototype)")
86
+ .getArray()
87
+ .map { $0.getString() } ?? []
88
+
89
+ expect(prototypeKeys).to(contain("myFunction"))
90
+ expect(prototypeKeys).notTo(contain("__native_constructor__"))
91
+ }
92
+
93
+ it("is an instance of") {
94
+ let isInstanceOf = try runtime?.eval([
95
+ "myObject = new ExpoModules.ClassTest.MyClass()",
96
+ "myObject instanceof ExpoModules.ClassTest.MyClass",
97
+ ])
98
+
99
+ expect(isInstanceOf?.getBool()) == true
100
+ }
101
+
102
+ it("defines properties on initialization") {
103
+ // The properties are not specified in the prototype, but defined during initialization.
104
+ let object = try runtime?.eval("new ExpoModules.ClassTest.MyClass()").asObject()
105
+ expect(object?.getPropertyNames()).to(contain("foo"))
106
+ expect(object?.getProperty("foo").getString()) == "bar"
107
+ }
108
+ }
109
+
110
+ describe("class with associated type") {
111
+ let appContext = AppContext.create()
112
+ let runtime = appContext.runtime
113
+
114
+ beforeSuite {
115
+ appContext.moduleRegistry.register(moduleType: ModuleWithCounterClass.self)
116
+ }
117
+ it("is defined") {
118
+ let isDefined = try! runtime!.eval("'Counter' in ExpoModules.TestModule")
119
+
120
+ expect(isDefined.getBool()) == true
121
+ }
122
+ it("creates shared object") {
123
+ let jsObject = try! runtime!.eval("new ExpoModules.TestModule.Counter(0)").getObject()
124
+ let nativeObject = SharedObjectRegistry.toNativeObject(jsObject)
125
+
126
+ expect(nativeObject).notTo(beNil())
127
+ }
128
+ it("registers shared object") {
129
+ let oldSize = SharedObjectRegistry.size
130
+ try! runtime?.eval("object = new ExpoModules.TestModule.Counter(0)")
131
+
132
+ expect(SharedObjectRegistry.size) == oldSize + 1
133
+ }
134
+ it("calls function with owner") {
135
+ try runtime?.eval([
136
+ "object = new ExpoModules.TestModule.Counter(0)",
137
+ "object.increment(1)",
138
+ ])
139
+ // no expectations, just checking if it doesn't fail
140
+ }
141
+ it("creates with initial value") {
142
+ let initialValue = Int.random(in: 1..<100)
143
+ let value = try runtime!.eval([
144
+ "object = new ExpoModules.TestModule.Counter(\(initialValue))",
145
+ "object.getValue()",
146
+ ])
147
+
148
+ expect(value.kind) == .number
149
+ expect(value.getInt()) == initialValue
150
+ }
151
+ it("gets shared object value") {
152
+ let value = try runtime!.eval([
153
+ "object = new ExpoModules.TestModule.Counter(0)",
154
+ "object.getValue()",
155
+ ])
156
+
157
+ expect(value.kind) == .number
158
+ expect(value.isNumber()) == true
159
+ }
160
+ it("changes shared object") {
161
+ try! runtime?.eval("object = new ExpoModules.TestModule.Counter(0)")
162
+ let incrementBy = Int.random(in: 1..<100)
163
+ let value = try runtime!.eval("object.getValue()").asInt()
164
+ let newValue = try runtime!.eval([
165
+ "object.increment(\(incrementBy))",
166
+ "object.getValue()",
167
+ ])
168
+
169
+ expect(newValue.kind) == .number
170
+ expect(newValue.getInt()) == value + incrementBy
171
+ }
172
+ }
173
+ }
174
+ }
175
+
176
+ /**
177
+ A module that exposes a Counter class with an associated shared object class.
178
+ */
179
+ fileprivate final class ModuleWithCounterClass: Module {
180
+ func definition() -> ModuleDefinition {
181
+ Name("TestModule")
182
+
183
+ Class(Counter.self) {
184
+ Constructor { (initialValue: Int) in
185
+ return Counter(initialValue: initialValue)
186
+ }
187
+ Function("increment") { (counter, value: Int) in
188
+ counter.increment(by: value)
189
+ }
190
+ Function("getValue") { counter in
191
+ return counter.currentValue
192
+ }
193
+ }
194
+ }
195
+ }
196
+
197
+ /**
198
+ A shared object class that stores some native value and can be used as an associated type of the JS class.
199
+ */
200
+ fileprivate final class Counter: SharedObject {
201
+ var currentValue = 0
202
+
203
+ init(initialValue: Int = 0) {
204
+ self.currentValue = initialValue
205
+ }
206
+
207
+ func increment(by value: Int = 1) {
208
+ currentValue += value
209
+ }
210
+ }