Skip to content

Commit

Permalink
replacing ExecutorBuilder with Realm (boa-dev#126)
Browse files Browse the repository at this point in the history
* replacing ExecutorBuilder with Realm

* fixing global_env

* adding benchmark for realm

* instrinsics was being called twice

* update changelog
  • Loading branch information
jasonwilliams authored Oct 2, 2019
1 parent 48819b6 commit 1c3fea4
Show file tree
Hide file tree
Showing 12 changed files with 223 additions and 103 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@ Features:
Enables Boa to run within the Test 262 framework.
This will help us see what is implemented or not within the spec

# Next

Feature enhancements:

- [FEATURE #119](https://github.com/jasonwilliams/boa/issues/119):
Introduce realm struct to hold realm context and global object

# 0.4.0 (2019-09-25)

v0.4.0 brings quite a big release. The biggest feature to land is the support of regular expressions.
Expand Down
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ harness = false
name = "fib"
harness = false

[[bench]]
name = "exec"
harness = false

[[bin]]
name = "boa"
path = "src/bin/bin.rs"
Expand Down
12 changes: 12 additions & 0 deletions benches/exec.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#[macro_use]
extern crate criterion;

use boa::realm::Realm;
use criterion::Criterion;

fn create_realm(c: &mut Criterion) {
c.bench_function("Create Realm", move |b| b.iter(|| Realm::create()));
}

criterion_group!(benches, create_realm);
criterion_main!(benches);
5 changes: 3 additions & 2 deletions src/bin/shell.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
clippy::module_name_repetitions
)]

use boa::realm::Realm;
use boa::{exec::Executor, forward_val};
use std::{fs::read_to_string, path::PathBuf};
use structopt::StructOpt;
Expand All @@ -35,8 +36,8 @@ pub fn main() -> Result<(), std::io::Error> {
let args = Opt::from_args();

let buffer = read_to_string(args.file)?;

let mut engine = Executor::new();
let realm = Realm::create();
let mut engine = Executor::new(realm);

match forward_val(&mut engine, &buffer) {
Ok(v) => print!("{}", v.to_string()),
Expand Down
142 changes: 59 additions & 83 deletions src/lib/exec.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
use crate::{
environment::lexical_environment::{new_function_environment, LexicalEnvironment},
environment::lexical_environment::new_function_environment,
js::{
array, boolean, console, function,
function::{create_unmapped_arguments_object, Function, RegularFunction},
json, math, object,
object::{ObjectKind, INSTANCE_PROTOTYPE, PROTOTYPE},
regexp, string,
value::{from_value, to_value, ResultValue, Value, ValueData},
},
realm::Realm,
syntax::ast::{
constant::Const,
expr::{Expr, ExprDef},
Expand All @@ -23,26 +21,17 @@ use std::{
/// An execution engine
pub trait Executor {
/// Make a new execution engine
fn new() -> Self;
fn new(realm: Realm) -> Self;
/// Run an expression
fn run(&mut self, expr: &Expr) -> ResultValue;
}

/// A Javascript intepreter
#[derive(Debug)]
pub struct Interpreter {
/// An object representing the global object
environment: LexicalEnvironment,
is_return: bool,
}

/// Builder for the [`Interpreter`]
///
/// [`Interpreter`]: struct.Interpreter.html
#[derive(Debug)]
pub struct InterpreterBuilder {
/// The global object
global: Value,
/// realm holds both the global object and the environment
realm: Realm,
}

fn exec_assign_op(op: &AssignOp, v_a: ValueData, v_b: ValueData) -> Value {
Expand All @@ -61,8 +50,11 @@ fn exec_assign_op(op: &AssignOp, v_a: ValueData, v_b: ValueData) -> Value {
}

impl Executor for Interpreter {
fn new() -> Self {
InterpreterBuilder::new().build()
fn new(realm: Realm) -> Self {
Interpreter {
realm,
is_return: false,
}
}

#[allow(clippy::match_same_arms)]
Expand Down Expand Up @@ -94,7 +86,7 @@ impl Executor for Interpreter {
Ok(obj)
}
ExprDef::Local(ref name) => {
let val = self.environment.get_binding_value(name);
let val = self.realm.environment.get_binding_value(name);
Ok(val)
}
ExprDef::GetConstField(ref obj, ref field) => {
Expand Down Expand Up @@ -123,10 +115,7 @@ impl Executor for Interpreter {
obj.borrow().get_field(&field.borrow().to_string()),
)
}
_ => (
self.environment.get_global_object().unwrap(),
self.run(&callee.clone())?,
), // 'this' binding should come from the function's self-contained environment
_ => (self.realm.global_obj.clone(), self.run(&callee.clone())?), // 'this' binding should come from the function's self-contained environment
};
let mut v_args = Vec::with_capacity(args.len());
for arg in args.iter() {
Expand Down Expand Up @@ -179,15 +168,15 @@ impl Executor for Interpreter {
Ok(result)
}
ExprDef::ObjectDecl(ref map) => {
let global_val = &self.environment.get_global_object().unwrap();
let global_val = &self.realm.environment.get_global_object().unwrap();
let obj = ValueData::new_obj(Some(global_val));
for (key, val) in map.iter() {
obj.borrow().set_field(key.clone(), self.run(val)?);
}
Ok(obj)
}
ExprDef::ArrayDecl(ref arr) => {
let global_val = &self.environment.get_global_object().unwrap();
let global_val = &self.realm.environment.get_global_object().unwrap();
let arr_map = ValueData::new_obj(Some(global_val));
// Note that this object is an Array
arr_map.set_kind(ObjectKind::Array);
Expand All @@ -199,7 +188,8 @@ impl Executor for Interpreter {
}
arr_map.borrow().set_internal_slot(
INSTANCE_PROTOTYPE,
self.environment
self.realm
.environment
.get_binding_value("Array")
.borrow()
.get_field_slice(PROTOTYPE),
Expand All @@ -212,9 +202,11 @@ impl Executor for Interpreter {
Function::RegularFunc(RegularFunction::new(*expr.clone(), args.clone()));
let val = Gc::new(ValueData::Function(Box::new(GcCell::new(function))));
if name.is_some() {
self.environment
self.realm
.environment
.create_mutable_binding(name.clone().unwrap(), false);
self.environment
self.realm
.environment
.initialize_binding(name.as_ref().unwrap(), val.clone())
}
Ok(val)
Expand Down Expand Up @@ -292,10 +284,11 @@ impl Executor for Interpreter {
}
ExprDef::BinOp(BinOp::Assign(ref op), ref a, ref b) => match a.def {
ExprDef::Local(ref name) => {
let v_a = (*self.environment.get_binding_value(&name)).clone();
let v_a = (*self.realm.environment.get_binding_value(&name)).clone();
let v_b = (*self.run(b)?).clone();
let value = exec_assign_op(op, v_a, v_b);
self.environment
self.realm
.environment
.set_mutable_binding(&name, value.clone(), true);
Ok(value)
}
Expand Down Expand Up @@ -335,7 +328,7 @@ impl Executor for Interpreter {
}
Function::RegularFunc(ref data) => {
// Create new scope
let env = &mut self.environment;
let env = &mut self.realm.environment;
env.push(new_function_environment(
construct.clone(),
this.clone(),
Expand All @@ -349,7 +342,7 @@ impl Executor for Interpreter {
env.initialize_binding(name, expr.to_owned());
}
let result = self.run(&data.expr);
self.environment.pop();
self.realm.environment.pop();
result
}
},
Expand All @@ -370,13 +363,17 @@ impl Executor for Interpreter {
let val = self.run(val_e)?;
match ref_e.def {
ExprDef::Local(ref name) => {
if *self.environment.get_binding_value(&name) != ValueData::Undefined {
if *self.realm.environment.get_binding_value(&name) != ValueData::Undefined
{
// Binding already exists
self.environment
self.realm
.environment
.set_mutable_binding(&name, val.clone(), true);
} else {
self.environment.create_mutable_binding(name.clone(), true);
self.environment.initialize_binding(name, val.clone());
self.realm
.environment
.create_mutable_binding(name.clone(), true);
self.realm.environment.initialize_binding(name, val.clone());
}
}
ExprDef::GetConstField(ref obj, ref field) => {
Expand All @@ -394,8 +391,10 @@ impl Executor for Interpreter {
Some(v) => self.run(&v)?,
None => Gc::new(ValueData::Null),
};
self.environment.create_mutable_binding(name.clone(), false);
self.environment.initialize_binding(&name, val);
self.realm
.environment
.create_mutable_binding(name.clone(), false);
self.realm.environment.initialize_binding(&name, val);
}
Ok(Gc::new(ValueData::Undefined))
}
Expand All @@ -406,17 +405,20 @@ impl Executor for Interpreter {
Some(v) => self.run(&v)?,
None => Gc::new(ValueData::Null),
};
self.environment.create_mutable_binding(name.clone(), false);
self.environment.initialize_binding(&name, val);
self.realm
.environment
.create_mutable_binding(name.clone(), false);
self.realm.environment.initialize_binding(&name, val);
}
Ok(Gc::new(ValueData::Undefined))
}
ExprDef::ConstDecl(ref vars) => {
for (name, value) in vars.iter() {
self.environment
self.realm
.environment
.create_immutable_binding(name.clone(), false);
let val = self.run(&value)?;
self.environment.initialize_binding(&name, val);
self.realm.environment.initialize_binding(&name, val);
}
Ok(Gc::new(ValueData::Undefined))
}
Expand All @@ -435,41 +437,6 @@ impl Executor for Interpreter {
}
}

impl InterpreterBuilder {
pub fn new() -> Self {
let global = ValueData::new_obj(None);
object::init(&global);
console::init(&global);
math::init(&global);
function::init(&global);
json::init(&global);
global.set_field_slice("String", string::create_constructor(&global));
global.set_field_slice("RegExp", regexp::create_constructor(&global));
global.set_field_slice("Array", array::create_constructor(&global));
global.set_field_slice("Boolean", boolean::create_constructor(&global));

Self { global }
}

pub fn init_globals<F: FnOnce(&Value)>(self, init_fn: F) -> Self {
init_fn(&self.global);
self
}

pub fn build(self) -> Interpreter {
Interpreter {
environment: LexicalEnvironment::new(self.global.clone()),
is_return: false,
}
}
}

impl Default for InterpreterBuilder {
fn default() -> Self {
Self::new()
}
}

impl Interpreter {
/// https://tc39.es/ecma262/#sec-call
fn call(&mut self, f: &Value, v: &Value, arguments_list: Vec<Value>) -> ResultValue {
Expand All @@ -490,7 +457,7 @@ impl Interpreter {
func(v, &arguments_list, self)
}
Function::RegularFunc(ref data) => {
let env = &mut self.environment;
let env = &mut self.realm.environment;
// New target (second argument) is only needed for constructors, just pass undefined
let undefined = Gc::new(ValueData::Undefined);
env.push(new_function_environment(
Expand All @@ -501,19 +468,25 @@ impl Interpreter {
for i in 0..data.args.len() {
let name = data.args.get(i).unwrap();
let expr: &Value = arguments_list.get(i).unwrap();
self.environment.create_mutable_binding(name.clone(), false);
self.environment.initialize_binding(name, expr.clone());
self.realm
.environment
.create_mutable_binding(name.clone(), false);
self.realm
.environment
.initialize_binding(name, expr.clone());
}

// Add arguments object
let arguments_obj = create_unmapped_arguments_object(arguments_list);
self.environment
self.realm
.environment
.create_mutable_binding("arguments".to_string(), false);
self.environment
self.realm
.environment
.initialize_binding("arguments", arguments_obj);

let result = self.run(&data.expr);
self.environment.pop();
self.realm.environment.pop();
result
}
},
Expand Down Expand Up @@ -608,6 +581,7 @@ impl Interpreter {
| ValueData::Null => Err(Gc::new(ValueData::Undefined)),
ValueData::Boolean(_) => {
let proto = self
.realm
.environment
.get_binding_value("Boolean")
.get_field_slice(PROTOTYPE);
Expand All @@ -618,6 +592,7 @@ impl Interpreter {
}
ValueData::Number(_) => {
let proto = self
.realm
.environment
.get_binding_value("Number")
.get_field_slice(PROTOTYPE);
Expand All @@ -627,6 +602,7 @@ impl Interpreter {
}
ValueData::String(_) => {
let proto = self
.realm
.environment
.get_binding_value("String")
.get_field_slice(PROTOTYPE);
Expand Down
Loading

0 comments on commit 1c3fea4

Please sign in to comment.