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 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
5 changes: 3 additions & 2 deletions nova_cli/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ edition = "2021"

[dependencies]
clap = { version = "4.3", features = ["derive"] }
oxc_parser = "0.0.6"
oxc_ast = "0.0.6"
oxc_parser = "0.1.0"
oxc_ast = "0.1.0"
oxc_span = "0.1.0"
nova_vm = { path = "../nova_vm" }
21 changes: 10 additions & 11 deletions nova_cli/src/main.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
use clap::{Parser as ClapParser, Subcommand};
use nova_vm::{heap::Heap, VM};
use oxc_ast::SourceType;
use oxc_parser::Parser;
use oxc_span::SourceType;

/// A JavaScript engine
#[derive(Debug, ClapParser)] // requires `derive` feature
Expand Down Expand Up @@ -47,16 +46,16 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
let parser = Parser::new(&allocator, &file, source_type.with_typescript(false));
let result = parser.parse();

let mut vm = VM {
source: &file,
pc: 0,
instructions: Vec::new(),
heap: Heap::new(),
};
// let mut vm = VM {
// source: &file,
// pc: 0,
// instructions: Vec::new(),
// heap: Heap::new(),
// };

vm.load_program(result.program);
println!("{:?}", vm.instructions);
vm.interpret();
// vm.load_program(result.program);
// println!("{:?}", vm.instructions);
// vm.interpret();
}
}

Expand Down
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.1.0"
oxc_span = "0.1.0"
oxc_ast = "0.1.0"
oxc_allocator = "0.1.0"
oxc_diagnostics = "0.1.0"
num-bigint-dig = "0.8"
small_vec = "0.1"
13 changes: 13 additions & 0 deletions nova_vm/src/builtins.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
mod array;
mod builtin_function;
mod ecmascript_function;
mod number;
pub mod ordinary;

pub use array::ArrayConstructor;
pub use builtin_function::{
create_builtin_function, todo_builtin, ArgumentsList, Behaviour, Builtin, BuiltinFunctionArgs,
ConstructorFn, RegularFn as JsFunction, RegularFn,
};
pub use ecmascript_function::ECMAScriptFunction;
pub use number::NumberConstructor;
31 changes: 31 additions & 0 deletions nova_vm/src/builtins/array.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
use super::{
builtin_function::define_builtin_function, create_builtin_function, ArgumentsList, Behaviour,
Builtin, BuiltinFunctionArgs,
};
use crate::{
execution::{Agent, JsResult, Realm},
types::{Object, Value},
};

pub struct ArrayConstructor;

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

Ok(object.into_object())
}
}

impl ArrayConstructor {
fn behaviour(
agent: &mut Agent,
this_value: Value,
arguments: ArgumentsList,
) -> JsResult<Value> {
todo!();
}
}
140 changes: 140 additions & 0 deletions nova_vm/src/builtins/builtin_function.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
use crate::{
execution::{Agent, Intrinsics, JsResult, Realm},
heap::{BuiltinObjectIndexes, CreateHeapData, FunctionHeapData, HeapBits, ObjectHeapData},
types::{Function, Object, PropertyDescriptor, PropertyKey, Value},
};

#[derive(Debug)]
pub struct ArgumentsList<'a>(&'a [Value]);

impl ArgumentsList<'_> {
#[inline]
pub fn get(&self, index: usize) -> Value {
*self.0.get(index).unwrap_or(&Value::Undefined)
}
}

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

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

pub trait Builtin {
fn create<'a>(realm: &'a mut Realm<'a, 'a>) -> JsResult<Object>;
}

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

impl<'a, 'ctx: 'a, 'host: 'ctx> BuiltinFunctionArgs<'a, 'ctx, 'host> {
pub fn new(length: u32, name: &'a 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<'a, 'b: 'a>(
behaviour: Behaviour,
args: BuiltinFunctionArgs<'a, 'b, 'b>,
) -> Function {
// 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(Intrinsics::function_prototype);

// TODO: Steps 3-4
// 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.
let object = realm.heap.create(ObjectHeapData {
bits: HeapBits::new(),
// 6. Set func.[[Prototype]] to prototype.
prototype: PropertyDescriptor {
value: Some(prototype.into_value()),
..Default::default()
},
// 7. Set func.[[Extensible]] to true.
extensible: true,
// 8. Set func.[[Realm]] to realm.
// NOTE: Heap data is implicitly attached to the Realm so I don't think
// this matters.
entries: Vec::new(),
});

let initial_name = realm.heap.create(args.name).into_value();
let func = realm.heap.create(FunctionHeapData {
bits: HeapBits::new(),
object: None,
behaviour,
// TODO: 9. Set func.[[InitialName]] to null.
// NOTE: This is non-standard.
initial_name,
// 10. Perform SetFunctionLength(func, length).
length: args.length as i64,
});

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

// 13. Return func.
func
}

pub fn define_builtin_function<'a, 'b>(
object: Object,
name: &'a str,
behaviour: RegularFn,
length: u32,
realm: &'a mut Realm<'b, 'b>,
) -> JsResult<()> {
let function = create_builtin_function(
Behaviour::Regular(behaviour),
BuiltinFunctionArgs::new(length, name, realm),
);

Ok(())
}

pub fn define_builtin_property(
object: Object,
name: &'static str,
descriptor: PropertyDescriptor,
) -> JsResult<()> {
Ok(())
}

pub fn todo_builtin(agent: &mut Agent, _: Value, _: ArgumentsList) -> JsResult<Value> {
agent.throw_exception(
crate::execution::agent::ExceptionType::SyntaxError,
"TODO: Builtin not implemented.",
);
Err(())
}
89 changes: 89 additions & 0 deletions nova_vm/src/builtins/ecmascript_function.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
use std::{cell::RefCell, rc::Rc};

use oxc_ast::ast::{FormalParameters, FunctionBody};

use crate::{
execution::{Agent, Environment, JsResult, PrivateEnvironment, Realm, ScriptOrModule},
types::{Number, Object, PropertyDescriptor, PropertyKey, Value},
};

#[derive(Debug, Clone, Copy)]
pub enum ConstructorKind {
Base,
Derived,
}

#[derive(Debug, Clone, Copy)]
pub enum ThisMode {
Lexical,
Strict,
Global,
}

/// 10.2 ECMAScript Function Objects
/// https://tc39.es/ecma262/#sec-ecmascript-function-objects
#[derive(Debug, Clone)]
pub struct ECMAScriptFunction<'ctx, 'host> {
/// [[Environment]]
pub environment: Environment,

/// [[PrivateEnvironment]]
pub private_environment: Option<Rc<RefCell<PrivateEnvironment>>>,

/// [[FormalParameters]]
pub formal_parameters: &'host FormalParameters<'host>,

/// [[ECMAScriptCode]]
pub ecmascript_code: &'host FunctionBody<'host>,

/// [[ConstructorKind]]
pub constructor_kind: ConstructorKind,

/// [[Realm]]
pub realm: Rc<RefCell<Realm<'ctx, 'host>>>,

/// [[ScriptOrModule]]
pub script_or_module: ScriptOrModule<'ctx, 'host>,

/// [[ThisMode]]
pub this_mode: ThisMode,

/// [[Strict]]
pub strict: bool,

/// [[HomeObject]]
pub home_object: Option<Object>,

/// [[SourceText]]
pub source_text: &'host str,

// TODO: [[Fields]], [[PrivateMethods]], [[ClassFieldInitializerName]]
/// [[IsClassConstructor]]
pub is_class_constructor: bool,
}

impl Object {
/// 10.2.10 SetFunctionLength ( F, length )
/// https://tc39.es/ecma262/#sec-setfunctionlength
pub fn set_function_length(self, agent: &mut Agent, length: i64) -> JsResult<()> {
let function = self;

// TODO: 1. Assert: F is an extensible object that does not have a "length" own property.

// 2. Perform ! DefinePropertyOrThrow(F, "length", PropertyDescriptor { [[Value]]: 𝔽(length), [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: true }).
function.define_property_or_throw(
agent,
PropertyKey::try_from(Value::try_from("length").unwrap()).unwrap(),
PropertyDescriptor {
value: Some(Number::try_from(length).unwrap().into_value()),
writable: Some(false),
enumerable: Some(false),
configurable: Some(true),
..Default::default()
},
)?;

// 3. Return unused.
Ok(())
}
}
Loading
Loading