Skip to content
Merged
18 changes: 13 additions & 5 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,22 +37,30 @@ jobs:
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@nightly
with:
toolchain: nightly-2025-10-31
toolchain: nightly-2026-05-28
components: rustfmt, clippy, llvm-tools-preview, rustc-dev
- name: Cache on ${{ github.ref_name }}
uses: Swatinem/rust-cache@v2
with:
shared-key: warm
- name: Install Dylint
uses: taiki-e/install-action@v2
# Build Dylint from crates.io instead of using prebuilt binaries: the
# binaries attached to dylint's GitHub releases (since v6.0.1) are built
# inside the dylint repo and bake in a path dependency on
# /home/runner/work/dylint/dylint/driver, which breaks driver builds.
- name: Install cargo-dylint
uses: taiki-e/cache-cargo-install-action@v2
with:
tool: cargo-dylint,dylint-link
tool: cargo-dylint@6.0.1
- name: Install dylint-link
uses: taiki-e/cache-cargo-install-action@v2
with:
tool: dylint-link@6.0.1
- name: Check formatting
run: cargo fmt --check
- name: Clippy
run: |
cargo +stable clippy --all-targets -- -D warnings
cargo +nightly-2025-10-31 clippy --all-targets --all-features -- -D warnings
cargo +nightly-2026-05-28 clippy --all-targets --all-features -- -D warnings
- name: Dylint tests
working-directory: nova_lint
run: cargo test
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ console = "=0.16.3"
ctrlc = "=3.5.2"
ecmascript_atomics = { version = "=0.2.3" }
fast-float = "=0.2.0"
hashbrown = "=0.17.0"
hashbrown = "=0.17.1"
lexical = { version = "=7.0.5", default-features = false, features = [
"std",
"write-integers",
Expand Down
21 changes: 14 additions & 7 deletions nova_cli/src/lib/child_hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use std::{
collections::VecDeque,
sync::{atomic::AtomicBool, mpsc},
thread,
time::Duration,
time::{Duration, Instant},
};

use nova_vm::ecmascript::{HostHooks, Job};
Expand All @@ -19,7 +19,7 @@ use crate::{ChildToHostMessage, HostToChildMessage};

pub struct CliChildHooks {
promise_job_queue: RefCell<VecDeque<Job>>,
macrotask_queue: RefCell<Vec<Job>>,
macrotask_queue: RefCell<Vec<(Option<Instant>, Job)>>,
pub(crate) receiver: mpsc::Receiver<HostToChildMessage>,
pub(crate) host_sender: mpsc::SyncSender<ChildToHostMessage>,
ready_to_leave: AtomicBool,
Expand Down Expand Up @@ -78,9 +78,11 @@ impl CliChildHooks {
let mut counter = 0u8;
while !off_thread_job_queue.is_empty() {
counter = counter.wrapping_add(1);
for (i, job) in off_thread_job_queue.iter().enumerate() {
if job.is_finished() {
let job = off_thread_job_queue.swap_remove(i);
let now = Instant::now();
for (i, (deadline, job)) in off_thread_job_queue.iter().enumerate() {
let deadline_reached = deadline.is_none_or(|d| now >= d);
if deadline_reached && job.is_finished() {
let (_, job) = off_thread_job_queue.swap_remove(i);
return Some(job);
}
}
Expand All @@ -96,14 +98,19 @@ impl CliChildHooks {

impl HostHooks for CliChildHooks {
fn enqueue_generic_job(&self, job: Job) {
self.macrotask_queue.borrow_mut().push(job);
self.macrotask_queue.borrow_mut().push((None, job));
}

fn enqueue_promise_job(&self, job: Job) {
self.promise_job_queue.borrow_mut().push_back(job);
}

fn enqueue_timeout_job(&self, _timeout_job: Job, _milliseconds: u64) {}
fn enqueue_timeout_job(&self, timeout_job: Job, milliseconds: u64) {
let deadline = Instant::now() + Duration::from_millis(milliseconds);
self.macrotask_queue
.borrow_mut()
.push((Some(deadline), timeout_job));
}

fn get_host_data(&self) -> &dyn std::any::Any {
self
Expand Down
29 changes: 21 additions & 8 deletions nova_cli/src/lib/host_hooks.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,14 @@
//! The [`HostHooks`] implementation for the main thread.

use std::{
cell::RefCell, collections::VecDeque, fmt::Debug, path::PathBuf, rc::Rc, sync::mpsc, thread,
time::Duration,
cell::RefCell,
collections::VecDeque,
fmt::Debug,
path::PathBuf,
rc::Rc,
sync::mpsc,
thread,
time::{Duration, Instant},
};

use nova_vm::{
Expand All @@ -29,7 +35,7 @@ pub enum ChildToHostMessage {

pub struct CliHostHooks {
promise_job_queue: RefCell<VecDeque<Job>>,
macrotask_queue: RefCell<Vec<Job>>,
macrotask_queue: RefCell<Vec<(Option<Instant>, Job)>>,
pub(crate) receiver: mpsc::Receiver<ChildToHostMessage>,
pub(crate) own_sender: mpsc::SyncSender<ChildToHostMessage>,
pub(crate) child_senders: RefCell<Vec<mpsc::SyncSender<HostToChildMessage>>>,
Expand Down Expand Up @@ -83,9 +89,11 @@ impl CliHostHooks {
let mut counter = 0u8;
while !off_thread_job_queue.is_empty() {
counter = counter.wrapping_add(1);
for (i, job) in off_thread_job_queue.iter().enumerate() {
if job.is_finished() {
let job = off_thread_job_queue.swap_remove(i);
let now = Instant::now();
for (i, (deadline, job)) in off_thread_job_queue.iter().enumerate() {
let deadline_reached = deadline.is_none_or(|d| now >= d);
if deadline_reached && job.is_finished() {
let (_, job) = off_thread_job_queue.swap_remove(i);
return Some(job);
}
}
Expand All @@ -101,14 +109,19 @@ impl CliHostHooks {

impl HostHooks for CliHostHooks {
fn enqueue_generic_job(&self, job: Job) {
self.macrotask_queue.borrow_mut().push(job);
self.macrotask_queue.borrow_mut().push((None, job));
}

fn enqueue_promise_job(&self, job: Job) {
self.promise_job_queue.borrow_mut().push_back(job);
}

fn enqueue_timeout_job(&self, _timeout_job: Job, _milliseconds: u64) {}
fn enqueue_timeout_job(&self, timeout_job: Job, milliseconds: u64) {
let deadline = Instant::now() + Duration::from_millis(milliseconds);
self.macrotask_queue
.borrow_mut()
.push((Some(deadline), timeout_job));
}

fn load_imported_module<'gc>(
&self,
Expand Down
8 changes: 5 additions & 3 deletions nova_lint/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,12 +45,14 @@ name = "spec_header_level"
path = "ui/spec_header_level.rs"

[dependencies]
clippy_utils = { git = "https://github.com/rust-lang/rust-clippy", rev = "c936595d17413c1f08e162e117e504fb4ed126e4" }
dylint_linting = { version = "5.0.0", features = ["constituent"] }
# Must match the nightly toolchain pinned in .github/workflows/ci.yml: each
# clippy_utils release only builds with its contemporary nightly.
clippy_utils = "0.1.98"
dylint_linting = { version = "6.0.1", features = ["constituent"] }
regex = "1"

[dev-dependencies]
dylint_testing = "5.0.0"
dylint_testing = "6.0.1"
nova_vm = { path = "../nova_vm" }

[package.metadata.rust-analyzer]
Expand Down
2 changes: 1 addition & 1 deletion nova_lint/rust-toolchain
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
[toolchain]
channel = "nightly-2025-10-31"
channel = "nightly-2026-05-28"
components = ["llvm-tools-preview", "rustc-dev"]
2 changes: 1 addition & 1 deletion nova_lint/src/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ pub fn match_def_paths(cx: &LateContext<'_>, did: DefId, syms: &[&[&str]]) -> bo

pub fn is_trait_item(cx: &LateContext<'_>, hir_id: HirId) -> bool {
if let Node::Item(item) = cx.tcx.parent_hir_node(hir_id) {
matches!(item.kind, ItemKind::Trait(..))
matches!(item.kind, ItemKind::Trait { .. })
} else {
false
}
Expand Down
Loading
Loading