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