diff --git a/IntegrationTests/TestSuites/Sources/PrimaryTests/UnitTestUtils.swift b/IntegrationTests/TestSuites/Sources/PrimaryTests/UnitTestUtils.swift index 5d915415d..eefa7194e 100644 --- a/IntegrationTests/TestSuites/Sources/PrimaryTests/UnitTestUtils.swift +++ b/IntegrationTests/TestSuites/Sources/PrimaryTests/UnitTestUtils.swift @@ -84,3 +84,20 @@ func expectString(_ value: JSValue, file: StaticString = #file, line: UInt = #li throw MessageError("Type of \(value) should be \"string\"", file: file, line: line, column: column) } } + +func expectThrow(_ body: @autoclosure () throws -> T, file: StaticString = #file, line: UInt = #line, column: UInt = #column) throws -> Error { + do { + _ = try body() + } catch { + return error + } + throw MessageError("Expect to throw an exception", file: file, line: line, column: column) +} + +func expectNotNil(_ value: T?, file: StaticString = #file, line: UInt = #line, column: UInt = #column) throws { + switch value { + case .some: return + case .none: + throw MessageError("Expect a non-nil value", file: file, line: line, column: column) + } +} diff --git a/IntegrationTests/TestSuites/Sources/PrimaryTests/main.swift b/IntegrationTests/TestSuites/Sources/PrimaryTests/main.swift index b522c8bef..09b61f50e 100644 --- a/IntegrationTests/TestSuites/Sources/PrimaryTests/main.swift +++ b/IntegrationTests/TestSuites/Sources/PrimaryTests/main.swift @@ -517,3 +517,57 @@ try test("JSValue accessor") { try expectEqual(globalObject1.prop_4[0], .number(3)) try expectEqual(globalObject1.prop_4[1], .number(4)) } + +try test("Exception") { + // ```js + // global.globalObject1 = { + // ... + // prop_9: { + // func1: function () { + // throw new Error(); + // }, + // func2: function () { + // throw "String Error"; + // }, + // func3: function () { + // throw 3.0 + // }, + // }, + // ... + // } + // ``` + // + + let globalObject1 = JSObject.global.globalObject1 + let prop_9: JSValue = globalObject1.prop_9 + + // MARK: Throwing method calls + let error1 = try expectThrow(try prop_9.object!.throwing.func1!()) + try expectEqual(error1 is JSValue, true) + let errorObject = JSError(from: error1 as! JSValue) + try expectNotNil(errorObject) + + let error2 = try expectThrow(try prop_9.object!.throwing.func2!()) + try expectEqual(error2 is JSValue, true) + let errorString = try expectString(error2 as! JSValue) + try expectEqual(errorString, "String Error") + + let error3 = try expectThrow(try prop_9.object!.throwing.func3!()) + try expectEqual(error3 is JSValue, true) + let errorNumber = try expectNumber(error3 as! JSValue) + try expectEqual(errorNumber, 3.0) + + // MARK: Simple function calls + let error4 = try expectThrow(try prop_9.func1.function!.throws()) + try expectEqual(error4 is JSValue, true) + let errorObject2 = JSError(from: error4 as! JSValue) + try expectNotNil(errorObject2) + + // MARK: Throwing constructor call + let Animal = JSObject.global.Animal.function! + _ = try Animal.throws.new("Tama", 3, true) + let ageError = try expectThrow(try Animal.throws.new("Tama", -3, true)) + try expectEqual(ageError is JSValue, true) + let errorObject3 = JSError(from: ageError as! JSValue) + try expectNotNil(errorObject3) +} diff --git a/IntegrationTests/bin/primary-tests.js b/IntegrationTests/bin/primary-tests.js index 2ab44c3de..b1f1049d1 100644 --- a/IntegrationTests/bin/primary-tests.js +++ b/IntegrationTests/bin/primary-tests.js @@ -36,9 +36,23 @@ global.globalObject1 = { }, prop_7: 3.14, prop_8: [0, , 2, 3, , , 6], + prop_9: { + func1: function () { + throw new Error(); + }, + func2: function () { + throw "String Error"; + }, + func3: function () { + throw 3.0 + }, + }, }; global.Animal = function (name, age, isCat) { + if (age < 0) { + throw new Error("Invalid age " + age); + } this.name = name; this.age = age; this.bark = () => { diff --git a/Runtime/src/index.ts b/Runtime/src/index.ts index 8a099c7d7..144fed737 100644 --- a/Runtime/src/index.ts +++ b/Runtime/src/index.ts @@ -118,7 +118,7 @@ class SwiftRuntimeHeap { export class SwiftRuntime { private instance: WebAssembly.Instance | null; private heap: SwiftRuntimeHeap; - private version: number = 700; + private version: number = 701; constructor() { this.instance = null; @@ -151,7 +151,7 @@ export class SwiftRuntime { for (let index = 0; index < args.length; index++) { const argument = args[index]; const base = argv + 16 * index; - writeValue(argument, base, base + 4, base + 8); + writeValue(argument, base, base + 4, base + 8, false); } let output: any; const callback_func_ref = this.heap.retain(function (result: any) { @@ -251,39 +251,59 @@ export class SwiftRuntime { value: any, kind_ptr: pointer, payload1_ptr: pointer, - payload2_ptr: pointer + payload2_ptr: pointer, + is_exception: boolean ) => { + const exceptionBit = (is_exception ? 1 : 0) << 31; if (value === null) { - writeUint32(kind_ptr, JavaScriptValueKind.Null); + writeUint32(kind_ptr, exceptionBit | JavaScriptValueKind.Null); return; } switch (typeof value) { case "boolean": { - writeUint32(kind_ptr, JavaScriptValueKind.Boolean); + writeUint32( + kind_ptr, + exceptionBit | JavaScriptValueKind.Boolean + ); writeUint32(payload1_ptr, value ? 1 : 0); break; } case "number": { - writeUint32(kind_ptr, JavaScriptValueKind.Number); + writeUint32( + kind_ptr, + exceptionBit | JavaScriptValueKind.Number + ); writeFloat64(payload2_ptr, value); break; } case "string": { - writeUint32(kind_ptr, JavaScriptValueKind.String); + writeUint32( + kind_ptr, + exceptionBit | JavaScriptValueKind.String + ); writeUint32(payload1_ptr, this.heap.retain(value)); break; } case "undefined": { - writeUint32(kind_ptr, JavaScriptValueKind.Undefined); + writeUint32( + kind_ptr, + exceptionBit | JavaScriptValueKind.Undefined + ); break; } case "object": { - writeUint32(kind_ptr, JavaScriptValueKind.Object); + writeUint32( + kind_ptr, + exceptionBit | JavaScriptValueKind.Object + ); writeUint32(payload1_ptr, this.heap.retain(value)); break; } case "function": { - writeUint32(kind_ptr, JavaScriptValueKind.Function); + writeUint32( + kind_ptr, + exceptionBit | JavaScriptValueKind.Function + ); writeUint32(payload1_ptr, this.heap.retain(value)); break; } @@ -332,7 +352,7 @@ export class SwiftRuntime { ) => { const obj = this.heap.referenceHeap(ref); const result = Reflect.get(obj, readString(name)); - writeValue(result, kind_ptr, payload1_ptr, payload2_ptr); + writeValue(result, kind_ptr, payload1_ptr, payload2_ptr, false); }, swjs_set_subscript: ( ref: ref, @@ -353,7 +373,7 @@ export class SwiftRuntime { ) => { const obj = this.heap.referenceHeap(ref); const result = Reflect.get(obj, index); - writeValue(result, kind_ptr, payload1_ptr, payload2_ptr); + writeValue(result, kind_ptr, payload1_ptr, payload2_ptr, false); }, swjs_encode_string: (ref: ref, bytes_ptr_result: pointer) => { const bytes = textEncoder.encode(this.heap.referenceHeap(ref)); @@ -383,12 +403,24 @@ export class SwiftRuntime { payload2_ptr: pointer ) => { const func = this.heap.referenceHeap(ref); - const result = Reflect.apply( - func, - undefined, - decodeValues(argv, argc) - ); - writeValue(result, kind_ptr, payload1_ptr, payload2_ptr); + let result: any; + try { + result = Reflect.apply( + func, + undefined, + decodeValues(argv, argc) + ); + } catch (error) { + writeValue( + error, + kind_ptr, + payload1_ptr, + payload2_ptr, + true + ); + return; + } + writeValue(result, kind_ptr, payload1_ptr, payload2_ptr, false); }, swjs_call_function_with_this: ( obj_ref: ref, @@ -401,12 +433,20 @@ export class SwiftRuntime { ) => { const obj = this.heap.referenceHeap(obj_ref); const func = this.heap.referenceHeap(func_ref); - const result = Reflect.apply( - func, - obj, - decodeValues(argv, argc) - ); - writeValue(result, kind_ptr, payload1_ptr, payload2_ptr); + let result: any; + try { + result = Reflect.apply(func, obj, decodeValues(argv, argc)); + } catch (error) { + writeValue( + error, + kind_ptr, + payload1_ptr, + payload2_ptr, + true + ); + return; + } + writeValue(result, kind_ptr, payload1_ptr, payload2_ptr, false); }, swjs_create_function: ( host_func_id: number, @@ -420,6 +460,31 @@ export class SwiftRuntime { }); writeUint32(func_ref_ptr, func_ref); }, + swjs_call_throwing_new: ( + ref: ref, + argv: pointer, + argc: number, + result_obj: pointer, + exception_kind_ptr: pointer, + exception_payload1_ptr: pointer, + exception_payload2_ptr: pointer + ) => { + const obj = this.heap.referenceHeap(ref); + let result: any; + try { + result = Reflect.construct(obj, decodeValues(argv, argc)); + } catch (error) { + writeValue( + error, + exception_kind_ptr, + exception_payload1_ptr, + exception_payload2_ptr, + true + ); + return; + } + writeUint32(result_obj, this.heap.retain(result)); + }, swjs_call_new: ( ref: ref, argv: pointer, @@ -428,10 +493,6 @@ export class SwiftRuntime { ) => { const obj = this.heap.referenceHeap(ref); const result = Reflect.construct(obj, decodeValues(argv, argc)); - if (typeof result != "object") - throw Error( - `Invalid result type of object constructor of "${obj}": "${result}"` - ); writeUint32(result_obj, this.heap.retain(result)); }, swjs_instanceof: (obj_ref: ref, constructor_ref: ref) => { diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift index adc4035ea..89bf168e3 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift @@ -19,25 +19,7 @@ public class JSFunction: JSObject { /// - Returns: The result of this call. @discardableResult public func callAsFunction(this: JSObject? = nil, arguments: [ConvertibleToJSValue]) -> JSValue { - let result = arguments.withRawJSValues { rawValues in - rawValues.withUnsafeBufferPointer { bufferPointer -> RawJSValue in - let argv = bufferPointer.baseAddress - let argc = bufferPointer.count - var result = RawJSValue() - if let thisId = this?.id { - _call_function_with_this(thisId, - self.id, argv, Int32(argc), - &result.kind, &result.payload1, &result.payload2) - } else { - _call_function( - self.id, argv, Int32(argc), - &result.kind, &result.payload1, &result.payload2 - ) - } - return result - } - } - return result.jsValue() + try! invokeJSFunction(self, arguments: arguments, this: this) } /// A variadic arguments version of `callAsFunction`. @@ -68,6 +50,25 @@ public class JSFunction: JSObject { } } + /// A modifier to call this function as a throwing function + /// + /// + /// ```javascript + /// function validateAge(age) { + /// if (age < 0) { + /// throw new Error("Invalid age"); + /// } + /// } + /// ``` + /// + /// ```swift + /// let validateAge = JSObject.global.validateAge.function! + /// try validateAge.throws(20) + /// ``` + public var `throws`: JSThrowingFunction { + JSThrowingFunction(self) + } + /// A variadic arguments version of `new`. public func new(_ arguments: ConvertibleToJSValue...) -> JSObject { new(arguments: arguments) @@ -87,6 +88,97 @@ public class JSFunction: JSObject { } } +/// A `JSFunction` wrapper that enables throwing function calls. +/// Exceptions produced by JavaScript functions will be thrown as `JSValue`. +public class JSThrowingFunction { + private let base: JSFunction + public init(_ base: JSFunction) { + self.base = base + } + + /// Call this function with given `arguments` and binding given `this` as context. + /// - Parameters: + /// - this: The value to be passed as the `this` parameter to this function. + /// - arguments: Arguments to be passed to this function. + /// - Returns: The result of this call. + @discardableResult + public func callAsFunction(this: JSObject? = nil, arguments: [ConvertibleToJSValue]) throws -> JSValue { + try invokeJSFunction(base, arguments: arguments, this: this) + } + + /// A variadic arguments version of `callAsFunction`. + @discardableResult + public func callAsFunction(this: JSObject? = nil, _ arguments: ConvertibleToJSValue...) throws -> JSValue { + try self(this: this, arguments: arguments) + } + + /// Instantiate an object from this function as a throwing constructor. + /// + /// Guaranteed to return an object because either: + /// + /// - a. the constructor explicitly returns an object, or + /// - b. the constructor returns nothing, which causes JS to return the `this` value, or + /// - c. the constructor returns undefined, null or a non-object, in which case JS also returns `this`. + /// + /// - Parameter arguments: Arguments to be passed to this constructor function. + /// - Returns: A new instance of this constructor. + public func new(arguments: [ConvertibleToJSValue]) throws -> JSObject { + try arguments.withRawJSValues { rawValues -> Result in + rawValues.withUnsafeBufferPointer { bufferPointer in + let argv = bufferPointer.baseAddress + let argc = bufferPointer.count + + var exceptionKind = JavaScriptValueKindAndFlags() + var exceptionPayload1 = JavaScriptPayload1() + var exceptionPayload2 = JavaScriptPayload2() + var resultObj = JavaScriptObjectRef() + _call_throwing_new( + self.base.id, argv, Int32(argc), + &resultObj, &exceptionKind, &exceptionPayload1, &exceptionPayload2 + ) + if exceptionKind.isException { + let exception = RawJSValue(kind: exceptionKind.kind, payload1: exceptionPayload1, payload2: exceptionPayload2) + return .failure(exception.jsValue()) + } + return .success(JSObject(id: resultObj)) + } + }.get() + } + + /// A variadic arguments version of `new`. + public func new(_ arguments: ConvertibleToJSValue...) throws -> JSObject { + try new(arguments: arguments) + } +} + +fileprivate func invokeJSFunction(_ jsFunc: JSFunction, arguments: [ConvertibleToJSValue], this: JSObject?) throws -> JSValue { + let (result, isException) = arguments.withRawJSValues { rawValues in + rawValues.withUnsafeBufferPointer { bufferPointer -> (JSValue, Bool) in + let argv = bufferPointer.baseAddress + let argc = bufferPointer.count + var kindAndFlags = JavaScriptValueKindAndFlags() + var payload1 = JavaScriptPayload1() + var payload2 = JavaScriptPayload2() + if let thisId = this?.id { + _call_function_with_this(thisId, + jsFunc.id, argv, Int32(argc), + &kindAndFlags, &payload1, &payload2) + } else { + _call_function( + jsFunc.id, argv, Int32(argc), + &kindAndFlags, &payload1, &payload2 + ) + } + let result = RawJSValue(kind: kindAndFlags.kind, payload1: payload1, payload2: payload2) + return (result.jsValue(), kindAndFlags.isException) + } + } + if isException { + throw result + } + return result +} + /// `JSClosure` represents a JavaScript function the body of which is written in Swift. /// This type can be passed as a callback handler to JavaScript functions. /// Note that the lifetime of `JSClosure` should be managed by users manually diff --git a/Sources/JavaScriptKit/FundamentalObjects/JSObject.swift b/Sources/JavaScriptKit/FundamentalObjects/JSObject.swift index 63b1b75ae..9763e1343 100644 --- a/Sources/JavaScriptKit/FundamentalObjects/JSObject.swift +++ b/Sources/JavaScriptKit/FundamentalObjects/JSObject.swift @@ -76,6 +76,27 @@ public class JSObject: Equatable { get { getJSValue(this: self, index: Int32(index)) } set { setJSValue(this: self, index: Int32(index), value: newValue) } } + + /// A modifier to call methods as throwing methods capturing `this` + /// + /// + /// ```javascript + /// const animal = { + /// validateAge: function() { + /// if (this.age < 0) { + /// throw new Error("Invalid age"); + /// } + /// } + /// } + /// ``` + /// + /// ```swift + /// let animal = JSObject.global.animal.object! + /// try animal.throwing.validateAge!() + /// ``` + public var `throwing`: JSThrowingObject { + JSThrowingObject(self) + } /// Return `true` if this value is an instance of the passed `constructor` function. /// - Parameter constructor: The constructor function to check. @@ -113,3 +134,32 @@ public class JSObject: Equatable { extension JSObject: CustomStringConvertible { public var description: String { self.toString!().string! } } + + +/// A `JSObject` wrapper that enables throwing method calls capturing `this`. +/// Exceptions produced by JavaScript functions will be thrown as `JSValue`. +@dynamicMemberLookup +public class JSThrowingObject { + private let base: JSObject + public init(_ base: JSObject) { + self.base = base + } + + /// Returns the `name` member method binding this object as `this` context. + /// - Parameter name: The name of this object's member to access. + /// - Returns: The `name` member method binding this object as `this` context. + @_disfavoredOverload + public subscript(_ name: String) -> ((ConvertibleToJSValue...) throws -> JSValue)? { + guard let function = base[name].function?.throws else { return nil } + return { [base] (arguments: ConvertibleToJSValue...) in + try function(this: base, arguments: arguments) + } + } + + /// A convenience method of `subscript(_ name: String) -> ((ConvertibleToJSValue...) throws -> JSValue)?` + /// to access the member through Dynamic Member Lookup. + @_disfavoredOverload + public subscript(dynamicMember name: String) -> ((ConvertibleToJSValue...) throws -> JSValue)? { + self[name] + } +} diff --git a/Sources/JavaScriptKit/JSValue.swift b/Sources/JavaScriptKit/JSValue.swift index 57c835cd5..438104441 100644 --- a/Sources/JavaScriptKit/JSValue.swift +++ b/Sources/JavaScriptKit/JSValue.swift @@ -102,6 +102,8 @@ extension JSValue { } } +extension JSValue: Swift.Error {} + extension JSValue { public func fromJSValue() -> Type? where Type: ConstructibleFromJSValue { return Type.construct(from: self) diff --git a/Sources/JavaScriptKit/XcodeSupport.swift b/Sources/JavaScriptKit/XcodeSupport.swift index 3e2c24a69..0777e911a 100644 --- a/Sources/JavaScriptKit/XcodeSupport.swift +++ b/Sources/JavaScriptKit/XcodeSupport.swift @@ -49,7 +49,7 @@ import _CJavaScriptKit func _call_function( _: JavaScriptObjectRef, _: UnsafePointer!, _: Int32, - _: UnsafeMutablePointer!, + _: UnsafeMutablePointer!, _: UnsafeMutablePointer!, _: UnsafeMutablePointer! ) { fatalError() } @@ -57,7 +57,7 @@ import _CJavaScriptKit _: JavaScriptObjectRef, _: JavaScriptObjectRef, _: UnsafePointer!, _: Int32, - _: UnsafeMutablePointer!, + _: UnsafeMutablePointer!, _: UnsafeMutablePointer!, _: UnsafeMutablePointer! ) { fatalError() } @@ -66,6 +66,14 @@ import _CJavaScriptKit _: UnsafePointer!, _: Int32, _: UnsafeMutablePointer! ) { fatalError() } + func _call_throwing_new( + _: JavaScriptObjectRef, + _: UnsafePointer!, _: Int32, + _: UnsafeMutablePointer!, + _: UnsafeMutablePointer!, + _: UnsafeMutablePointer!, + _: UnsafeMutablePointer! + ) { fatalError() } func _instanceof( _: JavaScriptObjectRef, _: JavaScriptObjectRef diff --git a/Sources/_CJavaScriptKit/_CJavaScriptKit.c b/Sources/_CJavaScriptKit/_CJavaScriptKit.c index a1282b5d9..dd0f40959 100644 --- a/Sources/_CJavaScriptKit/_CJavaScriptKit.c +++ b/Sources/_CJavaScriptKit/_CJavaScriptKit.c @@ -29,7 +29,7 @@ void _cleanup_host_function_call(void *argv_buffer) { /// this and `SwiftRuntime.version` in `./Runtime/src/index.ts`. __attribute__((export_name("swjs_library_version"))) int _library_version() { - return 700; + return 701; } #endif diff --git a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h index c180a7e28..6d383b3ea 100644 --- a/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h +++ b/Sources/_CJavaScriptKit/include/_CJavaScriptKit.h @@ -23,6 +23,11 @@ typedef enum __attribute__((enum_extensibility(closed))) { JavaScriptValueKindFunction = 6, } JavaScriptValueKind; +typedef struct { + JavaScriptValueKind kind: 31; + bool isException: 1; +} JavaScriptValueKindAndFlags; + typedef unsigned JavaScriptPayload1; typedef double JavaScriptPayload2; @@ -155,13 +160,13 @@ extern void _load_string(const JavaScriptObjectRef bytes, unsigned char *buffer) /// @param ref The target JavaScript function to call. /// @param argv A list of `RawJSValue` arguments to apply. /// @param argc The length of `argv``. -/// @param result_kind A result pointer of JavaScript value kind to get. -/// @param result_payload1 A result pointer of first payload of JavaScript value to set the target object. -/// @param result_payload2 A result pointer of second payload of JavaScript value to set the target object. +/// @param result_kind A result pointer of JavaScript value kind of returned result or thrown exception. +/// @param result_payload1 A result pointer of first payload of JavaScript value of returned result or thrown exception. +/// @param result_payload2 A result pointer of second payload of JavaScript value of returned result or thrown exception. __attribute__((__import_module__("javascript_kit"), __import_name__("swjs_call_function"))) extern void _call_function(const JavaScriptObjectRef ref, const RawJSValue *argv, - const int argc, JavaScriptValueKind *result_kind, + const int argc, JavaScriptValueKindAndFlags *result_kind, JavaScriptPayload1 *result_payload1, JavaScriptPayload2 *result_payload2); @@ -171,15 +176,15 @@ extern void _call_function(const JavaScriptObjectRef ref, const RawJSValue *argv /// @param func_ref The target JavaScript function to call. /// @param argv A list of `RawJSValue` arguments to apply. /// @param argc The length of `argv``. -/// @param result_kind A result pointer of JavaScript value kind to get. -/// @param result_payload1 A result pointer of first payload of JavaScript value to set the target object. -/// @param result_payload2 A result pointer of second payload of JavaScript value to set the target object. +/// @param result_kind A result pointer of JavaScript value kind of returned result or thrown exception. +/// @param result_payload1 A result pointer of first payload of JavaScript value of returned result or thrown exception. +/// @param result_payload2 A result pointer of second payload of JavaScript value of returned result or thrown exception. __attribute__((__import_module__("javascript_kit"), __import_name__("swjs_call_function_with_this"))) extern void _call_function_with_this(const JavaScriptObjectRef _this, const JavaScriptObjectRef func_ref, const RawJSValue *argv, const int argc, - JavaScriptValueKind *result_kind, + JavaScriptValueKindAndFlags *result_kind, JavaScriptPayload1 *result_payload1, JavaScriptPayload2 *result_payload2); @@ -195,6 +200,24 @@ extern void _call_new(const JavaScriptObjectRef ref, const RawJSValue *argv, const int argc, JavaScriptObjectRef *result_obj); +/// `_call_throwing_new` calls JavaScript object constructor with given arguments list. +/// +/// @param ref The target JavaScript constructor to call. +/// @param argv A list of `RawJSValue` arguments to apply. +/// @param argc The length of `argv``. +/// @param result_obj A result pointer of the constructed object. +/// @param exception_kind A result pointer of JavaScript value kind of thrown exception. +/// @param exception_payload1 A result pointer of first payload of JavaScript value of thrown exception. +/// @param exception_payload2 A result pointer of second payload of JavaScript value of thrown exception. +__attribute__((__import_module__("javascript_kit"), + __import_name__("swjs_call_throwing_new"))) +extern void _call_throwing_new(const JavaScriptObjectRef ref, + const RawJSValue *argv, const int argc, + JavaScriptObjectRef *result_obj, + JavaScriptValueKindAndFlags *exception_kind, + JavaScriptPayload1 *exception_payload1, + JavaScriptPayload2 *exception_payload2); + /// `_instanceof` acts like JavaScript `instanceof` operator. /// /// @param obj The target object to check its prototype chain.