Browse Source

replacing ExecutorBuilder with Realm (#126)

* replacing ExecutorBuilder with Realm

* fixing global_env

* adding benchmark for realm

* instrinsics was being called twice

* update changelog
pull/125/head
Jason Williams 5 years ago committed by GitHub
parent
commit
1c3fea4cf1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 7
      CHANGELOG.md
  2. 4
      Cargo.toml
  3. 12
      benches/exec.rs
  4. 5
      src/bin/shell.rs
  5. 142
      src/lib/exec.rs
  6. 7
      src/lib/js/array.rs
  7. 10
      src/lib/js/boolean.rs
  8. 4
      src/lib/js/function.rs
  9. 13
      src/lib/js/regexp.rs
  10. 16
      src/lib/js/string.rs
  11. 11
      src/lib/lib.rs
  12. 95
      src/lib/realm.rs

7
CHANGELOG.md

@ -8,6 +8,13 @@ Features:
Enables Boa to run within the Test 262 framework. Enables Boa to run within the Test 262 framework.
This will help us see what is implemented or not within the spec 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) # 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. v0.4.0 brings quite a big release. The biggest feature to land is the support of regular expressions.

4
Cargo.toml

@ -42,6 +42,10 @@ harness = false
name = "fib" name = "fib"
harness = false harness = false
[[bench]]
name = "exec"
harness = false
[[bin]] [[bin]]
name = "boa" name = "boa"
path = "src/bin/bin.rs" path = "src/bin/bin.rs"

12
benches/exec.rs

@ -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
src/bin/shell.rs

@ -21,6 +21,7 @@
clippy::module_name_repetitions clippy::module_name_repetitions
)] )]
use boa::realm::Realm;
use boa::{exec::Executor, forward_val}; use boa::{exec::Executor, forward_val};
use std::{fs::read_to_string, path::PathBuf}; use std::{fs::read_to_string, path::PathBuf};
use structopt::StructOpt; use structopt::StructOpt;
@ -35,8 +36,8 @@ pub fn main() -> Result<(), std::io::Error> {
let args = Opt::from_args(); let args = Opt::from_args();
let buffer = read_to_string(args.file)?; let buffer = read_to_string(args.file)?;
let realm = Realm::create();
let mut engine = Executor::new(); let mut engine = Executor::new(realm);
match forward_val(&mut engine, &buffer) { match forward_val(&mut engine, &buffer) {
Ok(v) => print!("{}", v.to_string()), Ok(v) => print!("{}", v.to_string()),

142
src/lib/exec.rs

@ -1,13 +1,11 @@
use crate::{ use crate::{
environment::lexical_environment::{new_function_environment, LexicalEnvironment}, environment::lexical_environment::new_function_environment,
js::{ js::{
array, boolean, console, function,
function::{create_unmapped_arguments_object, Function, RegularFunction}, function::{create_unmapped_arguments_object, Function, RegularFunction},
json, math, object,
object::{ObjectKind, INSTANCE_PROTOTYPE, PROTOTYPE}, object::{ObjectKind, INSTANCE_PROTOTYPE, PROTOTYPE},
regexp, string,
value::{from_value, to_value, ResultValue, Value, ValueData}, value::{from_value, to_value, ResultValue, Value, ValueData},
}, },
realm::Realm,
syntax::ast::{ syntax::ast::{
constant::Const, constant::Const,
expr::{Expr, ExprDef}, expr::{Expr, ExprDef},
@ -23,7 +21,7 @@ use std::{
/// An execution engine /// An execution engine
pub trait Executor { pub trait Executor {
/// Make a new execution engine /// Make a new execution engine
fn new() -> Self; fn new(realm: Realm) -> Self;
/// Run an expression /// Run an expression
fn run(&mut self, expr: &Expr) -> ResultValue; fn run(&mut self, expr: &Expr) -> ResultValue;
} }
@ -31,18 +29,9 @@ pub trait Executor {
/// A Javascript intepreter /// A Javascript intepreter
#[derive(Debug)] #[derive(Debug)]
pub struct Interpreter { pub struct Interpreter {
/// An object representing the global object
environment: LexicalEnvironment,
is_return: bool, is_return: bool,
} /// realm holds both the global object and the environment
realm: Realm,
/// Builder for the [`Interpreter`]
///
/// [`Interpreter`]: struct.Interpreter.html
#[derive(Debug)]
pub struct InterpreterBuilder {
/// The global object
global: Value,
} }
fn exec_assign_op(op: &AssignOp, v_a: ValueData, v_b: ValueData) -> Value { fn exec_assign_op(op: &AssignOp, v_a: ValueData, v_b: ValueData) -> Value {
@ -61,8 +50,11 @@ fn exec_assign_op(op: &AssignOp, v_a: ValueData, v_b: ValueData) -> Value {
} }
impl Executor for Interpreter { impl Executor for Interpreter {
fn new() -> Self { fn new(realm: Realm) -> Self {
InterpreterBuilder::new().build() Interpreter {
realm,
is_return: false,
}
} }
#[allow(clippy::match_same_arms)] #[allow(clippy::match_same_arms)]
@ -94,7 +86,7 @@ impl Executor for Interpreter {
Ok(obj) Ok(obj)
} }
ExprDef::Local(ref name) => { ExprDef::Local(ref name) => {
let val = self.environment.get_binding_value(name); let val = self.realm.environment.get_binding_value(name);
Ok(val) Ok(val)
} }
ExprDef::GetConstField(ref obj, ref field) => { ExprDef::GetConstField(ref obj, ref field) => {
@ -123,10 +115,7 @@ impl Executor for Interpreter {
obj.borrow().get_field(&field.borrow().to_string()), obj.borrow().get_field(&field.borrow().to_string()),
) )
} }
_ => ( _ => (self.realm.global_obj.clone(), self.run(&callee.clone())?), // 'this' binding should come from the function's self-contained environment
self.environment.get_global_object().unwrap(),
self.run(&callee.clone())?,
), // 'this' binding should come from the function's self-contained environment
}; };
let mut v_args = Vec::with_capacity(args.len()); let mut v_args = Vec::with_capacity(args.len());
for arg in args.iter() { for arg in args.iter() {
@ -179,7 +168,7 @@ impl Executor for Interpreter {
Ok(result) Ok(result)
} }
ExprDef::ObjectDecl(ref map) => { 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)); let obj = ValueData::new_obj(Some(global_val));
for (key, val) in map.iter() { for (key, val) in map.iter() {
obj.borrow().set_field(key.clone(), self.run(val)?); obj.borrow().set_field(key.clone(), self.run(val)?);
@ -187,7 +176,7 @@ impl Executor for Interpreter {
Ok(obj) Ok(obj)
} }
ExprDef::ArrayDecl(ref arr) => { 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)); let arr_map = ValueData::new_obj(Some(global_val));
// Note that this object is an Array // Note that this object is an Array
arr_map.set_kind(ObjectKind::Array); arr_map.set_kind(ObjectKind::Array);
@ -199,7 +188,8 @@ impl Executor for Interpreter {
} }
arr_map.borrow().set_internal_slot( arr_map.borrow().set_internal_slot(
INSTANCE_PROTOTYPE, INSTANCE_PROTOTYPE,
self.environment self.realm
.environment
.get_binding_value("Array") .get_binding_value("Array")
.borrow() .borrow()
.get_field_slice(PROTOTYPE), .get_field_slice(PROTOTYPE),
@ -212,9 +202,11 @@ impl Executor for Interpreter {
Function::RegularFunc(RegularFunction::new(*expr.clone(), args.clone())); Function::RegularFunc(RegularFunction::new(*expr.clone(), args.clone()));
let val = Gc::new(ValueData::Function(Box::new(GcCell::new(function)))); let val = Gc::new(ValueData::Function(Box::new(GcCell::new(function))));
if name.is_some() { if name.is_some() {
self.environment self.realm
.environment
.create_mutable_binding(name.clone().unwrap(), false); .create_mutable_binding(name.clone().unwrap(), false);
self.environment self.realm
.environment
.initialize_binding(name.as_ref().unwrap(), val.clone()) .initialize_binding(name.as_ref().unwrap(), val.clone())
} }
Ok(val) Ok(val)
@ -292,10 +284,11 @@ impl Executor for Interpreter {
} }
ExprDef::BinOp(BinOp::Assign(ref op), ref a, ref b) => match a.def { ExprDef::BinOp(BinOp::Assign(ref op), ref a, ref b) => match a.def {
ExprDef::Local(ref name) => { 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 v_b = (*self.run(b)?).clone();
let value = exec_assign_op(op, v_a, v_b); let value = exec_assign_op(op, v_a, v_b);
self.environment self.realm
.environment
.set_mutable_binding(&name, value.clone(), true); .set_mutable_binding(&name, value.clone(), true);
Ok(value) Ok(value)
} }
@ -335,7 +328,7 @@ impl Executor for Interpreter {
} }
Function::RegularFunc(ref data) => { Function::RegularFunc(ref data) => {
// Create new scope // Create new scope
let env = &mut self.environment; let env = &mut self.realm.environment;
env.push(new_function_environment( env.push(new_function_environment(
construct.clone(), construct.clone(),
this.clone(), this.clone(),
@ -349,7 +342,7 @@ impl Executor for Interpreter {
env.initialize_binding(name, expr.to_owned()); env.initialize_binding(name, expr.to_owned());
} }
let result = self.run(&data.expr); let result = self.run(&data.expr);
self.environment.pop(); self.realm.environment.pop();
result result
} }
}, },
@ -370,13 +363,17 @@ impl Executor for Interpreter {
let val = self.run(val_e)?; let val = self.run(val_e)?;
match ref_e.def { match ref_e.def {
ExprDef::Local(ref name) => { 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 // Binding already exists
self.environment self.realm
.environment
.set_mutable_binding(&name, val.clone(), true); .set_mutable_binding(&name, val.clone(), true);
} else { } else {
self.environment.create_mutable_binding(name.clone(), true); self.realm
self.environment.initialize_binding(name, val.clone()); .environment
.create_mutable_binding(name.clone(), true);
self.realm.environment.initialize_binding(name, val.clone());
} }
} }
ExprDef::GetConstField(ref obj, ref field) => { ExprDef::GetConstField(ref obj, ref field) => {
@ -394,8 +391,10 @@ impl Executor for Interpreter {
Some(v) => self.run(&v)?, Some(v) => self.run(&v)?,
None => Gc::new(ValueData::Null), None => Gc::new(ValueData::Null),
}; };
self.environment.create_mutable_binding(name.clone(), false); self.realm
self.environment.initialize_binding(&name, val); .environment
.create_mutable_binding(name.clone(), false);
self.realm.environment.initialize_binding(&name, val);
} }
Ok(Gc::new(ValueData::Undefined)) Ok(Gc::new(ValueData::Undefined))
} }
@ -406,17 +405,20 @@ impl Executor for Interpreter {
Some(v) => self.run(&v)?, Some(v) => self.run(&v)?,
None => Gc::new(ValueData::Null), None => Gc::new(ValueData::Null),
}; };
self.environment.create_mutable_binding(name.clone(), false); self.realm
self.environment.initialize_binding(&name, val); .environment
.create_mutable_binding(name.clone(), false);
self.realm.environment.initialize_binding(&name, val);
} }
Ok(Gc::new(ValueData::Undefined)) Ok(Gc::new(ValueData::Undefined))
} }
ExprDef::ConstDecl(ref vars) => { ExprDef::ConstDecl(ref vars) => {
for (name, value) in vars.iter() { for (name, value) in vars.iter() {
self.environment self.realm
.environment
.create_immutable_binding(name.clone(), false); .create_immutable_binding(name.clone(), false);
let val = self.run(&value)?; let val = self.run(&value)?;
self.environment.initialize_binding(&name, val); self.realm.environment.initialize_binding(&name, val);
} }
Ok(Gc::new(ValueData::Undefined)) Ok(Gc::new(ValueData::Undefined))
} }
@ -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 { impl Interpreter {
/// https://tc39.es/ecma262/#sec-call /// https://tc39.es/ecma262/#sec-call
fn call(&mut self, f: &Value, v: &Value, arguments_list: Vec<Value>) -> ResultValue { fn call(&mut self, f: &Value, v: &Value, arguments_list: Vec<Value>) -> ResultValue {
@ -490,7 +457,7 @@ impl Interpreter {
func(v, &arguments_list, self) func(v, &arguments_list, self)
} }
Function::RegularFunc(ref data) => { 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 // New target (second argument) is only needed for constructors, just pass undefined
let undefined = Gc::new(ValueData::Undefined); let undefined = Gc::new(ValueData::Undefined);
env.push(new_function_environment( env.push(new_function_environment(
@ -501,19 +468,25 @@ impl Interpreter {
for i in 0..data.args.len() { for i in 0..data.args.len() {
let name = data.args.get(i).unwrap(); let name = data.args.get(i).unwrap();
let expr: &Value = arguments_list.get(i).unwrap(); let expr: &Value = arguments_list.get(i).unwrap();
self.environment.create_mutable_binding(name.clone(), false); self.realm
self.environment.initialize_binding(name, expr.clone()); .environment
.create_mutable_binding(name.clone(), false);
self.realm
.environment
.initialize_binding(name, expr.clone());
} }
// Add arguments object // Add arguments object
let arguments_obj = create_unmapped_arguments_object(arguments_list); let arguments_obj = create_unmapped_arguments_object(arguments_list);
self.environment self.realm
.environment
.create_mutable_binding("arguments".to_string(), false); .create_mutable_binding("arguments".to_string(), false);
self.environment self.realm
.environment
.initialize_binding("arguments", arguments_obj); .initialize_binding("arguments", arguments_obj);
let result = self.run(&data.expr); let result = self.run(&data.expr);
self.environment.pop(); self.realm.environment.pop();
result result
} }
}, },
@ -608,6 +581,7 @@ impl Interpreter {
| ValueData::Null => Err(Gc::new(ValueData::Undefined)), | ValueData::Null => Err(Gc::new(ValueData::Undefined)),
ValueData::Boolean(_) => { ValueData::Boolean(_) => {
let proto = self let proto = self
.realm
.environment .environment
.get_binding_value("Boolean") .get_binding_value("Boolean")
.get_field_slice(PROTOTYPE); .get_field_slice(PROTOTYPE);
@ -618,6 +592,7 @@ impl Interpreter {
} }
ValueData::Number(_) => { ValueData::Number(_) => {
let proto = self let proto = self
.realm
.environment .environment
.get_binding_value("Number") .get_binding_value("Number")
.get_field_slice(PROTOTYPE); .get_field_slice(PROTOTYPE);
@ -627,6 +602,7 @@ impl Interpreter {
} }
ValueData::String(_) => { ValueData::String(_) => {
let proto = self let proto = self
.realm
.environment .environment
.get_binding_value("String") .get_binding_value("String")
.get_field_slice(PROTOTYPE); .get_field_slice(PROTOTYPE);

7
src/lib/js/array.rs

@ -290,11 +290,13 @@ pub fn create_constructor(global: &Value) -> Value {
mod tests { mod tests {
use crate::exec::Executor; use crate::exec::Executor;
use crate::forward; use crate::forward;
use crate::realm::Realm;
#[test] #[test]
fn concat() { fn concat() {
//TODO: array display formatter //TODO: array display formatter
let mut engine = Executor::new(); let realm = Realm::create();
let mut engine = Executor::new(realm);
let init = r#" let init = r#"
let empty = new Array(); let empty = new Array();
let one = new Array(1); let one = new Array(1);
@ -316,7 +318,8 @@ mod tests {
#[test] #[test]
fn join() { fn join() {
let mut engine = Executor::new(); let realm = Realm::create();
let mut engine = Executor::new(realm);
let init = r#" let init = r#"
let empty = [ ]; let empty = [ ];
let one = ["a"]; let one = ["a"];

10
src/lib/js/boolean.rs

@ -91,6 +91,7 @@ pub fn this_boolean_value(value: &Value) -> Value {
mod tests { mod tests {
use super::*; use super::*;
use crate::exec::Executor; use crate::exec::Executor;
use crate::realm::Realm;
use crate::{forward, forward_val, js::value::same_value}; use crate::{forward, forward_val, js::value::same_value};
#[test] #[test]
@ -103,7 +104,8 @@ mod tests {
#[test] #[test]
/// Test the correct type is returned from call and construct /// Test the correct type is returned from call and construct
fn construct_and_call() { fn construct_and_call() {
let mut engine = Executor::new(); let realm = Realm::create();
let mut engine = Executor::new(realm);
let init = r#" let init = r#"
const one = new Boolean(1); const one = new Boolean(1);
const zero = Boolean(0); const zero = Boolean(0);
@ -118,7 +120,8 @@ mod tests {
#[test] #[test]
fn constructor_gives_true_instance() { fn constructor_gives_true_instance() {
let mut engine = Executor::new(); let realm = Realm::create();
let mut engine = Executor::new(realm);
let init = r#" let init = r#"
const trueVal = new Boolean(true); const trueVal = new Boolean(true);
const trueNum = new Boolean(1); const trueNum = new Boolean(1);
@ -147,7 +150,8 @@ mod tests {
#[test] #[test]
fn instances_have_correct_proto_set() { fn instances_have_correct_proto_set() {
let mut engine = Executor::new(); let realm = Realm::create();
let mut engine = Executor::new(realm);
let init = r#" let init = r#"
const boolInstance = new Boolean(true); const boolInstance = new Boolean(true);
const boolProto = Boolean.prototype; const boolProto = Boolean.prototype;

4
src/lib/js/function.rs

@ -133,11 +133,13 @@ pub fn create_unmapped_arguments_object(arguments_list: Vec<Value>) -> Value {
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::exec::Executor; use crate::exec::Executor;
use crate::realm::Realm;
use crate::{forward, forward_val, js::value::from_value}; use crate::{forward, forward_val, js::value::from_value};
#[test] #[test]
fn check_arguments_object() { fn check_arguments_object() {
let mut engine = Executor::new(); let realm = Realm::create();
let mut engine = Executor::new(realm);
let init = r#" let init = r#"
function jason(a, b) { function jason(a, b) {
return arguments[0]; return arguments[0];

13
src/lib/js/regexp.rs

@ -292,10 +292,12 @@ mod tests {
use super::*; use super::*;
use crate::exec::Executor; use crate::exec::Executor;
use crate::forward; use crate::forward;
use crate::realm::Realm;
#[test] #[test]
fn test_constructors() { fn test_constructors() {
let mut engine = Executor::new(); let realm = Realm::create();
let mut engine = Executor::new(realm);
let init = r#" let init = r#"
let constructed = new RegExp("[0-9]+(\\.[0-9]+)?"); let constructed = new RegExp("[0-9]+(\\.[0-9]+)?");
let literal = /[0-9]+(\.[0-9]+)?/; let literal = /[0-9]+(\.[0-9]+)?/;
@ -345,7 +347,8 @@ mod tests {
#[test] #[test]
fn test_last_index() { fn test_last_index() {
let mut engine = Executor::new(); let realm = Realm::create();
let mut engine = Executor::new(realm);
let init = r#" let init = r#"
let regex = /[0-9]+(\.[0-9]+)?/g; let regex = /[0-9]+(\.[0-9]+)?/g;
"#; "#;
@ -360,7 +363,8 @@ mod tests {
#[test] #[test]
fn test_exec() { fn test_exec() {
let mut engine = Executor::new(); let realm = Realm::create();
let mut engine = Executor::new(realm);
let init = r#" let init = r#"
var re = /quick\s(brown).+?(jumps)/ig; var re = /quick\s(brown).+?(jumps)/ig;
var result = re.exec('The Quick Brown Fox Jumps Over The Lazy Dog'); var result = re.exec('The Quick Brown Fox Jumps Over The Lazy Dog');
@ -379,7 +383,8 @@ mod tests {
#[test] #[test]
fn test_to_string() { fn test_to_string() {
let mut engine = Executor::new(); let realm = Realm::create();
let mut engine = Executor::new(realm);
assert_eq!( assert_eq!(
forward(&mut engine, "(new RegExp('a+b+c')).toString()"), forward(&mut engine, "(new RegExp('a+b+c')).toString()"),

16
src/lib/js/string.rs

@ -713,6 +713,7 @@ pub fn init(global: &Value) {
mod tests { mod tests {
use super::*; use super::*;
use crate::exec::Executor; use crate::exec::Executor;
use crate::realm::Realm;
use crate::{forward, forward_val}; use crate::{forward, forward_val};
#[test] #[test]
@ -749,7 +750,8 @@ mod tests {
// } // }
#[test] #[test]
fn concat() { fn concat() {
let mut engine = Executor::new(); let realm = Realm::create();
let mut engine = Executor::new(realm);
let init = r#" let init = r#"
const hello = new String('Hello, '); const hello = new String('Hello, ');
const world = new String('world! '); const world = new String('world! ');
@ -766,7 +768,8 @@ mod tests {
#[test] #[test]
/// Test the correct type is returned from call and construct /// Test the correct type is returned from call and construct
fn construct_and_call() { fn construct_and_call() {
let mut engine = Executor::new(); let realm = Realm::create();
let mut engine = Executor::new(realm);
let init = r#" let init = r#"
const hello = new String('Hello'); const hello = new String('Hello');
const world = String('world'); const world = String('world');
@ -781,7 +784,8 @@ mod tests {
#[test] #[test]
fn repeat() { fn repeat() {
let mut engine = Executor::new(); let realm = Realm::create();
let mut engine = Executor::new(realm);
let init = r#" let init = r#"
const empty = new String(''); const empty = new String('');
const en = new String('english'); const en = new String('english');
@ -808,7 +812,8 @@ mod tests {
#[test] #[test]
fn starts_with() { fn starts_with() {
let mut engine = Executor::new(); let realm = Realm::create();
let mut engine = Executor::new(realm);
let init = r#" let init = r#"
const empty = new String(''); const empty = new String('');
const en = new String('english'); const en = new String('english');
@ -831,7 +836,8 @@ mod tests {
#[test] #[test]
fn ends_with() { fn ends_with() {
let mut engine = Executor::new(); let realm = Realm::create();
let mut engine = Executor::new(realm);
let init = r#" let init = r#"
const empty = new String(''); const empty = new String('');
const en = new String('english'); const en = new String('english');

11
src/lib/lib.rs

@ -36,11 +36,13 @@
pub mod environment; pub mod environment;
pub mod exec; pub mod exec;
pub mod js; pub mod js;
pub mod realm;
pub mod syntax; pub mod syntax;
use crate::{ use crate::{
exec::{Executor, Interpreter}, exec::{Executor, Interpreter},
js::value::ResultValue, js::value::ResultValue,
realm::Realm,
syntax::{ast::expr::Expr, lexer::Lexer, parser::Parser}, syntax::{ast::expr::Expr, lexer::Lexer, parser::Parser},
}; };
use wasm_bindgen::prelude::*; use wasm_bindgen::prelude::*;
@ -76,7 +78,9 @@ pub fn forward_val(engine: &mut Interpreter, src: &str) -> ResultValue {
/// Create a clean Interpreter and execute the code /// Create a clean Interpreter and execute the code
pub fn exec(src: &str) -> String { pub fn exec(src: &str) -> String {
let mut engine: Interpreter = Executor::new(); // Create new Realm
let realm = Realm::create();
let mut engine: Interpreter = Executor::new(realm);
forward(&mut engine, src) forward(&mut engine, src)
} }
@ -111,8 +115,9 @@ pub fn evaluate(src: &str) -> String {
return String::from("parsing failed"); return String::from("parsing failed");
} }
} }
// Create new Realm
let mut engine: Interpreter = Executor::new(); let realm = Realm::create();
let mut engine: Interpreter = Executor::new(realm);
let result = engine.run(&expr); let result = engine.run(&expr);
log("test2"); log("test2");
match result { match result {

95
src/lib/realm.rs

@ -0,0 +1,95 @@
//! Conceptually, a realm consists of a set of intrinsic objects, an ECMAScript global environment,
//! all of the ECMAScript code that is loaded within the scope of that global environment,
//! and other associated state and resources.
//!
//!A realm is represented in this implementation as a Realm struct with the fields specified from the spec
use crate::{
environment::{
declarative_environment_record::DeclarativeEnvironmentRecord,
global_environment_record::GlobalEnvironmentRecord,
lexical_environment::LexicalEnvironment,
object_environment_record::ObjectEnvironmentRecord,
},
js::{
array, boolean, console, function, json, math, object, regexp, string,
value::{Value, ValueData},
},
};
use gc::{Gc, GcCell};
use std::collections::{hash_map::HashMap, hash_set::HashSet};
/// Representation of a Realm.
/// In the specification these are called Realm Records.
#[derive(Debug)]
pub struct Realm {
pub global_obj: Value,
pub global_env: Gc<GcCell<Box<GlobalEnvironmentRecord>>>,
pub environment: LexicalEnvironment,
}
impl Realm {
pub fn create() -> Realm {
// Create brand new global object
// Global has no prototype to pass None to new_obj
let global = ValueData::new_obj(None);
// We need to clone the global here because its referenced from separate places (only pointer is cloned)
let global_env = new_global_environment(global.clone(), global.clone());
let new_realm = Realm {
global_obj: global.clone(),
global_env,
environment: LexicalEnvironment::new(global),
};
// Add new builtIns to Realm
// At a later date this can be removed from here and called explicity, but for now we almost always want these default builtins
new_realm.create_instrinsics();
new_realm
}
// Sets up the default global objects within Global
fn create_instrinsics(&self) {
let global = &self.global_obj;
// Create intrinsics, add global objects here
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));
}
}
// Similar to new_global_environment in lexical_environment, except we need to return a GlobalEnvirionment
fn new_global_environment(
global: Value,
this_value: Value,
) -> Gc<GcCell<Box<GlobalEnvironmentRecord>>> {
let obj_rec = Box::new(ObjectEnvironmentRecord {
bindings: global,
outer_env: None,
/// Object Environment Records created for with statements (13.11)
/// can provide their binding object as an implicit this value for use in function calls.
/// The capability is controlled by a withEnvironment Boolean value that is associated
/// with each object Environment Record. By default, the value of withEnvironment is false
/// for any object Environment Record.
with_environment: false,
});
let dcl_rec = Box::new(DeclarativeEnvironmentRecord {
env_rec: HashMap::new(),
outer_env: None,
});
Gc::new(GcCell::new(Box::new(GlobalEnvironmentRecord {
object_record: obj_rec,
global_this_binding: this_value,
declarative_record: dcl_rec,
var_names: HashSet::new(),
})))
}
Loading…
Cancel
Save