Skip to content

Commit ff490e9

Browse files
committed
Remove a not neede try/catch when evaluating JSFunction values so we can improve error reporting by not hiding the information we get form the JS crash
1 parent d2f0765 commit ff490e9

File tree

8 files changed

+133
-11
lines changed

8 files changed

+133
-11
lines changed

IntegrationTests/TestSuites/Sources/PrimaryTests/UnitTestUtils.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,12 @@ func expectThrow<T>(_ body: @autoclosure () throws -> T, file: StaticString = #f
103103
throw MessageError("Expect to throw an exception", file: file, line: line, column: column)
104104
}
105105

106+
func wrapUnsafeThrowableFunction(_ body: @escaping () -> Void, file: StaticString = #file, line: UInt = #line, column: UInt = #column) throws -> Error {
107+
JSObject.global.callThrowingClosure.function!(JSClosure { _ in
108+
body()
109+
return .undefined
110+
})
111+
}
106112
func expectNotNil<T>(_ value: T?, file: StaticString = #file, line: UInt = #line, column: UInt = #column) throws {
107113
switch value {
108114
case .some: return

IntegrationTests/TestSuites/Sources/PrimaryTests/main.swift

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -705,27 +705,21 @@ try test("Exception") {
705705
let prop_9: JSValue = globalObject1.prop_9
706706

707707
// MARK: Throwing method calls
708-
let error1 = try expectThrow(try prop_9.object!.throwing.func1!())
708+
let error1 = try wrapUnsafeThrowableFunction { _ = prop_9.object!.func1!() }
709709
try expectEqual(error1 is JSValue, true)
710710
let errorObject = JSError(from: error1 as! JSValue)
711711
try expectNotNil(errorObject)
712712

713-
let error2 = try expectThrow(try prop_9.object!.throwing.func2!())
713+
let error2 = try wrapUnsafeThrowableFunction { _ = prop_9.object!.func2!() }
714714
try expectEqual(error2 is JSValue, true)
715715
let errorString = try expectString(error2 as! JSValue)
716716
try expectEqual(errorString, "String Error")
717717

718-
let error3 = try expectThrow(try prop_9.object!.throwing.func3!())
718+
let error3 = try wrapUnsafeThrowableFunction { _ = prop_9.object!.func3!() }
719719
try expectEqual(error3 is JSValue, true)
720720
let errorNumber = try expectNumber(error3 as! JSValue)
721721
try expectEqual(errorNumber, 3.0)
722722

723-
// MARK: Simple function calls
724-
let error4 = try expectThrow(try prop_9.func1.function!.throws())
725-
try expectEqual(error4 is JSValue, true)
726-
let errorObject2 = JSError(from: error4 as! JSValue)
727-
try expectNotNil(errorObject2)
728-
729723
// MARK: Throwing constructor call
730724
let Animal = JSObject.global.Animal.function!
731725
_ = try Animal.throws.new("Tama", 3, true)

IntegrationTests/bin/primary-tests.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,14 @@ global.Animal = function (name, age, isCat) {
8282
}
8383
};
8484

85+
global.callThrowingClosure = (c) => {
86+
try {
87+
c()
88+
} catch (error) {
89+
return error
90+
}
91+
}
92+
8593
const { startWasiTask } = require("../lib");
8694

8795
startWasiTask("./dist/PrimaryTests.wasm").catch((err) => {

Runtime/src/index.ts

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,6 +226,29 @@ export class SwiftRuntime {
226226
this.memory
227227
);
228228
},
229+
swjs_call_function_unsafe: (
230+
ref: ref,
231+
argv: pointer,
232+
argc: number,
233+
kind_ptr: pointer,
234+
payload1_ptr: pointer,
235+
payload2_ptr: pointer
236+
) => {
237+
const func = this.memory.getObject(ref);
238+
const result = Reflect.apply(
239+
func,
240+
undefined,
241+
JSValue.decodeArray(argv, argc, this.memory)
242+
);
243+
JSValue.write(
244+
result,
245+
kind_ptr,
246+
payload1_ptr,
247+
payload2_ptr,
248+
false,
249+
this.memory
250+
);
251+
},
229252

230253
swjs_call_function_with_this: (
231254
obj_ref: ref,
@@ -265,6 +288,32 @@ export class SwiftRuntime {
265288
this.memory
266289
);
267290
},
291+
292+
swjs_call_function_with_this_unsafe: (
293+
obj_ref: ref,
294+
func_ref: ref,
295+
argv: pointer,
296+
argc: number,
297+
kind_ptr: pointer,
298+
payload1_ptr: pointer,
299+
payload2_ptr: pointer
300+
) => {
301+
const obj = this.memory.getObject(obj_ref);
302+
const func = this.memory.getObject(func_ref);
303+
const result = Reflect.apply(
304+
func,
305+
obj,
306+
JSValue.decodeArray(argv, argc, this.memory)
307+
);
308+
JSValue.write(
309+
result,
310+
kind_ptr,
311+
payload1_ptr,
312+
payload2_ptr,
313+
false,
314+
this.memory
315+
);
316+
},
268317
swjs_call_new: (ref: ref, argv: pointer, argc: number) => {
269318
const constructor = this.memory.getObject(ref);
270319
const instance = Reflect.construct(

Runtime/src/types.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,14 @@ export interface ImportedFunctions {
5858
payload1_ptr: pointer,
5959
payload2_ptr: pointer
6060
): void;
61+
swjs_call_function_unsafe(
62+
ref: number,
63+
argv: pointer,
64+
argc: number,
65+
kind_ptr: pointer,
66+
payload1_ptr: pointer,
67+
payload2_ptr: pointer
68+
): void;
6169
swjs_call_function_with_this(
6270
obj_ref: ref,
6371
func_ref: ref,
@@ -67,6 +75,15 @@ export interface ImportedFunctions {
6775
payload1_ptr: pointer,
6876
payload2_ptr: pointer
6977
): void;
78+
swjs_call_function_with_this_unsafe(
79+
obj_ref: ref,
80+
func_ref: ref,
81+
argv: pointer,
82+
argc: number,
83+
kind_ptr: pointer,
84+
payload1_ptr: pointer,
85+
payload2_ptr: pointer
86+
): void;
7087
swjs_call_new(ref: number, argv: pointer, argc: number): number;
7188
swjs_call_throwing_new(
7289
ref: number,

Sources/JavaScriptKit/FundamentalObjects/JSFunction.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,11 +93,11 @@ internal func invokeJSFunction(_ jsFunc: JSFunction, arguments: [ConvertibleToJS
9393
var payload1 = JavaScriptPayload1()
9494
var payload2 = JavaScriptPayload2()
9595
if let thisId = this?.id {
96-
_call_function_with_this(thisId,
96+
_call_function_with_this_unsafe(thisId,
9797
jsFunc.id, argv, Int32(argc),
9898
&kindAndFlags, &payload1, &payload2)
9999
} else {
100-
_call_function(
100+
_call_function_unsafe(
101101
jsFunc.id, argv, Int32(argc),
102102
&kindAndFlags, &payload1, &payload2
103103
)

Sources/JavaScriptKit/XcodeSupport.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,13 @@ import _CJavaScriptKit
5353
_: UnsafeMutablePointer<JavaScriptPayload1>!,
5454
_: UnsafeMutablePointer<JavaScriptPayload2>!
5555
) { fatalError() }
56+
func _call_function_unsafe(
57+
_: JavaScriptObjectRef,
58+
_: UnsafePointer<RawJSValue>!, _: Int32,
59+
_: UnsafeMutablePointer<JavaScriptValueKindAndFlags>!,
60+
_: UnsafeMutablePointer<JavaScriptPayload1>!,
61+
_: UnsafeMutablePointer<JavaScriptPayload2>!
62+
) { fatalError() }
5663
func _call_function_with_this(
5764
_: JavaScriptObjectRef,
5865
_: JavaScriptObjectRef,
@@ -61,6 +68,14 @@ import _CJavaScriptKit
6168
_: UnsafeMutablePointer<JavaScriptPayload1>!,
6269
_: UnsafeMutablePointer<JavaScriptPayload2>!
6370
) { fatalError() }
71+
func _call_function_with_this_unsafe(
72+
_: JavaScriptObjectRef,
73+
_: JavaScriptObjectRef,
74+
_: UnsafePointer<RawJSValue>!, _: Int32,
75+
_: UnsafeMutablePointer<JavaScriptValueKindAndFlags>!,
76+
_: UnsafeMutablePointer<JavaScriptPayload1>!,
77+
_: UnsafeMutablePointer<JavaScriptPayload2>!
78+
) { fatalError() }
6479
func _call_new(
6580
_: JavaScriptObjectRef,
6681
_: UnsafePointer<RawJSValue>!, _: Int32

Sources/_CJavaScriptKit/include/_CJavaScriptKit.h

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,21 @@ extern void _call_function(const JavaScriptObjectRef ref, const RawJSValue *argv
170170
JavaScriptPayload1 *result_payload1,
171171
JavaScriptPayload2 *result_payload2);
172172

173+
/// `_call_function` calls JavaScript function with given arguments list without capturing any exception
174+
///
175+
/// @param ref The target JavaScript function to call.
176+
/// @param argv A list of `RawJSValue` arguments to apply.
177+
/// @param argc The length of `argv``.
178+
/// @param result_kind A result pointer of JavaScript value kind of returned result or thrown exception.
179+
/// @param result_payload1 A result pointer of first payload of JavaScript value of returned result or thrown exception.
180+
/// @param result_payload2 A result pointer of second payload of JavaScript value of returned result or thrown exception.
181+
__attribute__((__import_module__("javascript_kit"),
182+
__import_name__("swjs_call_function_unsafe")))
183+
extern void _call_function_unsafe(const JavaScriptObjectRef ref, const RawJSValue *argv,
184+
const int argc, JavaScriptValueKindAndFlags *result_kind,
185+
JavaScriptPayload1 *result_payload1,
186+
JavaScriptPayload2 *result_payload2);
187+
173188
/// `_call_function_with_this` calls JavaScript function with given arguments list and given `_this`.
174189
///
175190
/// @param _this The value of `this` provided for the call to `func_ref`.
@@ -188,6 +203,24 @@ extern void _call_function_with_this(const JavaScriptObjectRef _this,
188203
JavaScriptPayload1 *result_payload1,
189204
JavaScriptPayload2 *result_payload2);
190205

206+
/// `_call_function_with_this` calls JavaScript function with given arguments list and given `_this` without capturing any exception.
207+
///
208+
/// @param _this The value of `this` provided for the call to `func_ref`.
209+
/// @param func_ref The target JavaScript function to call.
210+
/// @param argv A list of `RawJSValue` arguments to apply.
211+
/// @param argc The length of `argv``.
212+
/// @param result_kind A result pointer of JavaScript value kind of returned result or thrown exception.
213+
/// @param result_payload1 A result pointer of first payload of JavaScript value of returned result or thrown exception.
214+
/// @param result_payload2 A result pointer of second payload of JavaScript value of returned result or thrown exception.
215+
__attribute__((__import_module__("javascript_kit"),
216+
__import_name__("swjs_call_function_with_this_unsafe")))
217+
extern void _call_function_with_this_unsafe(const JavaScriptObjectRef _this,
218+
const JavaScriptObjectRef func_ref,
219+
const RawJSValue *argv, const int argc,
220+
JavaScriptValueKindAndFlags *result_kind,
221+
JavaScriptPayload1 *result_payload1,
222+
JavaScriptPayload2 *result_payload2);
223+
191224
/// `_call_new` calls JavaScript object constructor with given arguments list.
192225
///
193226
/// @param ref The target JavaScript constructor to call.

0 commit comments

Comments
 (0)