1 // Copyright 2022-present 650 Industries. All rights reserved. 2 3 // MARK: - Arguments 4 5 /** 6 Tries to cast a given value to the type that is wrapped by the dynamic type. 7 - Parameters: 8 - value: A value to be cast. If it's a ``JavaScriptValue``, it's first unpacked to the raw value. 9 - type: Something that implements ``AnyDynamicType`` and knows how to cast the argument. 10 - Returns: A new value converted according to the dynamic type. 11 - Throws: Rethrows various exceptions that could be thrown by the dynamic types. 12 */ 13 internal func cast(_ value: Any, toType type: AnyDynamicType) throws -> Any { 14 // TODO: Accept JavaScriptValue and JavaScriptObject as argument types. 15 if !(type is DynamicTypedArrayType), let value = value as? JavaScriptValue { 16 return try type.cast(value.getRaw()) 17 } 18 return try type.cast(value) 19 } 20 21 /** 22 Tries to cast the given arguments to the types expected by the function. 23 - Parameters: 24 - arguments: An array of arguments to be cast. 25 - function: A function for which to cast the arguments. 26 - Returns: An array of arguments after casting. Its size is the same as the input arrays. 27 - Throws: `InvalidArgsNumberException` when the number of arguments is not equal to the actual number 28 of function's arguments (without an owner and promise). Rethrows exceptions thrown by `cast(_:toType:)`. 29 */ 30 internal func cast(arguments: [Any], forFunction function: AnyFunction) throws -> [Any] { 31 if arguments.count != function.argumentsCount { 32 throw InvalidArgsNumberException((received: arguments.count, expected: function.argumentsCount)) 33 } 34 let argumentTypeOffset = function.takesOwner ? 1 : 0 35 return try arguments.enumerated().map { index, argument in 36 let argumentType = function.dynamicArgumentTypes[index + argumentTypeOffset] 37 38 do { 39 return try cast(argument, toType: argumentType) 40 } catch { 41 throw ArgumentCastException((index: index, type: argumentType)).causedBy(error) 42 } 43 } 44 } 45 46 /** 47 Prepends the owner to the array of arguments if the given function can take it. 48 */ 49 internal func concat(arguments: [Any], withOwner owner: AnyObject?, forFunction function: AnyFunction) -> [Any] { 50 if function.takesOwner, let owner = try? function.dynamicArgumentTypes.first?.cast(owner) { 51 return [owner] + arguments 52 } 53 return arguments 54 } 55 56 // MARK: - Exceptions 57 58 internal class InvalidArgsNumberException: GenericException<(received: Int, expected: Int)> { 59 override var reason: String { 60 "Received \(param.received) arguments, but \(param.expected) was expected" 61 } 62 } 63 64 internal class ArgumentCastException: GenericException<(index: Int, type: AnyDynamicType)> { 65 override var reason: String { 66 "Argument at index '\(param.index)' couldn't be cast to type \(param.type.description)" 67 } 68 } 69 70 private class ModuleUnavailableException: GenericException<String> { 71 override var reason: String { 72 "Module '\(param)' is no longer available" 73 } 74 } 75