Skip to content
Open
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
3 changes: 1 addition & 2 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -164,6 +164,7 @@ Options:
--flowgraph [<FORMAT>] Generate instruction flowgraph. Default is Graphviz [possible values: graphviz, mermaid]
--flowgraph-direction <FORMAT> Specifies the direction of the flowgraph. Default is top-top-bottom [possible values: top-to-bottom, bottom-to-top, left-to-right, right-to-left]
--debug-object Inject debugging object `$boa`
--test262-object Inject the test262 host object `$262`
-m, --module Treats the input files as modules
-r, --root <ROOT> Root path from where the module resolver will try to load the modules [default: .]
-h, --help Print help (see more with '--help')
Expand Down
3 changes: 2 additions & 1 deletion cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ rust-version.workspace = true
boa_engine = { workspace = true, features = ["deser", "float16", "flowgraph", "temporal", "trace", "xsum"] }
boa_parser.workspace = true
boa_gc.workspace = true
boa_runtime.workspace = true
boa_runtime = { workspace = true, features = ["test262"] }
rustyline = { workspace = true, features = ["derive", "with-file-history"] }
clap = { workspace = true, features = ["derive"] }
serde_json.workspace = true
Expand All @@ -33,6 +33,7 @@ rustls.workspace = true
[features]
default = [
"boa_engine/annex-b",
"boa_runtime/annex-b",
"boa_engine/experimental",
"boa_engine/intl_bundled",
"boa_engine/native-backtrace",
Expand Down
1 change: 1 addition & 0 deletions cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ Options:
--flowgraph [<FORMAT>] Generate instruction flowgraph. Default is Graphviz [possible values: graphviz, mermaid]
--flowgraph-direction <FORMAT> Specifies the direction of the flowgraph. Default is top-top-bottom [possible values: top-to-bottom, bottom-to-top, left-to-right, right-to-left]
--debug-object Inject debugging object `$boa`
--test262-object Inject the test262 host object `$262`
-m, --module Treats the input files as modules
-r, --root <ROOT> Root path from where the module resolver will try to load the modules [default: .]
-e, --expression <EXPR> Execute a JavaScript expression then exit
Expand Down
41 changes: 36 additions & 5 deletions cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,14 @@ struct Opt {
#[arg(long)]
debug_object: bool,

/// Inject the test262 host object `$262`.
#[arg(long)]
test262_object: bool,

/// Disallow the main thread from blocking (e.g. `Atomics.wait`).
#[arg(long)]
no_can_block: bool,

/// Treats the input files as modules.
#[arg(long, short = 'm', group = "mod")]
module: bool,
Expand Down Expand Up @@ -285,13 +293,18 @@ impl Drop for Counters {
///
/// Returns a error of type String with a error message,
/// if the source has a syntax or parsing error.
fn dump<R: ReadChar>(src: Source<'_, R>, args: &Opt, context: &mut Context) -> Result<()> {
fn dump<R: ReadChar>(
src: Source<'_, R>,
args: &Opt,
is_module: bool,
context: &mut Context,
) -> Result<()> {
if let Some(arg) = args.dump_ast {
let mut counters = Counters::new(args.time);
let arg = arg.unwrap_or_default();
let mut parser = boa_parser::Parser::new(src);
let dump =
if args.module {
if is_module {
let scope = context.realm().scope().clone();
let module = {
let _timer = counters.new_timer("Parsing");
Expand Down Expand Up @@ -386,7 +399,7 @@ fn evaluate_expr(
printer: &SharedExternalPrinterLogger,
) -> Result<()> {
if args.has_dump_flag() {
dump(Source::from_bytes(line), args, context)?;
dump(Source::from_bytes(line), args, args.module, context)?;
} else if let Some(flowgraph) = args.flowgraph {
match generate_flowgraph(
context,
Expand Down Expand Up @@ -433,8 +446,11 @@ fn evaluate_file(
loader: &SimpleModuleLoader,
printer: &SharedExternalPrinterLogger,
) -> Result<()> {
// Treat files with .mjs extension automatically as modules.
let is_module = args.module || file.extension().is_some_and(|ext| ext == "mjs");

if args.has_dump_flag() {
return dump(Source::from_filepath(file)?, args, context);
return dump(Source::from_filepath(file)?, args, is_module, context);
}

if let Some(flowgraph) = args.flowgraph {
Expand All @@ -450,7 +466,7 @@ fn evaluate_file(
return Ok(());
}

if args.module {
if is_module {
let source = Source::from_filepath(file)?;
let mut counters = Counters::new(args.time);
let module = {
Expand Down Expand Up @@ -546,6 +562,7 @@ fn main() -> Result<()> {
let context = &mut ContextBuilder::new()
.job_executor(executor.clone())
.module_loader(loader.clone())
.can_block(!args.no_can_block)
.build()
.map_err(|e| eyre!(e.to_string()))?;

Expand All @@ -562,6 +579,20 @@ fn main() -> Result<()> {
init_boa_debug_object(context);
}

if args.test262_object {
boa_runtime::test262::register_js262(
boa_runtime::test262::WorkerHandles::new(),
true, // register `console` in $262.agent worker threads
context,
);

// Add print() that test262 uses to report errors and async success.
// boa_tester handles it internally, but CLI should just print messages.
context
.eval(Source::from_bytes("var print = console.log.bind(console);"))
.expect("failed to define print");
}

// Configure optimizer options
let mut optimizer_options = OptimizerOptions::empty();
optimizer_options.set(OptimizerOptions::STATISTICS, args.optimizer_statistics);
Expand Down
3 changes: 3 additions & 0 deletions core/runtime/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ rust-version.workspace = true
boa_engine.workspace = true
base64.workspace = true
boa_gc.workspace = true
bus = { workspace = true, optional = true }
bytemuck.workspace = true
either = { workspace = true, optional = true }
futures = "0.3.32"
Expand Down Expand Up @@ -57,3 +58,5 @@ fetch = [
]
reqwest-blocking = ["dep:reqwest", "reqwest/blocking"]
process = []
annex-b = ["boa_engine/annex-b"]
test262 = ["dep:bus"]
3 changes: 3 additions & 0 deletions core/runtime/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ pub mod microtask;
#[cfg(feature = "process")]
pub mod process;
pub mod store;
/// Support for the `$262` test262 harness object.
#[cfg(feature = "test262")]
pub mod test262;
pub mod text;
#[cfg(feature = "url")]
pub mod url;
Expand Down
58 changes: 38 additions & 20 deletions tests/tester/src/exec/js262.rs → core/runtime/src/test262.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
use std::{
cell::RefCell,
rc::Rc,
sync::mpsc::{self, Sender},
sync::{
OnceLock,
mpsc::{self, Sender},
},
thread::JoinHandle,
time::Duration,
time::{Duration, Instant},
};

#[cfg(feature = "annex-b")]
Expand All @@ -18,25 +21,42 @@ use boa_engine::{
};
use bus::BusReader;

use crate::START;
/// Monotonic clock for `$262.agent.monotonicNow()`, initialized by `register_js262()`.
static START: OnceLock<Instant> = OnceLock::new();

pub(super) enum WorkerResult {
/// Result of a worker thread execution.
#[derive(Debug)]
pub enum WorkerResult {
/// The worker completed successfully.
Ok,
/// The worker returned an error.
Err(String),
/// The worker panicked.
Panic(String),
}

pub(super) type WorkerHandle = JoinHandle<Result<(), String>>;
type WorkerHandle = JoinHandle<Result<(), String>>;

/// Handles for worker threads spawned by `$262.agent.start()`.
#[derive(Debug, Clone)]
pub(super) struct WorkerHandles(Rc<RefCell<Vec<WorkerHandle>>>);
pub struct WorkerHandles(Rc<RefCell<Vec<WorkerHandle>>>);

impl Default for WorkerHandles {
fn default() -> Self {
Self::new()
}
}

impl WorkerHandles {
pub(super) fn new() -> Self {
/// Creates a new empty set of worker handles.
#[must_use]
pub fn new() -> Self {
Self(Rc::default())
}

pub(super) fn join_all(&mut self) -> Vec<WorkerResult> {
/// Joins all worker threads and returns their results.
#[allow(clippy::print_stderr)]
pub fn join_all(&mut self) -> Vec<WorkerResult> {
let handles = std::mem::take(&mut *self.0.borrow_mut());

handles
Expand Down Expand Up @@ -70,12 +90,13 @@ impl Drop for WorkerHandles {
}
}

/// Creates the object $262 in the context.
pub(super) fn register_js262(
handles: WorkerHandles,
console: bool,
context: &mut Context,
) -> JsObject {
/// Creates the object `$262` in the context.
///
/// # Panics
///
/// Panics if any of the expected global properties cannot be defined.
pub fn register_js262(handles: WorkerHandles, console: bool, context: &mut Context) -> JsObject {
START.get_or_init(Instant::now);
let global_obj = context.global_object();

let agent = agent_obj(handles, console, context);
Expand Down Expand Up @@ -205,6 +226,7 @@ fn monotonic_now(_: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValu
let clock = START
.get()
.ok_or_else(|| JsNativeError::typ().with_message("could not get the monotonic clock"))?;
#[allow(clippy::cast_precision_loss)]
Ok(JsValue::from(clock.elapsed().as_millis() as f64))
}

Expand Down Expand Up @@ -235,14 +257,10 @@ fn agent_obj(handles: WorkerHandles, console: bool, context: &mut Context) -> Js
register_js262_worker(rx, tx, context);

if console {
let console = boa_runtime::Console::init(context);
let console = crate::Console::init(context);

context
.register_global_property(
boa_runtime::Console::NAME,
console,
Attribute::all(),
)
.register_global_property(crate::Console::NAME, console, Attribute::all())
.expect("the console builtin shouldn't exist");
}

Expand Down
6 changes: 2 additions & 4 deletions tests/tester/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ rust-version.workspace = true

[dependencies]
boa_engine = { workspace = true, features = ["float16"] }
boa_runtime.workspace = true
boa_gc.workspace = true
boa_runtime = { workspace = true, features = ["test262"] }
clap = { workspace = true, features = ["derive"] }
serde = { workspace = true, features = ["derive"] }
serde_yaml = "0.9.34" # TODO: Track https://github.com/saphyr-rs/saphyr.
Expand All @@ -28,11 +27,10 @@ color-eyre.workspace = true
phf = { workspace = true, features = ["macros"] }
comfy-table.workspace = true
serde_repr.workspace = true
bus.workspace = true
cow-utils.workspace = true

[features]
annex-b = ["boa_engine/annex-b"]
annex-b = ["boa_engine/annex-b", "boa_runtime/annex-b"]
default = ["boa_engine/intl_bundled", "boa_engine/experimental", "annex-b"]

[lints]
Expand Down
4 changes: 2 additions & 2 deletions tests/tester/src/exec/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
//! Execution module for the test runner.

mod js262;
use boa_runtime::test262 as js262;

use crate::{
Harness, Outcome, Phase, SpecEdition, Statistics, SuiteResult, Test, TestFlags,
Expand All @@ -23,7 +23,7 @@ use rayon::prelude::*;
use rustc_hash::FxHashSet;
use std::{cell::RefCell, eprintln, path::Path, rc::Rc};

use self::js262::WorkerHandles;
use js262::WorkerHandles;

impl TestSuite {
/// Runs the test suite.
Expand Down
9 changes: 0 additions & 9 deletions tests/tester/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@ use std::{
ops::{Add, AddAssign},
path::{Path, PathBuf},
process::Command,
sync::OnceLock,
time::Instant,
};

use bitflags::bitflags;
Expand Down Expand Up @@ -46,8 +44,6 @@ mod exec;
mod read;
mod results;

static START: OnceLock<Instant> = OnceLock::new();

/// Structure that contains the configuration of the tester.
#[derive(Debug, Deserialize)]
struct Config {
Expand Down Expand Up @@ -188,11 +184,6 @@ const DEFAULT_TEST262_DIRECTORY: &str = "test262";
fn main() -> Result<()> {
color_eyre::install()?;

// initializes the monotonic clock.
START
.set(Instant::now())
.map_err(|_| eyre!("could not initialize the monotonic clock"))?;

match Cli::parse() {
Cli::Run {
verbose,
Expand Down