Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed

- Fixed cross-thread stack overflow false positives in parallel mode by updating stack baseline before QuickJS C entry points
- Fixed promise polling not returning Ready variant when exception occurs
- Fixed iterators to use correct IteratorPrototype chain
- Fixed a latent ABI layout vulnerability in `JS_NewPromiseCapability` FFI boundary by replacing tuple with strictly compatible array

Expand Down
5 changes: 5 additions & 0 deletions core/src/context/ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,6 +261,11 @@ impl<'js> Ctx<'js> {
}
}

/// Returns true if there is a pending JavaScript exception.
pub fn has_exception(&self) -> bool {
unsafe { qjs::JS_HasException(self.ctx.as_ptr()) }
}

/// Throws a JavaScript value as a new exception.
/// Always returns `Error::Exception`;
pub fn throw(&self, value: Value<'js>) -> Error {
Expand Down
7 changes: 7 additions & 0 deletions core/src/value/promise.rs
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,13 @@ where
return Poll::Ready(x);
}

// Check for pending exception (e.g., from interrupt handler).
// If there's a pending exception, the promise will never settle,
// so return the error instead of hanging forever.
if this.promise.ctx.has_exception() {
return Poll::Ready(Err(Error::Exception));
}

if this.state.is_none() {
let inner = Rc::new(RefCell::new(cx.waker().clone()));
this.state = Some(inner.clone());
Expand Down
Loading