From 7a8d2bd17487a322fa253354dd66f40cec00c53d Mon Sep 17 00:00:00 2001 From: Hans Halverson Date: Mon, 15 Jun 2026 16:34:01 -0700 Subject: [PATCH] [tests] Create compatibility test shell functions and global arguments array --- src/js/common/options.rs | 48 ++++ src/js/runtime/eval/eval.rs | 36 ++- .../runtime/intrinsics/error_constructor.rs | 13 + src/js/runtime/intrinsics/global_object.rs | 11 + src/js/runtime/intrinsics/rust_runtime.rs | 2 +- src/js/runtime/mod.rs | 1 + src/js/runtime/realm.rs | 33 +++ src/js/runtime/test_262_object.rs | 39 +-- src/js/runtime/test_shell.rs | 272 ++++++++++++++++++ src/main.rs | 13 +- tests/fuzz/src/main.rs | 9 +- tests/harness/src/runner.rs | 4 +- 12 files changed, 427 insertions(+), 54 deletions(-) create mode 100644 src/js/runtime/test_shell.rs diff --git a/src/js/common/options.rs b/src/js/common/options.rs index 464f1656..2cdb7e72 100644 --- a/src/js/common/options.rs +++ b/src/js/common/options.rs @@ -34,6 +34,10 @@ pub struct Args { #[arg(long, default_value_t = false)] pub expose_test_262: bool, + /// Expose the test shell methods for compatibility with the test shell of other engines + #[arg(long, default_value_t = false)] + pub expose_test_shell_compat: bool, + /// The minimum heap size. Must be between 4MB and 4GB, specified in the form "XMB" or "XGB" /// where X is a power of 2. #[arg(long, value_parser = parse_min_heap_size_arg)] @@ -76,6 +80,10 @@ pub struct Args { #[arg(required = true)] pub files: Vec, + + /// Args after an optional `--` + #[arg(last = true, hide = true)] + pub script_args: Vec, } /// Options passed throughout the program. @@ -113,6 +121,18 @@ pub struct Options { /// Print statistics about the parse phase pub parse_stats: bool, + /// Expose the global `gc` object + pub expose_gc: bool, + + /// Expose the global `$262` object for test262 tests + pub expose_test_262: bool, + + /// Expose global methods for test shell compatibility + pub expose_test_shell_compat: bool, + + /// Args passed to the program after `--` + pub script_args: Vec, + /// Create the heap from this SerializedHeap if set, otherwise create heap from scratch. pub serialized_heap: Option<&'static SerializedHeap<'static>>, } @@ -154,6 +174,10 @@ impl OptionsBuilder { max_heap_size: DEFAULT_MAX_HEAP_SIZE, no_color: false, parse_stats: false, + expose_gc: false, + expose_test_262: false, + expose_test_shell_compat: false, + script_args: vec![], serialized_heap: get_default_serialized_heap(), }) } @@ -177,6 +201,10 @@ impl OptionsBuilder { .max_heap_size(args.max_heap_size.unwrap_or(DEFAULT_MAX_HEAP_SIZE)) .no_color(args.no_color) .parse_stats(args.parse_stats) + .expose_gc(args.expose_gc) + .expose_test_262(args.expose_test_262) + .expose_test_shell_compat(args.expose_test_shell_compat) + .script_args(args.script_args.clone()) } /// Return the options that have been built, consuming the builder. @@ -256,6 +284,26 @@ impl OptionsBuilder { self } + pub fn expose_gc(mut self, expose_gc: bool) -> Self { + self.0.expose_gc = expose_gc; + self + } + + pub fn expose_test_262(mut self, expose_test_262: bool) -> Self { + self.0.expose_test_262 = expose_test_262; + self + } + + pub fn expose_test_shell_compat(mut self, expose_test_shell_compat: bool) -> Self { + self.0.expose_test_shell_compat = expose_test_shell_compat; + self + } + + pub fn script_args(mut self, script_args: Vec) -> Self { + self.0.script_args = script_args; + self + } + pub fn serialized_heap( mut self, serialized_heap: Option<&'static SerializedHeap<'static>>, diff --git a/src/js/runtime/eval/eval.rs b/src/js/runtime/eval/eval.rs index d138121e..ac53a7ac 100644 --- a/src/js/runtime/eval/eval.rs +++ b/src/js/runtime/eval/eval.rs @@ -4,14 +4,14 @@ use crate::{ common::wtf_8::{Wtf8Cow, Wtf8String}, parser::{ ParseContext, - analyze::{PrivateNameUsage, analyze_for_eval}, + analyze::{PrivateNameUsage, analyze, analyze_for_eval}, ast::{self}, - parse_script_for_eval, + parse_script, parse_script_for_eval, scope_tree::BindingKind, source::Source, }, runtime::{ - Context, EvalResult, Handle, HeapPtr, Value, + Context, EvalResult, Handle, HeapPtr, Realm, Value, abstract_operations::call_object, bytecode::{ function::Closure, generator::BytecodeProgramGenerator, instruction::EvalFlags, @@ -287,3 +287,33 @@ fn eval_declaration_instantiation(mut cx: Context, program: &ast::Program) -> Ev fn error_name_already_declared(cx: Context, name: Handle) -> EvalResult<()> { syntax_error(cx, &format!("identifier `{name}` has already been declared")) } + +/// Evaluate a script in the given realm. +pub fn evaluate_script( + mut cx: Context, + realm: Handle, + source: Rc, +) -> EvalResult> { + let pcx = ParseContext::new(source); + let parse_result = match parse_script(&pcx, cx.options.clone()) { + Ok(parse_result) => parse_result, + Err(error) => return syntax_parse_error(cx, &error), + }; + + let analyzed_result = match analyze(parse_result) { + Ok(analyzed_result) => analyzed_result, + // Return the first analysis error + Err(errors) => return syntax_parse_error(cx, &errors.errors[0]), + }; + + let bytecode_script = match BytecodeProgramGenerator::generate_from_parse_script_result( + cx, + &analyzed_result, + realm, + ) { + Ok(bytecode_script) => bytecode_script, + Err(error) => return syntax_error(cx, &error.to_string()), + }; + + cx.vm().execute_script(bytecode_script) +} diff --git a/src/js/runtime/intrinsics/error_constructor.rs b/src/js/runtime/intrinsics/error_constructor.rs index 44a31388..56198b93 100644 --- a/src/js/runtime/intrinsics/error_constructor.rs +++ b/src/js/runtime/intrinsics/error_constructor.rs @@ -91,6 +91,19 @@ impl ErrorObject { Ok(error) } + /// Return a generic error object with the given message. + pub fn new_with_message(mut cx: Context, message: String) -> EvalResult> { + let message_value = cx.alloc_string(&message)?.as_value(); + let error_object = + Self::new(cx, Intrinsic::ErrorPrototype, /* skip_current_frame */ false)?; + + error_object + .as_object() + .intrinsic_data_prop(cx, cx.names.message(), message_value)?; + + Ok(error_object) + } + fn initialize_stack_trace( cx: Context, mut error: Handle, diff --git a/src/js/runtime/intrinsics/global_object.rs b/src/js/runtime/intrinsics/global_object.rs index a46a88b2..7a0172ba 100644 --- a/src/js/runtime/intrinsics/global_object.rs +++ b/src/js/runtime/intrinsics/global_object.rs @@ -19,10 +19,12 @@ use crate::{ error::uri_error, eval::eval::perform_eval, function::get_argument, + gc_object::GcObject, intrinsics::{intrinsics::Intrinsic, rust_runtime::RuntimeFunction}, property_descriptor::PropertyDescriptor, string_parsing::{StringLexer, parse_signed_decimal_literal, skip_string_whitespace}, string_value::{FlatString, StringValue}, + test_262_object::Test262Object, to_string, type_utilities::{to_int32, to_number}, }, @@ -154,6 +156,15 @@ pub fn set_default_global_bindings(cx: Context, realm: Handle) -> EvalRes let console_object = ConsoleObject::new(cx, realm)?.into(); value_prop!(cx.names.console(), console_object, true, false, true); + // Optional, non-standard properties of global object + if cx.options.expose_gc { + GcObject::install(cx, realm)?; + } + + if cx.options.expose_test_262 { + Test262Object::install(cx, realm)?; + } + Ok(()) }) } diff --git a/src/js/runtime/intrinsics/rust_runtime.rs b/src/js/runtime/intrinsics/rust_runtime.rs index 525f13a6..4bf28a7b 100644 --- a/src/js/runtime/intrinsics/rust_runtime.rs +++ b/src/js/runtime/intrinsics/rust_runtime.rs @@ -100,7 +100,7 @@ pub type RuntimeFunctionId = u16; // Check that the number of runtime functions fits in the RustRuntimeFunctionId type. static_assert!(NUM_BUILTIN_RUST_RUNTIME_FUNCTIONS <= (1 << (RuntimeFunctionId::BITS as usize))); -type RuntimeFunctionPtr = fn( +pub type RuntimeFunctionPtr = fn( cx: Context, this_value: Handle, arguments: &[Handle], diff --git a/src/js/runtime/mod.rs b/src/js/runtime/mod.rs index f15f78dc..d3d8bcf6 100644 --- a/src/js/runtime/mod.rs +++ b/src/js/runtime/mod.rs @@ -53,6 +53,7 @@ mod string_parsing; pub mod string_value; mod tasks; pub mod test_262_object; +mod test_shell; mod type_utilities; mod value; diff --git a/src/js/runtime/realm.rs b/src/js/runtime/realm.rs index 7e91e442..7aad02cb 100644 --- a/src/js/runtime/realm.rs +++ b/src/js/runtime/realm.rs @@ -1,3 +1,5 @@ +use std::time::Instant; + use crate::{ field_offset, handle_scope, must_a, parser::scope_tree::REALM_SCOPE_SLOT_NAME, @@ -9,6 +11,7 @@ use crate::{ collections::{BsHashMap, BsHashMapField, InlineArray}, error::{err_assign_constant, syntax_error}, gc::{Handle, HeapItem, HeapPtr, HeapVisitor}, + gc_object::GcObject, global_names::has_restricted_global_property, heap_item_descriptor::{HeapItemDescriptor, HeapItemKind}, interned_strings::InternedStrings, @@ -21,6 +24,8 @@ use crate::{ scope::Scope, scope_names::{ScopeFlags, ScopeNameFlags, ScopeNames}, string_value::FlatString, + test_262_object::Test262Object, + test_shell::TestShell, }, set_uninit, }; @@ -39,6 +44,9 @@ pub struct Realm { /// An empty function in this realm. Used to form a dummy stack frame to set the current realm /// when the stack would otherwise be empty. empty_function: HeapPtr, + /// Timestamp when this realm was created. Times returned from `performance.now` are relative + /// to this timestamp. + time_origin: Instant, pub intrinsics: Intrinsics, } @@ -68,6 +76,7 @@ impl Realm { set_uninit!(realm.global_scopes, HeapPtr::uninit()); set_uninit!(realm.lexical_names, HeapPtr::uninit()); set_uninit!(realm.empty_function, HeapPtr::uninit()); + set_uninit!(realm.time_origin, Instant::now()); let realm = realm.to_handle(); @@ -108,6 +117,10 @@ impl Realm { self.empty_function } + pub fn time_origin(&self) -> Instant { + self.time_origin + } + pub fn get_intrinsic_ptr(&self, intrinsic: Intrinsic) -> HeapPtr { self.intrinsics.get_ptr(intrinsic) } @@ -289,6 +302,26 @@ impl Handle { Ok(()) } + + /// Install optional, non-standard properties of the global object based on the configuration + /// included in the Context's options. + pub fn install_optional_globals(&mut self, cx: Context) -> AllocResult<()> { + handle_scope!(cx, { + if cx.options.expose_gc { + GcObject::install(cx, *self)?; + } + + if cx.options.expose_test_262 { + Test262Object::install(cx, *self)?; + } + + if cx.options.expose_test_shell_compat { + TestShell::install(cx, *self)?; + } + + Ok(()) + }) + } } impl HeapItem for HeapPtr { diff --git a/src/js/runtime/test_262_object.rs b/src/js/runtime/test_262_object.rs index e703522e..4f4ea955 100644 --- a/src/js/runtime/test_262_object.rs +++ b/src/js/runtime/test_262_object.rs @@ -2,13 +2,13 @@ use std::rc::Rc; use crate::{ handle_scope, must_a, - parser::{ParseContext, analyze::analyze, parse_script, source::Source}, + parser::source::Source, runtime::{ Context, EvalResult, Handle, PropertyKey, Realm, Value, abstract_operations::set, alloc_error::AllocResult, - bytecode::generator::BytecodeProgramGenerator, - error::{syntax_error, syntax_parse_error, type_error}, + error::{syntax_parse_error, type_error}, + eval::eval::evaluate_script, function::get_argument, get, intrinsics::{ @@ -153,8 +153,8 @@ impl Test262Object { _: &[Handle], ) -> EvalResult> { // Create a new realm that also has the test262 object installed - let realm = Realm::new(cx)?; - Test262Object::install(cx, realm)?; + let mut realm = Realm::new(cx)?; + realm.install_optional_globals(cx)?; get(cx, realm.global_object(), test_262_key(cx)?) } @@ -181,34 +181,7 @@ impl Test262Object { Err(error) => return syntax_parse_error(cx, &error), }; - let pcx = ParseContext::new(source); - let parse_result = parse_script(&pcx, cx.options.clone()); - let parse_result = match parse_result { - Ok(parse_result) => parse_result, - Err(error) => return syntax_parse_error(cx, &error), - }; - - let analyzed_result = match analyze(parse_result) { - Ok(analyzed_result) => analyzed_result, - Err(errors) => { - // Choose an arbitrary syntax error to return - let error = &errors.errors[0]; - return syntax_parse_error(cx, error); - } - }; - - let realm = cx.current_realm(); - let gen_result = BytecodeProgramGenerator::generate_from_parse_script_result( - cx, - &analyzed_result, - realm, - ); - let bytecode_script = match gen_result { - Ok(bytecode_script) => bytecode_script, - Err(error) => return syntax_error(cx, &error.to_string()), - }; - - cx.vm().execute_script(bytecode_script) + evaluate_script(cx, cx.current_realm(), source) } pub fn detach_array_buffer( diff --git a/src/js/runtime/test_shell.rs b/src/js/runtime/test_shell.rs new file mode 100644 index 00000000..397987e1 --- /dev/null +++ b/src/js/runtime/test_shell.rs @@ -0,0 +1,272 @@ +use std::{ + path::{Path, PathBuf}, + rc::Rc, +}; + +use crate::{ + common::wtf_8::Wtf8String, + must_a, + parser::source::Source, + runtime::{ + Context, EvalResult, Handle, PropertyDescriptor, PropertyKey, Realm, Value, + abstract_operations::{create_data_property_or_throw, define_property_or_throw}, + alloc_error::AllocResult, + array_object::array_create_in_realm, + builtin_function::BuiltinFunction, + console::ConsoleObject, + error::syntax_parse_error, + eval::eval::evaluate_script, + function::get_argument, + gc_object::GcObject, + intrinsics::{ + array_buffer_constructor::ArrayBufferObject, error_constructor::ErrorObject, + intrinsics::Intrinsic, rust_runtime::RuntimeFunctionPtr, + }, + object_value::ObjectValue, + to_string, + }, +}; + +/// Global methods used in the shell for other engines, used for compatibility with some third-party +/// test suites. +pub struct TestShell; + +impl TestShell { + pub fn install(cx: Context, realm: Handle) -> AllocResult<()> { + // Install the global functions used in the compatibility test shell + Self::install_custom_function(cx, realm, "gc", GcObject::run, 0)?; + Self::install_custom_function(cx, realm, "load", Self::load, 1)?; + Self::install_custom_function(cx, realm, "loadString", Self::load_string, 1)?; + Self::install_custom_function(cx, realm, "print", ConsoleObject::log, 0)?; + Self::install_custom_function(cx, realm, "printErr", ConsoleObject::log, 0)?; + Self::install_custom_function(cx, realm, "read", Self::read, 1)?; + Self::install_custom_function(cx, realm, "readFile", Self::read, 1)?; + Self::install_custom_function(cx, realm, "runString", Self::run_string, 1)?; + + // Install the `performance.now` method + let performance_object = Self::install_performance_object(cx, realm)?; + Self::install_custom_function_on_object( + cx, + realm, + performance_object, + "now", + Self::performance_now, + 0, + )?; + + // Install the global `arguments` array + Self::install_script_arguments(cx, realm)?; + + Ok(()) + } + + fn install_custom_function( + cx: Context, + realm: Handle, + name: &'static str, + function_ptr: RuntimeFunctionPtr, + length: u32, + ) -> AllocResult<()> { + Self::install_custom_function_on_object( + cx, + realm, + realm.global_object(), + name, + function_ptr, + length, + ) + } + + fn install_custom_function_on_object( + mut cx: Context, + realm: Handle, + object: Handle, + name: &'static str, + function_ptr: RuntimeFunctionPtr, + length: u32, + ) -> AllocResult<()> { + let function_id = cx.rust_runtime_functions.register(function_ptr).unwrap(); + + let name_string = cx.alloc_static_string(name)?; + let name_key = PropertyKey::string_handle(cx, name_string)?; + let fuzzilli_function = + BuiltinFunction::create_custom(cx, function_id, length, name_key, realm, None)?; + + let desc = PropertyDescriptor::data(fuzzilli_function.as_value(), true, false, true); + must_a!(define_property_or_throw(cx, object, name_key, desc)); + + Ok(()) + } + + fn install_performance_object( + mut cx: Context, + realm: Handle, + ) -> AllocResult> { + let performance_object = + ObjectValue::new(cx, Some(realm.get_intrinsic(Intrinsic::ObjectPrototype)), true)?; + + let name_key = PropertyKey::string_handle(cx, cx.alloc_static_string("performance")?)?; + let desc = PropertyDescriptor::data(performance_object.as_value(), true, false, true); + must_a!(define_property_or_throw(cx, realm.global_object(), name_key, desc)); + + Ok(performance_object) + } + + /// Install the script arguments passed after `--` as a global `arguments` array. + fn install_script_arguments(mut cx: Context, realm: Handle) -> AllocResult<()> { + let array = must_a!(array_create_in_realm(cx, realm, 0, None)); + + for (index, arg) in cx.options.script_args.clone().iter().enumerate() { + let arg_value = must_a!(cx.alloc_string(arg)).as_value(); + let index_key = PropertyKey::from_u64(cx, index as u64)?.to_handle(cx); + must_a!(create_data_property_or_throw(cx, array.as_object(), index_key, arg_value)); + } + + let arguments_key = PropertyKey::string_handle(cx, cx.alloc_static_string("arguments")?)?; + realm + .global_object() + .intrinsic_data_prop(cx, arguments_key, array.as_value())?; + + Ok(()) + } + + /// Read a file at the given path and evaluate it as a script in the current realm. + fn load( + cx: Context, + _: Handle, + arguments: &[Handle], + ) -> EvalResult> { + let path_arg = get_argument(cx, arguments, 0); + let path = resolve_path_arg(cx, path_arg)?; + + let source = match Source::new_from_file(&path.to_string_lossy()) { + Ok(source) => Rc::new(source), + Err(error) => return syntax_parse_error(cx, &error), + }; + + evaluate_script(cx, cx.current_realm(), source) + } + + /// Evaluate a script in the current realm. + fn load_string( + cx: Context, + _: Handle, + arguments: &[Handle], + ) -> EvalResult> { + let script_arg = get_argument(cx, arguments, 0); + let script_string = to_string(cx, script_arg)?.to_wtf8_string()?; + let source = source_from_string(cx, script_string)?; + + evaluate_script(cx, cx.current_realm(), source) + } + + /// Read a file to either a string or an ArrayBuffer depending on the value of the second + /// argument. + fn read( + mut cx: Context, + _: Handle, + arguments: &[Handle], + ) -> EvalResult> { + let path_arg = get_argument(cx, arguments, 0); + let path = resolve_path_arg(cx, path_arg)?; + + let mode_arg = get_argument(cx, arguments, 1); + let is_binary = mode_arg.is_string() && mode_arg.as_string().flatten()?.eq_str("binary"); + + if is_binary { + let bytes = match std::fs::read(&path) { + Ok(bytes) => bytes, + Err(_) => return read_file_error(cx, &path), + }; + + let buffer_constructor = cx.get_intrinsic(Intrinsic::ArrayBufferConstructor); + let mut array_buffer = ArrayBufferObject::new( + cx, + buffer_constructor, + bytes.len(), + /* max_byte_length */ None, + /* data */ None, + )?; + array_buffer.data_mut().copy_from_slice(&bytes); + + Ok(array_buffer.as_value()) + } else { + let string = match std::fs::read_to_string(&path) { + Ok(string) => string, + Err(_) => return read_file_error(cx, &path), + }; + + Ok(cx.alloc_string(&string)?.as_value()) + } + } + + fn performance_now( + cx: Context, + _: Handle, + _: &[Handle], + ) -> EvalResult> { + let time_origin = cx.current_realm().time_origin(); + let duration_millis = time_origin.elapsed().as_secs_f64() * 1000.0; + + Ok(cx.number(duration_millis)) + } + + /// Create a new realm, evaluate the script in it, and return the new realm's global object. + fn run_string( + cx: Context, + _: Handle, + arguments: &[Handle], + ) -> EvalResult> { + let script_arg = get_argument(cx, arguments, 0); + let script_string = to_string(cx, script_arg)?.to_wtf8_string()?; + let source = source_from_string(cx, script_string)?; + + let mut new_realm = Realm::new(cx)?; + new_realm.install_optional_globals(cx)?; + + evaluate_script(cx, new_realm, source)?; + + Ok(new_realm.global_object().as_value()) + } +} + +/// Resolve a path argument relative to the directory of the currently executing source +/// file. Handle both absolute and relative paths. +fn resolve_path_arg(mut cx: Context, path_arg: Handle) -> EvalResult { + let path_string = to_string(cx, path_arg)?.flatten()?.to_string(); + let path = Path::new(&path_string); + + if path.is_absolute() { + return Ok(path.to_path_buf()); + } + + let Some(current_source_file) = cx.vm().current_source_file() else { + return Ok(path.to_path_buf()); + }; + + let current_source_file_path = current_source_file.path().to_string(); + let Some(current_source_file_dir) = Path::new(¤t_source_file_path).parent() else { + return Ok(path.to_path_buf()); + }; + + Ok(current_source_file_dir.join(path)) +} + +fn source_from_string(mut cx: Context, string: Wtf8String) -> EvalResult> { + // Use the file path of the active source file + let file_path = cx + .vm() + .current_source_file() + .map_or_else(|| "".to_string(), |source| source.path().to_string()); + + match Source::new_for_string(&file_path, string) { + Ok(source) => Ok(Rc::new(source)), + Err(err) => syntax_parse_error(cx, &err), + } +} + +fn read_file_error(cx: Context, path: &Path) -> EvalResult> { + let error_object = + ErrorObject::new_with_message(cx, format!("failed to read {}", path.to_string_lossy()))?; + Ok(error_object.as_value()) +} diff --git a/src/main.rs b/src/main.rs index 5fe4704a..4f24382d 100644 --- a/src/main.rs +++ b/src/main.rs @@ -7,10 +7,7 @@ use brimstone_core::{ terminal::stderr_should_use_colors, }, parser::source::Source, - runtime::{ - BsResult, Context, ContextBuilder, alloc_error::AllocResult, gc_object::GcObject, - test_262_object::Test262Object, - }, + runtime::{BsResult, Context, ContextBuilder, alloc_error::AllocResult}, }; use clap::Parser; @@ -25,13 +22,7 @@ fn create_context(args: &Args) -> AllocResult { let options = parse_options(args); let cx = ContextBuilder::new().set_options(options).build()?; - if args.expose_gc { - GcObject::install(cx, cx.initial_realm())?; - } - - if args.expose_test_262 { - Test262Object::install(cx, cx.initial_realm())?; - } + cx.initial_realm().install_optional_globals(cx)?; #[cfg(feature = "gc_stress_test")] { diff --git a/tests/fuzz/src/main.rs b/tests/fuzz/src/main.rs index 0f2f9158..480d938b 100644 --- a/tests/fuzz/src/main.rs +++ b/tests/fuzz/src/main.rs @@ -9,14 +9,13 @@ use std::{ }; use brimstone_core::{ - common::wtf_8::Wtf8String, + common::{options::OptionsBuilder, wtf_8::Wtf8String}, handle_scope, must_a, parser::source::Source, runtime::{ Context, ContextBuilder, EvalResult, Handle, PropertyDescriptor, PropertyKey, Value, abstract_operations::define_property_or_throw, alloc_error::AllocResult, builtin_function::BuiltinFunction, error::type_error, function::get_argument, - gc_object::GcObject, }, }; @@ -65,9 +64,11 @@ fn main() { // Set up context for test let completion_or_panic = panic::catch_unwind(|| { - let cx = ContextBuilder::new().build()?; + let options = Rc::new(OptionsBuilder::new().expose_gc(true).build().unwrap()); + let cx = ContextBuilder::new().set_options(options).build()?; + + cx.initial_realm().install_optional_globals(cx); install_fuzzilli_function(cx)?; - GcObject::install(cx, cx.initial_realm())?; // Execute test case let source = Rc::new(Source::new_for_string("", test_wtf8_string).unwrap()); diff --git a/tests/harness/src/runner.rs b/tests/harness/src/runner.rs index d2947cec..dfc54477 100644 --- a/tests/harness/src/runner.rs +++ b/tests/harness/src/runner.rs @@ -231,6 +231,7 @@ fn run_single_test( // Set up options for test let options = OptionsBuilder::new() .annex_b(test.is_annex_b) + .expose_test_262(true) .min_heap_size(HEAP_SIZE) .build() .unwrap(); @@ -242,8 +243,7 @@ fn run_single_test( .unwrap(); let options = cx.options.clone(); - // Each realm has access to the test262 object - Test262Object::install(cx, cx.initial_realm()).unwrap(); + cx.initial_realm().install_optional_globals(cx).unwrap(); // Wrap in catch_unwind so that we can clean up the context in the event of a panic let panic_result = panic::catch_unwind(AssertUnwindSafe(|| {