1 // Copyright 2022-present 650 Industries. All rights reserved. 2 3 import Dispatch 4 5 /** 6 Type-erased protocol for asynchronous functions. 7 */ 8 internal protocol AnyAsyncFunctionComponent: AnyFunction { 9 /** 10 Specifies on which queue the function should run. 11 */ 12 func runOnQueue(_ queue: DispatchQueue?) -> Self 13 } 14 15 /** 16 The default queue used for module's async function calls. 17 */ 18 private let defaultQueue = DispatchQueue(label: "expo.modules.AsyncFunctionQueue", qos: .userInitiated) 19 20 /** 21 Represents a function that can only be called asynchronously, thus its JavaScript equivalent returns a Promise. 22 */ 23 public final class AsyncFunctionComponent<Args, FirstArgType, ReturnType>: AnyAsyncFunctionComponent { 24 typealias ClosureType = (Args) throws -> ReturnType 25 26 /** 27 The underlying closure to run when the function is called. 28 */ 29 let body: ClosureType 30 31 /** 32 Bool value indicating whether the function takes promise as the last argument. 33 */ 34 let takesPromise: Bool 35 36 /** 37 Dispatch queue on which each function's call is run. 38 */ 39 var queue: DispatchQueue? 40 41 init( 42 _ name: String, 43 firstArgType: FirstArgType.Type, 44 dynamicArgumentTypes: [AnyDynamicType], 45 _ body: @escaping ClosureType 46 ) { 47 self.name = name 48 self.takesPromise = dynamicArgumentTypes.last?.wraps(Promise.self) ?? false 49 self.body = body 50 51 // Drop the last argument type if it's the `Promise`. 52 self.dynamicArgumentTypes = takesPromise ? dynamicArgumentTypes.dropLast(1) : dynamicArgumentTypes 53 } 54 55 // MARK: - AnyFunction 56 57 let name: String 58 59 let dynamicArgumentTypes: [AnyDynamicType] 60 61 var argumentsCount: Int { 62 return dynamicArgumentTypes.count - (takesOwner ? 1 : 0) 63 } 64 65 var takesOwner: Bool = false 66 67 func call(by owner: AnyObject?, withArguments args: [Any], callback: @escaping (FunctionCallResult) -> ()) { 68 let promise = Promise { value in 69 callback(.success(Conversions.convertFunctionResult(value))) 70 } rejecter: { exception in 71 callback(.failure(exception)) 72 } 73 var arguments: [Any] = [] 74 75 do { 76 arguments = concat( 77 arguments: try cast(arguments: args, forFunction: self), 78 withOwner: owner, 79 forFunction: self 80 ) 81 } catch let error as Exception { 82 callback(.failure(error)) 83 return 84 } catch { 85 callback(.failure(UnexpectedException(error))) 86 return 87 } 88 89 // Add promise to the array of arguments if necessary. 90 if takesPromise { 91 arguments.append(promise) 92 } 93 94 let queue = queue ?? defaultQueue 95 96 queue.async { [body, name] in 97 let returnedValue: ReturnType? 98 99 do { 100 let argumentsTuple = try Conversions.toTuple(arguments) as! Args 101 returnedValue = try body(argumentsTuple) 102 } catch let error as Exception { 103 promise.reject(FunctionCallException(name).causedBy(error)) 104 return 105 } catch { 106 promise.reject(UnexpectedException(error)) 107 return 108 } 109 if !self.takesPromise { 110 promise.resolve(returnedValue) 111 } 112 } 113 } 114 115 // MARK: - JavaScriptObjectBuilder 116 117 func build(inRuntime runtime: JavaScriptRuntime) -> JavaScriptObject { 118 return runtime.createAsyncFunction(name, argsCount: argumentsCount) { [weak self, name] this, args, resolve, reject in 119 guard let self = self else { 120 let exception = NativeFunctionUnavailableException(name) 121 return reject(exception.code, exception.description, nil) 122 } 123 self.call(by: this, withArguments: args) { result in 124 switch result { 125 case .failure(let error): 126 reject(error.code, error.description, nil) 127 case .success(let value): 128 resolve(value) 129 } 130 } 131 } 132 } 133 134 // MARK: - AnyAsyncFunctionComponent 135 136 public func runOnQueue(_ queue: DispatchQueue?) -> Self { 137 self.queue = queue 138 return self 139 } 140 } 141 142 // MARK: - Exceptions 143 144 internal final class NativeFunctionUnavailableException: GenericException<String> { 145 override var reason: String { 146 return "Native function '\(param)' is no longer available in memory" 147 } 148 } 149 150 // MARK: - Factories 151 152 /** 153 Asynchronous function without arguments. 154 */ 155 public func AsyncFunction<R>( 156 _ name: String, 157 @_implicitSelfCapture _ closure: @escaping () throws -> R 158 ) -> AsyncFunctionComponent<(), Void, R> { 159 return AsyncFunctionComponent( 160 name, 161 firstArgType: Void.self, 162 dynamicArgumentTypes: [], 163 closure 164 ) 165 } 166 167 /** 168 Asynchronous function with one argument. 169 */ 170 public func AsyncFunction<R, A0: AnyArgument>( 171 _ name: String, 172 @_implicitSelfCapture _ closure: @escaping (A0) throws -> R 173 ) -> AsyncFunctionComponent<(A0), A0, R> { 174 return AsyncFunctionComponent( 175 name, 176 firstArgType: A0.self, 177 dynamicArgumentTypes: [~A0.self], 178 closure 179 ) 180 } 181 182 /** 183 Asynchronous function with two arguments. 184 */ 185 public func AsyncFunction<R, A0: AnyArgument, A1: AnyArgument>( 186 _ name: String, 187 @_implicitSelfCapture _ closure: @escaping (A0, A1) throws -> R 188 ) -> AsyncFunctionComponent<(A0, A1), A0, R> { 189 return AsyncFunctionComponent( 190 name, 191 firstArgType: A0.self, 192 dynamicArgumentTypes: [~A0.self, ~A1.self], 193 closure 194 ) 195 } 196 197 /** 198 Asynchronous function with three arguments. 199 */ 200 public func AsyncFunction<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument>( 201 _ name: String, 202 @_implicitSelfCapture _ closure: @escaping (A0, A1, A2) throws -> R 203 ) -> AsyncFunctionComponent<(A0, A1, A2), A0, R> { 204 return AsyncFunctionComponent( 205 name, 206 firstArgType: A0.self, 207 dynamicArgumentTypes: [ 208 ~A0.self, 209 ~A1.self, 210 ~A2.self 211 ], 212 closure 213 ) 214 } 215 216 /** 217 Asynchronous function with four arguments. 218 */ 219 public func AsyncFunction<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument, A3: AnyArgument>( 220 _ name: String, 221 @_implicitSelfCapture _ closure: @escaping (A0, A1, A2, A3) throws -> R 222 ) -> AsyncFunctionComponent<(A0, A1, A2, A3), A0, R> { 223 return AsyncFunctionComponent( 224 name, 225 firstArgType: A0.self, 226 dynamicArgumentTypes: [ 227 ~A0.self, 228 ~A1.self, 229 ~A2.self, 230 ~A3.self 231 ], 232 closure 233 ) 234 } 235 236 /** 237 Asynchronous function with five arguments. 238 */ 239 public func AsyncFunction<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument, A3: AnyArgument, A4: AnyArgument>( 240 _ name: String, 241 @_implicitSelfCapture _ closure: @escaping (A0, A1, A2, A3, A4) throws -> R 242 ) -> AsyncFunctionComponent<(A0, A1, A2, A3, A4), A0, R> { 243 return AsyncFunctionComponent( 244 name, 245 firstArgType: A0.self, 246 dynamicArgumentTypes: [ 247 ~A0.self, 248 ~A1.self, 249 ~A2.self, 250 ~A3.self, 251 ~A4.self 252 ], 253 closure 254 ) 255 } 256 257 /** 258 Asynchronous function with six arguments. 259 */ 260 public func AsyncFunction<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument, A3: AnyArgument, A4: AnyArgument, A5: AnyArgument>( 261 _ name: String, 262 @_implicitSelfCapture _ closure: @escaping (A0, A1, A2, A3, A4, A5) throws -> R 263 ) -> AsyncFunctionComponent<(A0, A1, A2, A3, A4, A5), A0, R> { 264 return AsyncFunctionComponent( 265 name, 266 firstArgType: A0.self, 267 dynamicArgumentTypes: [ 268 ~A0.self, 269 ~A1.self, 270 ~A2.self, 271 ~A3.self, 272 ~A4.self, 273 ~A5.self 274 ], 275 closure 276 ) 277 } 278 279 /** 280 Asynchronous function with seven arguments. 281 */ 282 public func AsyncFunction<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument, A3: AnyArgument, A4: AnyArgument, A5: AnyArgument, A6: AnyArgument>( 283 _ name: String, 284 @_implicitSelfCapture _ closure: @escaping (A0, A1, A2, A3, A4, A5, A6) throws -> R 285 ) -> AsyncFunctionComponent<(A0, A1, A2, A3, A4, A5, A6), A0, R> { 286 return AsyncFunctionComponent( 287 name, 288 firstArgType: A0.self, 289 dynamicArgumentTypes: [ 290 ~A0.self, 291 ~A1.self, 292 ~A2.self, 293 ~A3.self, 294 ~A4.self, 295 ~A5.self, 296 ~A6.self 297 ], 298 closure 299 ) 300 } 301 302 /** 303 Asynchronous function with eight arguments. 304 */ 305 public func AsyncFunction<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument, A3: AnyArgument, A4: AnyArgument, A5: AnyArgument, A6: AnyArgument, A7: AnyArgument>( 306 _ name: String, 307 @_implicitSelfCapture _ closure: @escaping (A0, A1, A2, A3, A4, A5, A6, A7) throws -> R 308 ) -> AsyncFunctionComponent<(A0, A1, A2, A3, A4, A5, A6, A7), A0, R> { 309 return AsyncFunctionComponent( 310 name, 311 firstArgType: A0.self, 312 dynamicArgumentTypes: [ 313 ~A0.self, 314 ~A1.self, 315 ~A2.self, 316 ~A3.self, 317 ~A4.self, 318 ~A5.self, 319 ~A6.self, 320 ~A7.self 321 ], 322 closure 323 ) 324 } 325