Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: engine setup #24

Closed
wants to merge 21 commits into from
Closed
Show file tree
Hide file tree
Changes from 2 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
9 changes: 7 additions & 2 deletions nova_vm/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,5 +8,10 @@ edition = "2021"
[dependencies]
gc = { version = "0.4", features = ["derive"] }
wtf8 = "0.1"
oxc_parser = "0.0.6"
oxc_ast = "0.0.6"
oxc_parser = "0.0.7"
oxc_span = "0.0.7"
oxc_ast = "0.0.7"
oxc_allocator = "0.0.7"
oxc_diagnostics = "0.0.7"
num-bigint-dig = "0.8"
small_vec = "0.1"
6 changes: 6 additions & 0 deletions nova_vm/src/builtins.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
mod array;
mod builtin_function;

pub use builtin_function::{
create_builtin_function, ArgumentsList, Behaviour, Builtin, BuiltinFunctionArgs,
};
25 changes: 25 additions & 0 deletions nova_vm/src/builtins/array.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use super::{create_builtin_function, ArgumentsList, Behaviour, Builtin, BuiltinFunctionArgs};
use crate::{
execution::{Agent, JsResult, Realm},
types::{Object, Value},
};

struct ArrayConstructor;

impl Builtin for ArrayConstructor {
fn create(realm: &mut Realm) -> Object {
let object = create_builtin_function(
&mut realm.agent.clone().borrow_mut(),
Behaviour::Regular(Self::behaviour),
BuiltinFunctionArgs::new(1, "Array", realm),
);

object
}
}

impl ArrayConstructor {
fn behaviour(agent: &mut Agent, value: Value, arguments: ArgumentsList) -> JsResult<Value> {
todo!();
}
}
96 changes: 96 additions & 0 deletions nova_vm/src/builtins/builtin_function.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
use crate::{
execution::{Agent, JsResult, Realm},
types::{Object, Value},
};

#[derive(Debug)]
pub struct ArgumentsList;

type RegularFn = fn(&mut Agent, Value, ArgumentsList) -> JsResult<Value>;
type ConstructorFn = fn(&mut Agent, Value, ArgumentsList, Option<Object>) -> JsResult<Value>;

#[derive(Debug)]
pub enum Behaviour {
Regular(RegularFn),
Constructor(ConstructorFn),
}

pub trait Builtin {
fn create(realm: &mut Realm) -> Object;
}

#[derive(Debug, Default)]
pub struct BuiltinFunctionArgs<'a, 'ctx, 'host> {
pub length: u32,
pub name: &'static str,
pub realm: Option<&'a mut Realm<'ctx, 'host>>,
pub prototype: Option<Object>,
pub prefix: Option<Object>,
}

impl<'a, 'ctx, 'host: 'ctx> BuiltinFunctionArgs<'a, 'ctx, 'host> {
pub fn new(length: u32, name: &'static str, realm: &'a mut Realm<'ctx, 'host>) -> Self {
Self {
length,
name,
realm: Some(realm),
..Default::default()
}
}
}

/// 10.3.3 CreateBuiltinFunction ( behaviour, length, name, additionalInternalSlotsList [ , realm [ , prototype [ , prefix ] ] ] )
/// https://tc39.es/ecma262/#sec-createbuiltinfunction
pub fn create_builtin_function<'ctx, 'host: 'ctx>(
agent: &mut Agent<'ctx, 'host>,
behaviour: Behaviour,
args: BuiltinFunctionArgs<'_, 'ctx, 'host>,
) -> Object {
// 1. If realm is not present, set realm to the current Realm Record.
let realm = args.realm.unwrap(); // TODO: load record

// 2. If prototype is not present, set prototype to realm.[[Intrinsics]].[[%Function.prototype%]].
let prototype = args
.prototype
.unwrap_or_else(|| realm.intrinsics.function_prototype());

// 3. Let internalSlotsList be a List containing the names of all the internal slots that 10.3
// requires for the built-in function object that is about to be created.
// 4. Append to internalSlotsList the elements of additionalInternalSlotsList.

// 5. Let func be a new built-in function object that, when called, performs the action
// described by behaviour using the provided arguments as the values of the corresponding
// parameters specified by behaviour. The new function object has internal slots whose names
// are the elements of internalSlotsList, and an [[InitialName]] internal slot.

// 10. Perform SetFunctionLength(func, length).

// 11. If prefix is not present, then
// a. Perform SetFunctionName(func, name).
// 12. Else,
// a. Perform SetFunctionName(func, name, prefix).

// 13. Return func.
todo!();
}

pub fn define_builtin_function<'ctx, 'host: 'ctx>(
object: Object,
name: &'static str,
behaviour: RegularFn,
length: u32,
realm: &'ctx mut Realm<'ctx, 'host>,
) {
let agent_mut = realm.agent.clone();
let mut agent = agent_mut.borrow_mut();

let function = create_builtin_function(
&mut agent,
Behaviour::Regular(behaviour),
BuiltinFunctionArgs::new(length, name, realm),
);

define_builtin_property(object, name, Value::from(function));
}

pub fn define_builtin_property(object: Object, name: &'static str, value: Value) {}
13 changes: 13 additions & 0 deletions nova_vm/src/execution.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
pub mod agent;
mod default_host_hooks;
mod environments;
mod execution_context;
mod realm;

pub use agent::{Agent, JsResult};
pub use environments::{
DeclarativeEnvironment, Environment, FunctionEnvironment, GlobalEnvironment, ObjectEnvironment,
PrivateEnvironment,
};
pub use execution_context::{ECMAScriptCode, ExecutionContext, ScriptOrModule};
pub use realm::Realm;
44 changes: 44 additions & 0 deletions nova_vm/src/execution/agent.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use super::{ExecutionContext, Realm};
use crate::{
types::{Object, Symbol, Value},
Heap,
};
use std::collections::HashMap;

#[derive(Debug, Default)]
pub struct Options {
pub disable_gc: bool,
pub print_ast: bool,
pub print_bytecode: bool,
}

pub type JsResult<T> = std::result::Result<T, ()>;

// #[derive(Debug)]
// pub struct PreAllocated;

#[derive(Debug)]
pub struct HostHooks {
pub host_ensure_can_compile_strings: fn(callee_realm: &mut Realm) -> JsResult<()>,
pub host_has_source_text_available: fn(func: Object) -> bool,
}

/// 9.7 Agents
/// https://tc39.es/ecma262/#sec-agents
#[derive(Debug)]
pub struct Agent<'ctx, 'host> {
pub heap: Heap,
pub options: Options,
// pre_allocated: PreAllocated,
pub exception: Option<Value>,
pub symbol_id: usize,
pub global_symbol_registry: HashMap<&'static str, Symbol>,
pub host_hooks: HostHooks,
pub execution_context_stack: Vec<ExecutionContext<'ctx, 'host>>,
}

impl Agent<'_, '_> {
pub fn current_realm(&self) -> &mut Realm {
todo!()
}
}
15 changes: 15 additions & 0 deletions nova_vm/src/execution/default_host_hooks.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
use super::{JsResult, Realm};
use crate::types::Object;

/// 19.2.1.2 HostEnsureCanCompileStrings ( calleeRealm )
/// https://tc39.es/ecma262/#sec-hostensurecancompilestrings
pub fn host_ensure_can_compile_strings(_: &mut Realm) -> JsResult<()> {
Ok(())
}

/// 20.2.5 HostHasSourceTextAvailable ( func )
/// https://tc39.es/ecma262/#sec-hosthassourcetextavailable
pub fn host_has_source_text_available(_: Object) -> bool {
// The default implementation of HostHasSourceTextAvailable is to return true.
return true;
}
25 changes: 25 additions & 0 deletions nova_vm/src/execution/environments.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
//! 9.1 Environment Records
//! https://tc39.es/ecma262/#sec-environment-records

pub mod declarative_environment;
pub mod function_environment;
pub mod global_environment;
pub mod object_environment;
pub mod private_environment;

pub use declarative_environment::DeclarativeEnvironment;
pub use function_environment::FunctionEnvironment;
pub use global_environment::GlobalEnvironment;
pub use object_environment::ObjectEnvironment;
pub use private_environment::PrivateEnvironment;
use std::{cell::RefCell, rc::Rc};

/// 9.1.1 The Environment Record Type Hierarchy
/// https://tc39.es/ecma262/#sec-the-environment-record-type-hierarchy
#[derive(Debug)]
pub enum Environment {
DeclarativeEnvironment(Rc<RefCell<DeclarativeEnvironment>>),
ObjectEnvironment(Rc<RefCell<ObjectEnvironment>>),
FunctionEnvironment(Rc<RefCell<FunctionEnvironment>>),
GlobalEnvironment(Rc<RefCell<GlobalEnvironment>>),
}
29 changes: 29 additions & 0 deletions nova_vm/src/execution/environments/declarative_environment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
use super::Environment;
use crate::types::Value;
use std::collections::HashMap;

/// 9.1.1.1 Declarative Environment Records
/// https://tc39.es/ecma262/#sec-declarative-environment-records
#[derive(Debug)]
pub struct DeclarativeEnvironment {
pub outer_env: Option<Environment>,
pub bindings: HashMap<&'static str, Binding>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

comment: I think the keys may benefit from being Value::Strings. After all, often a binding key will also find its way into a map key etc.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: I'd actually personally probably set these up as ParallelVecs with one Vec for keys, one Vec for values and then probably one for the rest. It's quite possible that we end up with better perf iterating through a vector of keys than hopping through a HashMap linked list. This especially so since the iteration will likely be aggressively unrolled and SIMD'd.

And the reason for separating keys from values is that usually we're looking for a single item by key: If we iterate key-value pairs then all but once the value bytes are entirely wasted and we waste cache line space. Only once do we actually hit the right key and we know where we hit it by our iteration index.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice suggestion. I had added the &'static str to avoid lifetimes while copying the spec entities but this would be better. Will work on implementing this.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ParallelVecs in question should be sorted, and all code should appropriately use logarithmic lookups (stdlib has us covered here with slice methods).

If we're to do away with HashMaps entirely for some parts of the code*, we should have a generic type built on ParallelVec for reuse.

* There are other hash map types and algorithms and allocator configurations? Maybe those would be more suitible? A HashMap in an arena allocator for example. I believe there are already libraries for this.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

}

#[derive(Debug)]
pub struct Binding {
pub value: Option<Value>,
pub strict: bool,
pub mutable: bool,
pub deletable: bool,
}

impl DeclarativeEnvironment {
/// 9.1.1.1.1 HasBinding ( N )
/// https://tc39.es/ecma262/#sec-declarative-environment-records-hasbinding-n
pub fn has_binding(self, name: &str) -> bool {
// 1. If envRec has a binding for N, return true.
// 2. Return false.
return self.bindings.contains_key(name);
}
}
37 changes: 37 additions & 0 deletions nova_vm/src/execution/environments/function_environment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use super::{DeclarativeEnvironment, Environment};
use crate::types::Value;
use std::{cell::RefCell, rc::Rc};

#[derive(Debug)]
pub enum ThisBindingStatus {
Lexical,
Initialized,
Uninitialized,
}

#[derive(Debug)]
struct ECMAScriptFunction;

/// 9.1.1.3 Function Environment Records
/// https://tc39.es/ecma262/#sec-function-environment-records
#[derive(Debug)]
pub struct FunctionEnvironment {
/// [[ThisValue]]
this_value: Value,

/// [[ThisBindingStatus]]
this_binding_status: ThisBindingStatus,

/// [[FunctionObject]]
function_object: ECMAScriptFunction,

/// [[NewTarget]]
new_target: Option<Value>,

/// [[OuterEnv]]
outer_env: Option<Environment>,

// NOTE: This is how we implement the spec's inheritance of function
// environments.
declarative_environment: Rc<RefCell<DeclarativeEnvironment>>,
}
23 changes: 23 additions & 0 deletions nova_vm/src/execution/environments/global_environment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
use super::{DeclarativeEnvironment, Environment, ObjectEnvironment};
use crate::types::Object;
use std::{cell::RefCell, collections::HashMap, rc::Rc};

/// 9.1.1.4 Global Environment Records
/// https://tc39.es/ecma262/#sec-global-environment-records
#[derive(Debug)]
pub struct GlobalEnvironment {
// [[ObjectRecord]]
object_record: Rc<RefCell<ObjectEnvironment>>,

/// [[GlobalThisValue]]
global_this_value: Object,

/// [[DeclarativeRecord]]
declarative_record: Rc<RefCell<DeclarativeEnvironment>>,

/// [[VarNames]]
var_names: HashMap<&'static str, ()>,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Use a vector.


/// [[OuterEnv]]
outer_env: Option<Environment>,
}
16 changes: 16 additions & 0 deletions nova_vm/src/execution/environments/object_environment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use super::Environment;
use crate::types::Object;

/// 9.1.1.2 Object Environment Records
/// https://tc39.es/ecma262/#sec-object-environment-records
#[derive(Debug)]
pub struct ObjectEnvironment {
/// [[BindingObject]]
binding_object: Object,

/// [[IsWithEnvironment]]
is_with_environment: bool,

/// [[OuterEnv]]
outer_env: Option<Environment>,
}
12 changes: 12 additions & 0 deletions nova_vm/src/execution/environments/private_environment.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
use std::{cell::RefCell, collections::HashMap, rc::Rc};

/// 9.2 PrivateEnvironment Records
/// https://tc39.es/ecma262/#sec-privateenvironment-records
#[derive(Debug)]
pub struct PrivateEnvironment {
/// [[OuterPrivateEnvironment]]
outer_private_environment: Option<Rc<RefCell<PrivateEnvironment>>>,

/// [[Names]]
names: HashMap<&'static str, ()>, // TODO: Implement private names
}
Loading
Loading