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