Skip to content

Commit d786374

Browse files
Merge pull request #349 from swiftwasm/yt/capture-error-message
Capture error message at JSException construction
2 parents 1caee45 + 80b3790 commit d786374

File tree

2 files changed

+31
-2
lines changed

2 files changed

+31
-2
lines changed

Sources/JavaScriptKit/JSException.swift

+17-2
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
/// let jsErrorValue = error.thrownValue
1313
/// }
1414
/// ```
15-
public struct JSException: Error, Equatable {
15+
public struct JSException: Error, Equatable, CustomStringConvertible {
1616
/// The value thrown from JavaScript.
1717
/// This can be any JavaScript value (error object, string, number, etc.).
1818
public var thrownValue: JSValue {
@@ -25,10 +25,25 @@ public struct JSException: Error, Equatable {
2525
/// from `Error` protocol.
2626
private nonisolated(unsafe) let _thrownValue: JSValue
2727

28+
/// A description of the exception.
29+
public let description: String
30+
31+
/// The stack trace of the exception.
32+
public let stack: String?
33+
2834
/// Initializes a new JSException instance with a value thrown from JavaScript.
2935
///
30-
/// Only available within the package.
36+
/// Only available within the package. This must be called on the thread where the exception object created.
3137
package init(_ thrownValue: JSValue) {
3238
self._thrownValue = thrownValue
39+
// Capture the stringified representation on the object owner thread
40+
// to bring useful info to the catching thread even if they are different threads.
41+
if let errorObject = thrownValue.object, let stack = errorObject.stack.string {
42+
self.description = "JSException(\(stack))"
43+
self.stack = stack
44+
} else {
45+
self.description = "JSException(\(thrownValue))"
46+
self.stack = nil
47+
}
3348
}
3449
}

Tests/JavaScriptEventLoopTests/WebWorkerTaskExecutorTests.swift

+14
Original file line numberDiff line numberDiff line change
@@ -620,6 +620,20 @@ final class WebWorkerTaskExecutorTests: XCTestCase {
620620
XCTAssertEqual(object["test"].string!, "Hello, World!")
621621
}
622622

623+
func testThrowJSExceptionAcrossThreads() async throws {
624+
let executor = try await WebWorkerTaskExecutor(numberOfThreads: 1)
625+
let task = Task(executorPreference: executor) {
626+
_ = try JSObject.global.eval.function!.throws("throw new Error()")
627+
}
628+
do {
629+
try await task.value
630+
XCTFail()
631+
} catch let error as JSException {
632+
// Stringify JSException coming from worker should be allowed
633+
_ = String(describing: error)
634+
}
635+
}
636+
623637
// func testDeinitJSObjectOnDifferentThread() async throws {
624638
// let executor = try await WebWorkerTaskExecutor(numberOfThreads: 1)
625639
//

0 commit comments

Comments
 (0)