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 Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ regex = "1.11.1"
ryu-js = "1.0.2"
sonic-rs = "0.3.17"
unicode-normalization = "0.1.24"
usdt = { git = "https://github.com/oxidecomputer/usdt/", branch = "master" }
wtf8 = "0.1"

[workspace.metadata.dylint]
Expand Down
3 changes: 3 additions & 0 deletions nova_cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ use nova_vm::{
context::{Bindable, GcScope, NoGcScope},
rootable::Scopable,
},
register_probes,
};
use oxc_parser::Parser;
use oxc_semantic::{SemanticBuilder, SemanticBuilderReturn};
Expand Down Expand Up @@ -244,6 +245,8 @@ impl ModuleMap {
fn main() -> Result<(), Box<dyn std::error::Error>> {
let args = Cli::parse();

register_probes().unwrap();

match args.command {
Command::Parse { path } => {
let file = std::fs::read_to_string(&path)?;
Expand Down
2 changes: 2 additions & 0 deletions nova_vm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ ryu-js = { workspace = true }
small_string = { path = "../small_string", version = "0.2.0" }
sonic-rs = { workspace = true, optional = true }
unicode-normalization = { workspace = true }
usdt = { workspace = true }
wtf8 = { workspace = true }

[features]
Expand Down Expand Up @@ -93,3 +94,4 @@ proposal-atomics-microwait = ["atomics"]

[build-dependencies]
small_string = { path = "../small_string", version = "0.2.0" }
usdt = { workspace = true }
5 changes: 5 additions & 0 deletions nova_vm/src/ecmascript.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

//! [ECMAScript language](https://tc39.es/ecma262/)
//!
//! This module contains everything directly related to the ECMAScript language
//! specification and its implementation in the Nova engine.

pub(crate) mod abstract_operations;
pub(crate) mod builders;
pub mod builtins;
Expand Down
7 changes: 7 additions & 0 deletions nova_vm/src/ecmascript/abstract_operations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,13 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

//! ## [7 Abstract Operations](https://tc39.es/ecma262/#sec-abstract-operations)
//!
//! These operations are not a part of the ECMAScript language; they are
//! defined here solely to aid the specification of the semantics of the
//! ECMAScript language. Other, more specialized abstract operations are
//! defined throughout this specification.

pub(crate) mod keyed_group;
pub(crate) mod operations_on_iterator_objects;
pub(crate) mod operations_on_objects;
Expand Down
28 changes: 25 additions & 3 deletions nova_vm/src/ecmascript/builtins.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,33 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

//! # 10 Ordinary and Exotic Objects Behaviours
//! ## [10 Ordinary and Exotic Objects Behaviours](https://tc39.es/ecma262/#sec-ordinary-and-exotic-objects-behaviours)
//!
//! Currently only contains code related to subsections 10.2, 10.3 and 10.4.
//! Most things in JavaScript are objects: those are all defined underneath
//! this module.
//!
//! https://tc39.es/ecma262/#sec-ordinary-and-exotic-objects-behaviours
//! ### Ordinary and exotic objects
//!
//! The ECMAScript specification defines _ordinary objects_ by their _internal
//! methods_: an object which uses the ordinary internal methods is an ordinary
//! object, while objects that override some or all internal methods are
//! _exotic objects_. However, often the more important thing to an object is
//! its list of _internal slots_. Internal slots define what data an object can
//! hold: this data is not necessarily directly readable by JavaScript code but
//! it tends to dictate an object's usage very strongly.
//!
//! Thus according to the ECMAScript specification sense, some objects are
//! ordinary but have extra internal slots compared to plain objects created
//! using JavaScript object literals or other equivalent means. Such objects
//! include ArrayBuffers, Maps, and Sets. From a JavaScript programmer's point
//! of view it is evident that these objects are not "ordinary". Hence, it may
//! be beneficial to think of plain objects as "ordinary" and all other objects
//! as "exotic". This is very much the view that the Nova engine takes: in a
//! very real sense, all non-plain objects have both extra internal slots and
//! override the ordinary internal methods. This is because all such objects
//! in Nova have a special `[[BackingObject]]` slot and their internal methods
//! are modified such that they delegate "ordinary object business" to the
//! backing object if it exists.

pub(crate) mod arguments;
mod array;
Expand Down
4 changes: 1 addition & 3 deletions nova_vm/src/ecmascript/builtins/array.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at https://mozilla.org/MPL/2.0/.

//! ### 10.4.2 Array Exotic Objects
//!
//! https://tc39.es/ecma262/#sec-array-exotic-objects
//! ### [10.4.2 Array Exotic Objects](https://tc39.es/ecma262/#sec-array-exotic-objects)

pub(crate) mod abstract_operations;
mod data;
Expand Down
4 changes: 2 additions & 2 deletions nova_vm/src/ecmascript/builtins/array/data.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ use crate::{
/// mathematical value is strictly less than 2**32.
#[derive(Debug, Clone, Copy, Default)]
pub struct ArrayHeapData<'a> {
pub object_index: Option<OrdinaryObject<'a>>,
pub(crate) object_index: Option<OrdinaryObject<'a>>,
// TODO: Use enum { ElementsVector, SmallVec<[Value; 3]> }
// to get some inline benefit together with a 32 byte size
// for ArrayHeapData to fit two in one cache line.
pub elements: ElementsVector<'a>,
pub(crate) elements: ElementsVector<'a>,
}

// SAFETY: Property implemented as a lifetime transmute.
Expand Down
4 changes: 2 additions & 2 deletions nova_vm/src/ecmascript/builtins/bound_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -142,8 +142,8 @@ pub(crate) fn bound_function_create<'a>(
}

impl<'a> FunctionInternalProperties<'a> for BoundFunction<'a> {
fn get_name(self, agent: &Agent) -> String<'static> {
agent[self].name.unwrap_or(String::EMPTY_STRING)
fn get_name(self, agent: &Agent) -> &String<'a> {
agent[self].name.as_ref().unwrap_or(&String::EMPTY_STRING)
}

fn get_length(self, agent: &Agent) -> u8 {
Expand Down
18 changes: 16 additions & 2 deletions nova_vm/src/ecmascript/builtins/builtin_constructor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ use crate::{
CompactionLists, CreateHeapData, Heap, HeapMarkAndSweep, HeapSweepWeakReference,
ObjectEntry, ObjectEntryPropertyDescriptor, WorkQueues, indexes::BuiltinConstructorIndex,
},
ndt,
};

use super::ArgumentsList;
Expand Down Expand Up @@ -159,7 +160,7 @@ impl IndexMut<BuiltinConstructorFunction<'_>> for Vec<Option<BuiltinConstructorH
}

impl<'a> FunctionInternalProperties<'a> for BuiltinConstructorFunction<'a> {
fn get_name(self, _: &Agent) -> String<'static> {
fn get_name(self, _: &Agent) -> &String<'a> {
unreachable!();
}

Expand Down Expand Up @@ -216,11 +217,24 @@ impl<'a> FunctionInternalProperties<'a> for BuiltinConstructorFunction<'a> {
new_target: Function,
gc: GcScope<'gc, '_>,
) -> JsResult<'gc, Object<'gc>> {
let mut id = 0;
ndt::builtin_constructor_start!(|| {
id = create_id(agent, self);
let name = self.get_name(agent).to_string_lossy(agent);
(name, id)
});
// 1. Return ? BuiltinCallOrConstruct(F, uninitialized, argumentsList, newTarget).
builtin_call_or_construct(agent, self, arguments_list, new_target, gc)
let result = builtin_call_or_construct(agent, self, arguments_list, new_target, gc);
ndt::builtin_constructor_done!(|| id);
result
}
}

#[inline(never)]
fn create_id(agent: &Agent, f: BuiltinConstructorFunction) -> u64 {
((f.0.into_u32() as u64) << 32) | agent[f].source_text.start as u64
}

/// ### [10.3.3 BuiltinCallOrConstruct ( F, thisArgument, argumentsList, newTarget )](https://tc39.es/ecma262/#sec-builtincallorconstruct)
///
/// The abstract operation BuiltinCallOrConstruct takes arguments F (a built-in
Expand Down
57 changes: 50 additions & 7 deletions nova_vm/src/ecmascript/builtins/builtin_function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use core::{
marker::PhantomData,
ops::{Deref, Index, IndexMut},
};
use std::{hint::unreachable_unchecked, ptr::NonNull};
use std::{borrow::Cow, hint::unreachable_unchecked, ptr::NonNull};

use crate::{
ecmascript::{
Expand All @@ -26,6 +26,7 @@ use crate::{
IntrinsicConstructorIndexes, IntrinsicFunctionIndexes, ObjectEntry,
ObjectEntryPropertyDescriptor, WorkQueues, indexes::BuiltinFunctionIndex,
},
ndt,
};

#[derive(Default)]
Expand Down Expand Up @@ -504,6 +505,17 @@ impl<'a> From<BuiltinFunction<'a>> for Function<'a> {
}
}

impl<'a> TryFrom<Function<'a>> for BuiltinFunction<'a> {
type Error = ();

fn try_from(value: Function<'a>) -> Result<Self, Self::Error> {
match value {
Function::BuiltinFunction(f) => Ok(f),
_ => Err(()),
}
}
}

impl Index<BuiltinFunction<'_>> for Agent {
type Output = BuiltinFunctionHeapData<'static>;

Expand Down Expand Up @@ -539,8 +551,11 @@ impl IndexMut<BuiltinFunction<'_>> for Vec<Option<BuiltinFunctionHeapData<'stati
}

impl<'a> FunctionInternalProperties<'a> for BuiltinFunction<'a> {
fn get_name(self, agent: &Agent) -> String<'static> {
agent[self].initial_name.unwrap_or(String::EMPTY_STRING)
fn get_name(self, agent: &Agent) -> &String<'a> {
agent[self]
.initial_name
.as_ref()
.unwrap_or(&String::EMPTY_STRING)
}

fn get_length(self, agent: &Agent) -> u8 {
Expand Down Expand Up @@ -579,8 +594,17 @@ impl<'a> FunctionInternalProperties<'a> for BuiltinFunction<'a> {
arguments_list: ArgumentsList,
gc: GcScope<'gc, '_>,
) -> JsResult<'gc, Value<'gc>> {
let mut id = 0;
ndt::builtin_call_start!(|| {
let args = create_name_and_id(agent, self);
id = args.1;
args
});
// 1. Return ? BuiltinCallOrConstruct(F, thisArgument, argumentsList, undefined).
builtin_call_or_construct(agent, self, Some(this_argument), arguments_list, None, gc)
let result =
builtin_call_or_construct(agent, self, Some(this_argument), arguments_list, None, gc);
ndt::builtin_call_done!(|| id);
result
}

/// ### [10.3.2 \[\[Construct\]\] ( argumentsList, newTarget )](https://tc39.es/ecma262/#sec-built-in-function-objects-construct-argumentslist-newtarget)
Expand All @@ -596,20 +620,39 @@ impl<'a> FunctionInternalProperties<'a> for BuiltinFunction<'a> {
new_target: Function,
gc: GcScope<'gc, '_>,
) -> JsResult<'gc, Object<'gc>> {
let mut id = 0;
ndt::builtin_constructor_start!(|| {
let args = create_name_and_id(agent, self);
id = args.1;
args
});
// 1. Return ? BuiltinCallOrConstruct(F, uninitialized, argumentsList, newTarget).
builtin_call_or_construct(agent, self, None, arguments_list, Some(new_target), gc)
.map(|result| result.try_into().unwrap())
let result =
builtin_call_or_construct(agent, self, None, arguments_list, Some(new_target), gc)
.map(|result| result.try_into().unwrap());
ndt::builtin_constructor_done!(|| id);
result
}
}

#[inline(never)]
fn create_name_and_id<'a>(agent: &'a Agent, f: BuiltinFunction<'a>) -> (Cow<'a, str>, u64) {
let id = match agent[f].behaviour {
Behaviour::Regular(f) => f as usize as u64,
Behaviour::Constructor(f) => f as usize as u64,
};
let name = f.get_name(agent).to_string_lossy(agent);
(name, id)
}

/// ### [10.3.3 BuiltinCallOrConstruct ( F, thisArgument, argumentsList, newTarget )](https://tc39.es/ecma262/#sec-builtincallorconstruct)
///
/// The abstract operation BuiltinCallOrConstruct takes arguments F (a built-in
/// function object), thisArgument (an ECMAScript language value or
/// uninitialized), argumentsList (a List of ECMAScript language values), and
/// newTarget (a constructor or undefined) and returns either a normal
/// completion containing an ECMAScript language value or a throw completion.
pub(crate) fn builtin_call_or_construct<'gc>(
fn builtin_call_or_construct<'gc>(
agent: &mut Agent,
f: BuiltinFunction,
this_argument: Option<Value>,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@ impl<'a> From<BuiltinPromiseFinallyFunction<'a>> for Value<'a> {
}

impl<'a> FunctionInternalProperties<'a> for BuiltinPromiseFinallyFunction<'a> {
fn get_name(self, _: &Agent) -> String<'static> {
String::EMPTY_STRING
fn get_name(self, _: &Agent) -> &String<'a> {
&String::EMPTY_STRING
}

fn get_length(self, agent: &Agent) -> u8 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -92,8 +92,8 @@ impl<'a> From<BuiltinPromiseResolvingFunction<'a>> for Value<'a> {
}

impl<'a> FunctionInternalProperties<'a> for BuiltinPromiseResolvingFunction<'a> {
fn get_name(self, _: &Agent) -> String<'static> {
String::EMPTY_STRING
fn get_name(self, _: &Agent) -> &String<'a> {
&String::EMPTY_STRING
}

fn get_length(self, _: &Agent) -> u8 {
Expand Down
Loading
Loading