1 // Copyright 2022-present 650 Industries. All rights reserved. 2 3 /** 4 Type-erased protocol for synchronous functions. 5 */ 6 internal protocol AnySyncFunctionComponent: AnyFunction { 7 /** 8 Calls the function synchronously with given arguments. 9 - Parameters: 10 - owner: An object that calls this function. If the `takesOwner` property is true 11 and type of the first argument matches the owner type, it's being passed as the argument. 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 - Returns: A value returned by the called function when succeeded or an error when it failed. 14 */ 15 func call(by owner: AnyObject?, withArguments args: [Any]) throws -> Any 16 } 17 18 /** 19 Represents a function that can only be called synchronously. 20 */ 21 public final class SyncFunctionComponent<Args, FirstArgType, ReturnType>: AnySyncFunctionComponent { 22 typealias ClosureType = (Args) throws -> ReturnType 23 24 /** 25 The underlying closure to run when the function is called. 26 */ 27 let body: ClosureType 28 29 init( 30 _ name: String, 31 firstArgType: FirstArgType.Type, 32 dynamicArgumentTypes: [AnyDynamicType], 33 _ body: @escaping ClosureType 34 ) { 35 self.name = name 36 self.dynamicArgumentTypes = dynamicArgumentTypes 37 self.body = body 38 } 39 40 // MARK: - AnyFunction 41 42 let name: String 43 44 let dynamicArgumentTypes: [AnyDynamicType] 45 46 var argumentsCount: Int { 47 return dynamicArgumentTypes.count - (takesOwner ? 1 : 0) 48 } 49 50 var takesOwner: Bool = false 51 52 func call(by owner: AnyObject?, withArguments args: [Any], callback: @escaping (FunctionCallResult) -> ()) { 53 do { 54 let result = try call(by: owner, withArguments: args) 55 callback(.success(result)) 56 } catch let error as Exception { 57 callback(.failure(error)) 58 } catch { 59 callback(.failure(UnexpectedException(error))) 60 } 61 } 62 63 // MARK: - AnySyncFunctionComponent 64 65 func call(by owner: AnyObject?, withArguments args: [Any]) throws -> Any { 66 do { 67 let arguments = concat( 68 arguments: try cast(arguments: args, forFunction: self), 69 withOwner: owner, 70 forFunction: self 71 ) 72 let argumentsTuple = try Conversions.toTuple(arguments) as! Args 73 return try body(argumentsTuple) 74 } catch let error as Exception { 75 throw FunctionCallException(name).causedBy(error) 76 } catch { 77 throw UnexpectedException(error) 78 } 79 } 80 81 // MARK: - JavaScriptObjectBuilder 82 83 func build(inRuntime runtime: JavaScriptRuntime) -> JavaScriptObject { 84 return runtime.createSyncFunction(name, argsCount: argumentsCount) { [weak self, name] this, args in 85 guard let self = self else { 86 throw NativeFunctionUnavailableException(name) 87 } 88 return try self.call(by: this, withArguments: args) 89 } 90 } 91 } 92 93 /** 94 Synchronous function without arguments. 95 */ 96 public func Function<R>( 97 _ name: String, 98 _ closure: @escaping () throws -> R 99 ) -> SyncFunctionComponent<(), Void, R> { 100 return SyncFunctionComponent( 101 name, 102 firstArgType: Void.self, 103 dynamicArgumentTypes: [], 104 closure 105 ) 106 } 107 108 /** 109 Synchronous function with one argument. 110 */ 111 public func Function<R, A0: AnyArgument>( 112 _ name: String, 113 _ closure: @escaping (A0) throws -> R 114 ) -> SyncFunctionComponent<(A0), A0, R> { 115 return SyncFunctionComponent( 116 name, 117 firstArgType: A0.self, 118 dynamicArgumentTypes: [~A0.self], 119 closure 120 ) 121 } 122 123 /** 124 Synchronous function with two arguments. 125 */ 126 public func Function<R, A0: AnyArgument, A1: AnyArgument>( 127 _ name: String, 128 _ closure: @escaping (A0, A1) throws -> R 129 ) -> SyncFunctionComponent<(A0, A1), A0, R> { 130 return SyncFunctionComponent( 131 name, 132 firstArgType: A0.self, 133 dynamicArgumentTypes: [~A0.self, ~A1.self], 134 closure 135 ) 136 } 137 138 /** 139 Synchronous function with three arguments. 140 */ 141 public func Function<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument>( 142 _ name: String, 143 _ closure: @escaping (A0, A1, A2) throws -> R 144 ) -> SyncFunctionComponent<(A0, A1, A2), A0, R> { 145 return SyncFunctionComponent( 146 name, 147 firstArgType: A0.self, 148 dynamicArgumentTypes: [ 149 ~A0.self, 150 ~A1.self, 151 ~A2.self 152 ], 153 closure 154 ) 155 } 156 157 /** 158 Synchronous function with four arguments. 159 */ 160 public func Function<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument, A3: AnyArgument>( 161 _ name: String, 162 _ closure: @escaping (A0, A1, A2, A3) throws -> R 163 ) -> SyncFunctionComponent<(A0, A1, A2, A3), A0, R> { 164 return SyncFunctionComponent( 165 name, 166 firstArgType: A0.self, 167 dynamicArgumentTypes: [ 168 ~A0.self, 169 ~A1.self, 170 ~A2.self, 171 ~A3.self 172 ], 173 closure 174 ) 175 } 176 177 /** 178 Synchronous function with five arguments. 179 */ 180 public func Function<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument, A3: AnyArgument, A4: AnyArgument>( 181 _ name: String, 182 _ closure: @escaping (A0, A1, A2, A3, A4) throws -> R 183 ) -> SyncFunctionComponent<(A0, A1, A2, A3, A4), A0, R> { 184 return SyncFunctionComponent( 185 name, 186 firstArgType: A0.self, 187 dynamicArgumentTypes: [ 188 ~A0.self, 189 ~A1.self, 190 ~A2.self, 191 ~A3.self, 192 ~A4.self 193 ], 194 closure 195 ) 196 } 197 198 /** 199 Synchronous function with six arguments. 200 */ 201 public func Function<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument, A3: AnyArgument, A4: AnyArgument, A5: AnyArgument>( 202 _ name: String, 203 _ closure: @escaping (A0, A1, A2, A3, A4, A5) throws -> R 204 ) -> SyncFunctionComponent<(A0, A1, A2, A3, A4, A5), A0, R> { 205 return SyncFunctionComponent( 206 name, 207 firstArgType: A0.self, 208 dynamicArgumentTypes: [ 209 ~A0.self, 210 ~A1.self, 211 ~A2.self, 212 ~A3.self, 213 ~A4.self, 214 ~A5.self 215 ], 216 closure 217 ) 218 } 219 220 /** 221 Synchronous function with seven arguments. 222 */ 223 public func Function<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument, A3: AnyArgument, A4: AnyArgument, A5: AnyArgument, A6: AnyArgument>( 224 _ name: String, 225 _ closure: @escaping (A0, A1, A2, A3, A4, A5, A6) throws -> R 226 ) -> SyncFunctionComponent<(A0, A1, A2, A3, A4, A5, A6), A0, R> { 227 return SyncFunctionComponent( 228 name, 229 firstArgType: A0.self, 230 dynamicArgumentTypes: [ 231 ~A0.self, 232 ~A1.self, 233 ~A2.self, 234 ~A3.self, 235 ~A4.self, 236 ~A5.self, 237 ~A6.self 238 ], 239 closure 240 ) 241 } 242 243 /** 244 Synchronous function with eight arguments. 245 */ 246 public func Function<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument, A3: AnyArgument, A4: AnyArgument, A5: AnyArgument, A6: AnyArgument, A7: AnyArgument>( 247 _ name: String, 248 _ closure: @escaping (A0, A1, A2, A3, A4, A5, A6, A7) throws -> R 249 ) -> SyncFunctionComponent<(A0, A1, A2, A3, A4, A5, A6, A7), A0, R> { 250 return SyncFunctionComponent( 251 name, 252 firstArgType: A0.self, 253 dynamicArgumentTypes: [ 254 ~A0.self, 255 ~A1.self, 256 ~A2.self, 257 ~A3.self, 258 ~A4.self, 259 ~A5.self, 260 ~A6.self, 261 ~A7.self 262 ], 263 closure 264 ) 265 } 266