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(Conversions.convertFunctionResult(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 let result = try body(argumentsTuple) 74 return Conversions.convertFunctionResult(result) 75 } catch let error as Exception { 76 throw FunctionCallException(name).causedBy(error) 77 } catch { 78 throw UnexpectedException(error) 79 } 80 } 81 82 // MARK: - JavaScriptObjectBuilder 83 84 func build(inRuntime runtime: JavaScriptRuntime) -> JavaScriptObject { 85 // We intentionally capture a strong reference to `self`, otherwise the "detached" objects would 86 // immediately lose the reference to the definition and thus the underlying native function. 87 // It may potentially cause memory leaks, but at the time of writing this comment, 88 // the native definition instance deallocates correctly when the JS VM triggers the garbage collector. 89 return runtime.createSyncFunction(name, argsCount: argumentsCount) { [weak runtime, self] this, args in 90 guard let runtime = runtime else { 91 throw Exceptions.RuntimeLost() 92 } 93 let result = try self.call(by: this, withArguments: args) 94 return Conversions.convertFunctionResult(result, runtime: runtime) 95 } 96 } 97 } 98 99 /** 100 Synchronous function without arguments. 101 */ 102 public func Function<R>( 103 _ name: String, 104 @_implicitSelfCapture _ closure: @escaping () throws -> R 105 ) -> SyncFunctionComponent<(), Void, R> { 106 return SyncFunctionComponent( 107 name, 108 firstArgType: Void.self, 109 dynamicArgumentTypes: [], 110 closure 111 ) 112 } 113 114 /** 115 Synchronous function with one argument. 116 */ 117 public func Function<R, A0: AnyArgument>( 118 _ name: String, 119 @_implicitSelfCapture _ closure: @escaping (A0) throws -> R 120 ) -> SyncFunctionComponent<(A0), A0, R> { 121 return SyncFunctionComponent( 122 name, 123 firstArgType: A0.self, 124 dynamicArgumentTypes: [~A0.self], 125 closure 126 ) 127 } 128 129 /** 130 Synchronous function with two arguments. 131 */ 132 public func Function<R, A0: AnyArgument, A1: AnyArgument>( 133 _ name: String, 134 @_implicitSelfCapture _ closure: @escaping (A0, A1) throws -> R 135 ) -> SyncFunctionComponent<(A0, A1), A0, R> { 136 return SyncFunctionComponent( 137 name, 138 firstArgType: A0.self, 139 dynamicArgumentTypes: [~A0.self, ~A1.self], 140 closure 141 ) 142 } 143 144 /** 145 Synchronous function with three arguments. 146 */ 147 public func Function<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument>( 148 _ name: String, 149 @_implicitSelfCapture _ closure: @escaping (A0, A1, A2) throws -> R 150 ) -> SyncFunctionComponent<(A0, A1, A2), A0, R> { 151 return SyncFunctionComponent( 152 name, 153 firstArgType: A0.self, 154 dynamicArgumentTypes: [ 155 ~A0.self, 156 ~A1.self, 157 ~A2.self 158 ], 159 closure 160 ) 161 } 162 163 /** 164 Synchronous function with four arguments. 165 */ 166 public func Function<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument, A3: AnyArgument>( 167 _ name: String, 168 @_implicitSelfCapture _ closure: @escaping (A0, A1, A2, A3) throws -> R 169 ) -> SyncFunctionComponent<(A0, A1, A2, A3), A0, R> { 170 return SyncFunctionComponent( 171 name, 172 firstArgType: A0.self, 173 dynamicArgumentTypes: [ 174 ~A0.self, 175 ~A1.self, 176 ~A2.self, 177 ~A3.self 178 ], 179 closure 180 ) 181 } 182 183 /** 184 Synchronous function with five arguments. 185 */ 186 public func Function<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument, A3: AnyArgument, A4: AnyArgument>( 187 _ name: String, 188 @_implicitSelfCapture _ closure: @escaping (A0, A1, A2, A3, A4) throws -> R 189 ) -> SyncFunctionComponent<(A0, A1, A2, A3, A4), A0, R> { 190 return SyncFunctionComponent( 191 name, 192 firstArgType: A0.self, 193 dynamicArgumentTypes: [ 194 ~A0.self, 195 ~A1.self, 196 ~A2.self, 197 ~A3.self, 198 ~A4.self 199 ], 200 closure 201 ) 202 } 203 204 /** 205 Synchronous function with six arguments. 206 */ 207 public func Function<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument, A3: AnyArgument, A4: AnyArgument, A5: AnyArgument>( 208 _ name: String, 209 @_implicitSelfCapture _ closure: @escaping (A0, A1, A2, A3, A4, A5) throws -> R 210 ) -> SyncFunctionComponent<(A0, A1, A2, A3, A4, A5), A0, R> { 211 return SyncFunctionComponent( 212 name, 213 firstArgType: A0.self, 214 dynamicArgumentTypes: [ 215 ~A0.self, 216 ~A1.self, 217 ~A2.self, 218 ~A3.self, 219 ~A4.self, 220 ~A5.self 221 ], 222 closure 223 ) 224 } 225 226 /** 227 Synchronous function with seven arguments. 228 */ 229 public func Function<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument, A3: AnyArgument, A4: AnyArgument, A5: AnyArgument, A6: AnyArgument>( 230 _ name: String, 231 @_implicitSelfCapture _ closure: @escaping (A0, A1, A2, A3, A4, A5, A6) throws -> R 232 ) -> SyncFunctionComponent<(A0, A1, A2, A3, A4, A5, A6), A0, R> { 233 return SyncFunctionComponent( 234 name, 235 firstArgType: A0.self, 236 dynamicArgumentTypes: [ 237 ~A0.self, 238 ~A1.self, 239 ~A2.self, 240 ~A3.self, 241 ~A4.self, 242 ~A5.self, 243 ~A6.self 244 ], 245 closure 246 ) 247 } 248 249 /** 250 Synchronous function with eight arguments. 251 */ 252 public func Function<R, A0: AnyArgument, A1: AnyArgument, A2: AnyArgument, A3: AnyArgument, A4: AnyArgument, A5: AnyArgument, A6: AnyArgument, A7: AnyArgument>( 253 _ name: String, 254 @_implicitSelfCapture _ closure: @escaping (A0, A1, A2, A3, A4, A5, A6, A7) throws -> R 255 ) -> SyncFunctionComponent<(A0, A1, A2, A3, A4, A5, A6, A7), A0, R> { 256 return SyncFunctionComponent( 257 name, 258 firstArgType: A0.self, 259 dynamicArgumentTypes: [ 260 ~A0.self, 261 ~A1.self, 262 ~A2.self, 263 ~A3.self, 264 ~A4.self, 265 ~A5.self, 266 ~A6.self, 267 ~A7.self 268 ], 269 closure 270 ) 271 } 272