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
48 changes: 48 additions & 0 deletions src/js/common/options.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)]
Expand Down Expand Up @@ -76,6 +80,10 @@ pub struct Args {

#[arg(required = true)]
pub files: Vec<String>,

/// Args after an optional `--`
#[arg(last = true, hide = true)]
pub script_args: Vec<String>,
}

/// Options passed throughout the program.
Expand Down Expand Up @@ -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<String>,

/// Create the heap from this SerializedHeap if set, otherwise create heap from scratch.
pub serialized_heap: Option<&'static SerializedHeap<'static>>,
}
Expand Down Expand Up @@ -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(),
})
}
Expand All @@ -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.
Expand Down Expand Up @@ -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<String>) -> Self {
self.0.script_args = script_args;
self
}

pub fn serialized_heap(
mut self,
serialized_heap: Option<&'static SerializedHeap<'static>>,
Expand Down
36 changes: 33 additions & 3 deletions src/js/runtime/eval/eval.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -287,3 +287,33 @@ fn eval_declaration_instantiation(mut cx: Context, program: &ast::Program) -> Ev
fn error_name_already_declared(cx: Context, name: Handle<FlatString>) -> 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<Realm>,
source: Rc<Source>,
) -> EvalResult<Handle<Value>> {
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)
}
13 changes: 13 additions & 0 deletions src/js/runtime/intrinsics/error_constructor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Handle<ErrorObject>> {
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<ErrorObject>,
Expand Down
11 changes: 11 additions & 0 deletions src/js/runtime/intrinsics/global_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
},
Expand Down Expand Up @@ -154,6 +156,15 @@ pub fn set_default_global_bindings(cx: Context, realm: Handle<Realm>) -> 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(())
})
}
Expand Down
2 changes: 1 addition & 1 deletion src/js/runtime/intrinsics/rust_runtime.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Value>,
arguments: &[Handle<Value>],
Expand Down
1 change: 1 addition & 0 deletions src/js/runtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down
33 changes: 33 additions & 0 deletions src/js/runtime/realm.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
use std::time::Instant;

use crate::{
field_offset, handle_scope, must_a,
parser::scope_tree::REALM_SCOPE_SLOT_NAME,
Expand All @@ -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,
Expand All @@ -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,
};
Expand All @@ -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<Closure>,
/// Timestamp when this realm was created. Times returned from `performance.now` are relative
/// to this timestamp.
time_origin: Instant,
pub intrinsics: Intrinsics,
}

Expand Down Expand Up @@ -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();

Expand Down Expand Up @@ -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<ObjectValue> {
self.intrinsics.get_ptr(intrinsic)
}
Expand Down Expand Up @@ -289,6 +302,26 @@ impl Handle<Realm> {

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<Realm> {
Expand Down
39 changes: 6 additions & 33 deletions src/js/runtime/test_262_object.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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::{
Expand Down Expand Up @@ -153,8 +153,8 @@ impl Test262Object {
_: &[Handle<Value>],
) -> EvalResult<Handle<Value>> {
// 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)?)
}
Expand All @@ -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(
Expand Down
Loading
Loading