1*a2ddfabcSTomasz Sapeta // Copyright 2023-present 650 Industries. All rights reserved. 2*a2ddfabcSTomasz Sapeta 3*a2ddfabcSTomasz Sapeta /** 4*a2ddfabcSTomasz Sapeta Represents a JavaScript function that can be called by the native code and that must return the given generic `ReturnType`. 5*a2ddfabcSTomasz Sapeta */ 6*a2ddfabcSTomasz Sapeta public final class JavaScriptFunction<ReturnType>: AnyArgument, AnyJavaScriptValue { 7*a2ddfabcSTomasz Sapeta /** 8*a2ddfabcSTomasz Sapeta Raw representation of the JavaScript function that doesn't impose any restrictions on the returned type. 9*a2ddfabcSTomasz Sapeta */ 10*a2ddfabcSTomasz Sapeta private let rawFunction: RawJavaScriptFunction 11*a2ddfabcSTomasz Sapeta 12*a2ddfabcSTomasz Sapeta /** 13*a2ddfabcSTomasz Sapeta Weak reference to the app context that is necessary to convert some arguments associated with the context (e.g. shared objects). 14*a2ddfabcSTomasz Sapeta */ 15*a2ddfabcSTomasz Sapeta private weak var appContext: AppContext? 16*a2ddfabcSTomasz Sapeta 17*a2ddfabcSTomasz Sapeta init(rawFunction: RawJavaScriptFunction, appContext: AppContext) { 18*a2ddfabcSTomasz Sapeta self.rawFunction = rawFunction 19*a2ddfabcSTomasz Sapeta self.appContext = appContext 20*a2ddfabcSTomasz Sapeta } 21*a2ddfabcSTomasz Sapeta 22*a2ddfabcSTomasz Sapeta // MARK: - Calling 23*a2ddfabcSTomasz Sapeta 24*a2ddfabcSTomasz Sapeta /** 25*a2ddfabcSTomasz Sapeta Calls the function with the given `this` object and arguments. 26*a2ddfabcSTomasz Sapeta */ callnull27*a2ddfabcSTomasz Sapeta public func call(_ arguments: Any..., usingThis this: JavaScriptObject? = nil) throws -> ReturnType { 28*a2ddfabcSTomasz Sapeta return try call(withArguments: arguments, asConstructor: false, usingThis: this) 29*a2ddfabcSTomasz Sapeta } 30*a2ddfabcSTomasz Sapeta 31*a2ddfabcSTomasz Sapeta /** 32*a2ddfabcSTomasz Sapeta Calls the function as a constructor with the given arguments. It's like calling a function with the `new` keyword. 33*a2ddfabcSTomasz Sapeta */ callAsConstructornull34*a2ddfabcSTomasz Sapeta public func callAsConstructor(_ arguments: Any...) throws -> ReturnType { 35*a2ddfabcSTomasz Sapeta return try call(withArguments: arguments, asConstructor: true, usingThis: nil) 36*a2ddfabcSTomasz Sapeta } 37*a2ddfabcSTomasz Sapeta 38*a2ddfabcSTomasz Sapeta /** 39*a2ddfabcSTomasz Sapeta Universal function that calls the function with given arguments, this object and whether to call it as a constructor. 40*a2ddfabcSTomasz Sapeta */ callnull41*a2ddfabcSTomasz Sapeta private func call(withArguments arguments: [Any] = [], asConstructor: Bool = false, usingThis this: JavaScriptObject? = nil) throws -> ReturnType { 42*a2ddfabcSTomasz Sapeta guard let appContext else { 43*a2ddfabcSTomasz Sapeta throw AppContextLostException() 44*a2ddfabcSTomasz Sapeta } 45*a2ddfabcSTomasz Sapeta let value = rawFunction.call(withArguments: arguments, thisObject: this, asConstructor: false) 46*a2ddfabcSTomasz Sapeta let dynamicType = ~ReturnType.self 47*a2ddfabcSTomasz Sapeta 48*a2ddfabcSTomasz Sapeta guard let result = try dynamicType.cast(jsValue: value, appContext: appContext) as? ReturnType else { 49*a2ddfabcSTomasz Sapeta throw UnexpectedReturnType(dynamicType.description) 50*a2ddfabcSTomasz Sapeta } 51*a2ddfabcSTomasz Sapeta return result 52*a2ddfabcSTomasz Sapeta } 53*a2ddfabcSTomasz Sapeta 54*a2ddfabcSTomasz Sapeta // MARK: - AnyJavaScriptValue 55*a2ddfabcSTomasz Sapeta convertnull56*a2ddfabcSTomasz Sapeta internal static func convert(from value: JavaScriptValue, appContext: AppContext) throws -> Self { 57*a2ddfabcSTomasz Sapeta guard value.kind == .function else { 58*a2ddfabcSTomasz Sapeta throw Conversions.ConvertingException<JavaScriptFunction<ReturnType>>(value) 59*a2ddfabcSTomasz Sapeta } 60*a2ddfabcSTomasz Sapeta return Self(rawFunction: value.getFunction(), appContext: appContext) 61*a2ddfabcSTomasz Sapeta } 62*a2ddfabcSTomasz Sapeta } 63*a2ddfabcSTomasz Sapeta 64*a2ddfabcSTomasz Sapeta private final class UnexpectedReturnType: GenericException<String> { 65*a2ddfabcSTomasz Sapeta override var reason: String { 66*a2ddfabcSTomasz Sapeta return "The function returned a value that cannot be converted to \(param)" 67*a2ddfabcSTomasz Sapeta } 68*a2ddfabcSTomasz Sapeta } 69