1 // Copyright 2022-present 650 Industries. All rights reserved. 2 3 public extension JavaScriptRuntime { 4 /** 5 A type of the closure that you pass to the `createSyncFunction` function. 6 */ 7 typealias SyncFunctionClosure = (_ this: JavaScriptValue, _ arguments: [JavaScriptValue]) throws -> Any 8 9 /** 10 Evaluates JavaScript code represented as a string. 11 12 - Parameter source: A string representing a JavaScript expression, statement, or sequence of statements. 13 The expression can include variables and properties of existing objects. 14 - Returns: The completion value of evaluating the given code represented as `JavaScriptValue`. 15 If the completion value is empty, `undefined` is returned. 16 - Throws: `JavaScriptEvalException` when evaluated code has invalid syntax or throws an error. 17 - Note: It wraps the original `evaluateScript` to better handle and rethrow exceptions. 18 */ 19 @discardableResult evalnull20 func eval(_ source: String) throws -> JavaScriptValue { 21 do { 22 var result: JavaScriptValue? 23 try EXUtilities.catchException { 24 result = self.__evaluateScript(source) 25 } 26 // There is no risk to force unwrapping as long as the `evaluateScript` returns nonnull value. 27 return result! 28 } catch { 29 throw JavaScriptEvalException(error as NSError) 30 } 31 } 32 33 /** 34 Evaluates the JavaScript code made by joining an array of strings with a newline separator. 35 See the other ``eval(_:)`` for more details. 36 */ 37 @discardableResult evalnull38 func eval(_ source: [String]) throws -> JavaScriptValue { 39 try eval(source.joined(separator: "\n")) 40 } 41 42 /** 43 Creates a synchronous host function that runs the given closure when it's called. 44 The value returned by the closure is synchronously returned to JS. 45 - Returns: A JavaScript function represented as a `JavaScriptObject`. 46 - Note: It refines the ObjC implementation from `EXJavaScriptRuntime` to properly catch Swift errors and rethrow them as ObjC `NSError`. 47 */ createSyncFunctionnull48 func createSyncFunction(_ name: String, argsCount: Int = 0, closure: @escaping SyncFunctionClosure) -> JavaScriptObject { 49 return __createSyncFunction(name, argsCount: argsCount) { this, args, errorPointer in 50 do { 51 return try runWithErrorPointer(errorPointer) { 52 return try closure(this, args) 53 } 54 } catch { 55 // Nicely log all errors to the console. 56 log.error(error) 57 58 // Can return anything as the error will be caught through the error pointer already. 59 return nil 60 } 61 } 62 } 63 } 64 65 internal final class JavaScriptEvalException: GenericException<NSError> { 66 override var reason: String { 67 return param.userInfo["message"] as? String ?? "unknown reason" 68 } 69 } 70