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 rework (rebase) #28

Merged
merged 32 commits into from
Oct 15, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
c339f75
feat: engine setup
sno2 Jul 24, 2023
3da9393
fix small string
sno2 Jul 24, 2023
1715743
fix small string
sno2 Jul 24, 2023
fcf0321
fix small integer
sno2 Jul 24, 2023
86565b7
more work
sno2 Jul 25, 2023
5859a2f
little empty string bonus
sno2 Jul 25, 2023
6158f72
bank error in your favor. inherit $500
sno2 Jul 25, 2023
a5d47e7
press CTRL+S to save
sno2 Jul 25, 2023
ebe57e8
get builtin flow working mid
sno2 Jul 26, 2023
7087a03
pattern-based equality
sno2 Jul 26, 2023
a02ac1e
fix invalid truncating stuff
sno2 Jul 26, 2023
add3819
checkpoint
sno2 Jul 27, 2023
c63190f
some work
sno2 Jul 27, 2023
6afaed3
more work towards builtins
sno2 Jul 29, 2023
ff01455
make intrinsics const functions
sno2 Jul 29, 2023
5c994b3
remove double Option
sno2 Jul 29, 2023
0b557e7
adjust for larger small bigint range
sno2 Jul 29, 2023
01954ee
update oxc deps
sno2 Jul 29, 2023
87a00b6
update to stable and fix cli
sno2 Jul 29, 2023
cf36a6e
Fix some post-rebase issues
aapoalas Sep 10, 2023
115e75a
Fix build after rebase
aapoalas Sep 17, 2023
08a81ba
Sub-enums, stuff
aapoalas Sep 24, 2023
ce879b2
Work on Environments
aapoalas Oct 2, 2023
843be8e
Prepare for multi-realm heap by implementing Intrinsics struct
aapoalas Oct 11, 2023
173c9b7
Remove vestigal src/value.rs
aapoalas Oct 11, 2023
de84f0c
get_object_index helper for Object sub-enum
aapoalas Oct 11, 2023
c1d263c
Move Heap to Agent
aapoalas Oct 12, 2023
c99a42d
Begin vector-based environments work
aapoalas Oct 14, 2023
24204e2
Environments in Heap
aapoalas Oct 15, 2023
d57cc47
Remove some unsafe from bytecode
aapoalas Oct 15, 2023
9b8f3f6
Rename Environment enum to EnvironmentIndex
aapoalas Oct 15, 2023
225e4fb
Remove meaningless clone
aapoalas Oct 15, 2023
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;
30 changes: 30 additions & 0 deletions nova_vm/src/builtins/array.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
use super::{create_builtin_function, ArgumentsList, Behaviour, Builtin, BuiltinFunctionArgs};
use crate::{
execution::{Agent, JsResult},
types::{Object, Value},
};

pub struct ArrayConstructor;

impl Builtin for ArrayConstructor {
fn create<'a>(agent: &'a mut Agent<'a, 'a>) -> JsResult<Object> {
let realm = agent.current_realm_id();
let object = create_builtin_function(
agent,
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!();
}
}
137 changes: 137 additions & 0 deletions nova_vm/src/builtins/builtin_function.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
use crate::{
execution::{Agent, Intrinsics, JsResult, Realm, RealmIdentifier},
heap::CreateHeapData,
types::{Function, Object, PropertyDescriptor, Value},
Heap,
};

#[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>(agent: &'a mut Agent<'a, 'a>) -> JsResult<Object>;
}

#[derive(Debug, Default)]
pub struct BuiltinFunctionArgs<'a, 'ctx, 'host> {
pub length: u32,
pub name: &'a str,
pub realm: Option<RealmIdentifier<'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: RealmIdentifier<'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>(
agent: &mut Agent<'b, 'b>,
behaviour: Behaviour,
args: BuiltinFunctionArgs<'a, 'b, 'b>,
) -> Function {
// 1. If realm is not present, set realm to the current Realm Record.
let realm_id = args.realm.unwrap_or(agent.current_realm_id());

// 2. If prototype is not present, set prototype to realm.[[Intrinsics]].[[%Function.prototype%]].
let prototype = args.prototype.unwrap_or_else(|| {
agent
.get_realm(realm_id)
.intrinsics
.function_prototype()
.into()
});
let heap = &mut agent.heap;

// 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.
// 6. Set func.[[Prototype]] to prototype.
// 7. Set func.[[Extensible]] to true.
// 8. Set func.[[Realm]] to realm.
// NOTE: Heap data is implicitly attached to the Realm so I don't think
// this matters.
let object = heap.create_object_with_prototype(prototype);

// 9. Set func.[[InitialName]] to null.
// TODO: This is non-standard.
let initial_name = heap.create(args.name).into_value();
// 10. Perform SetFunctionLength(func, length).
let length = args.length as u8;
// TODO: Actually set behaviour somewhere
let func = heap.create_function(initial_name, length, false);

// 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.
Function(func)
}

pub fn define_builtin_function<'a, 'b>(
agent: &mut Agent<'b, 'b>,
object: Object,
name: &'a str,
behaviour: RegularFn,
length: u32,
realm: RealmIdentifier<'b, 'b>,
) -> JsResult<()> {
let function = create_builtin_function(
agent,
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(())
}
91 changes: 91 additions & 0 deletions nova_vm/src/builtins/ecmascript_function.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use std::{cell::RefCell, rc::Rc};

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

use crate::{
execution::{
Agent, EnvironmentIndex, JsResult, PrivateEnvironmentIndex, RealmIdentifier, 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: EnvironmentIndex,

/// [[PrivateEnvironment]]
pub private_environment: Option<PrivateEnvironmentIndex>,

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

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

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

/// [[Realm]]
pub realm: RealmIdentifier<'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