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 20 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 Creates a synchronous host function that runs the given closure when it's called. 35 The value returned by the closure is synchronously returned to JS. 36 - Returns: A JavaScript function represented as a `JavaScriptObject`. 37 - Note: It refines the ObjC implementation from `EXJavaScriptRuntime` to properly catch Swift errors and rethrow them as ObjC `NSError`. 38 */ 39 func createSyncFunction(_ name: String, argsCount: Int = 0, closure: @escaping SyncFunctionClosure) -> JavaScriptObject { 40 return __createSyncFunction(name, argsCount: argsCount) { this, args, errorPointer in 41 do { 42 return try runWithErrorPointer(errorPointer) { 43 return try closure(this, args) 44 } 45 } catch { 46 // Nicely log all errors to the console. 47 log.error(error) 48 49 // Can return anything as the error will be caught through the error pointer already. 50 return nil 51 } 52 } 53 } 54 } 55 56 internal final class JavaScriptEvalException: GenericException<NSError> { 57 override var reason: String { 58 return param.userInfo["message"] as? String ?? "unknown reason" 59 } 60 } 61