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