Browse Source

Make environment methods take `&mut Context` (#1131)

pull/1246/head
Halid Odat 4 years ago committed by GitHub
parent
commit
411d57afab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 3
      boa/src/builtins/array/mod.rs
  2. 9
      boa/src/builtins/function/mod.rs
  3. 2
      boa/src/builtins/global_this/mod.rs
  4. 7
      boa/src/builtins/map/mod.rs
  5. 2
      boa/src/builtins/mod.rs
  6. 29
      boa/src/context.rs
  7. 86
      boa/src/environment/declarative_environment_record.rs
  8. 85
      boa/src/environment/environment_record_trait.rs
  9. 103
      boa/src/environment/function_environment_record.rs
  10. 141
      boa/src/environment/global_environment_record.rs
  11. 171
      boa/src/environment/lexical_environment.rs
  12. 32
      boa/src/environment/mod.rs
  13. 70
      boa/src/environment/object_environment_record.rs
  14. 1
      boa/src/gc.rs
  15. 67
      boa/src/object/gcobject.rs
  16. 12
      boa/src/realm.rs
  17. 12
      boa/src/syntax/ast/node/block/mod.rs
  18. 2
      boa/src/syntax/ast/node/call/mod.rs
  19. 21
      boa/src/syntax/ast/node/declaration/function_decl/mod.rs
  20. 38
      boa/src/syntax/ast/node/declaration/mod.rs
  21. 6
      boa/src/syntax/ast/node/identifier/mod.rs
  22. 103
      boa/src/syntax/ast/node/iteration/for_in_loop/mod.rs
  23. 10
      boa/src/syntax/ast/node/iteration/for_loop/mod.rs
  24. 105
      boa/src/syntax/ast/node/iteration/for_of_loop/mod.rs
  25. 6
      boa/src/syntax/ast/node/mod.rs
  26. 25
      boa/src/syntax/ast/node/operator/assign/mod.rs
  27. 12
      boa/src/syntax/ast/node/operator/bin_op/mod.rs
  28. 5
      boa/src/syntax/ast/node/template/mod.rs
  29. 22
      boa/src/syntax/ast/node/try_node/mod.rs

3
boa/src/builtins/array/mod.rs

@ -17,8 +17,7 @@ use crate::{
builtins::array::array_iterator::{ArrayIterationKind, ArrayIterator}, builtins::array::array_iterator::{ArrayIterationKind, ArrayIterator},
builtins::BuiltIn, builtins::BuiltIn,
builtins::Number, builtins::Number,
gc::GcObject, object::{ConstructorBuilder, FunctionBuilder, GcObject, ObjectData, PROTOTYPE},
object::{ConstructorBuilder, FunctionBuilder, ObjectData, PROTOTYPE},
property::{Attribute, DataDescriptor}, property::{Attribute, DataDescriptor},
value::{same_value_zero, IntegerOrInfinity, Value}, value::{same_value_zero, IntegerOrInfinity, Value},
BoaProfiler, Context, Result, BoaProfiler, Context, Result,

9
boa/src/builtins/function/mod.rs

@ -126,13 +126,13 @@ impl Function {
local_env local_env
.borrow_mut() .borrow_mut()
// Function parameters can share names in JavaScript... // Function parameters can share names in JavaScript...
.create_mutable_binding(param.name().to_owned(), false, true) .create_mutable_binding(param.name().to_owned(), false, true, context)
.expect("Failed to create binding for rest param"); .expect("Failed to create binding for rest param");
// Set Binding to value // Set Binding to value
local_env local_env
.borrow_mut() .borrow_mut()
.initialize_binding(param.name(), array) .initialize_binding(param.name(), array, context)
.expect("Failed to initialize rest param"); .expect("Failed to initialize rest param");
} }
@ -142,17 +142,18 @@ impl Function {
param: &FormalParameter, param: &FormalParameter,
value: Value, value: Value,
local_env: &Environment, local_env: &Environment,
context: &mut Context,
) { ) {
// Create binding // Create binding
local_env local_env
.borrow_mut() .borrow_mut()
.create_mutable_binding(param.name().to_owned(), false, true) .create_mutable_binding(param.name().to_owned(), false, true, context)
.expect("Failed to create binding"); .expect("Failed to create binding");
// Set Binding to value // Set Binding to value
local_env local_env
.borrow_mut() .borrow_mut()
.initialize_binding(param.name(), value) .initialize_binding(param.name(), value, context)
.expect("Failed to intialize binding"); .expect("Failed to intialize binding");
} }

2
boa/src/builtins/global_this/mod.rs

@ -30,7 +30,7 @@ impl BuiltIn for GlobalThis {
( (
Self::NAME, Self::NAME,
context.global_object().clone().into(), context.global_object().into(),
Self::attribute(), Self::attribute(),
) )
} }

7
boa/src/builtins/map/mod.rs

@ -79,12 +79,7 @@ impl Map {
} }
let map_prototype = context let map_prototype = context
.global_object() .global_object()
.clone() .get(&"Map".into(), context.global_object().into(), context)?
.get(
&"Map".into(),
context.global_object().clone().into(),
context,
)?
.get_field(PROTOTYPE, context)? .get_field(PROTOTYPE, context)?
.as_object() .as_object()
.expect("'Map' global property should be an object"); .expect("'Map' global property should be an object");

2
boa/src/builtins/mod.rs

@ -93,7 +93,7 @@ pub fn init(context: &mut Context) {
console::Console::init, console::Console::init,
]; ];
let global_object = context.global_object().clone(); let global_object = context.global_object();
for init in &globals { for init in &globals {
let (name, value, attribute) = init(context); let (name, value, attribute) = init(context);

29
boa/src/context.rs

@ -210,7 +210,7 @@ impl StandardObjects {
#[derive(Debug)] #[derive(Debug)]
pub struct Context { pub struct Context {
/// realm holds both the global object and the environment /// realm holds both the global object and the environment
realm: Realm, pub(crate) realm: Realm,
/// The current executor. /// The current executor.
executor: Interpreter, executor: Interpreter,
@ -270,16 +270,6 @@ impl Context {
Default::default() Default::default()
} }
#[inline]
pub fn realm(&self) -> &Realm {
&self.realm
}
#[inline]
pub fn realm_mut(&mut self) -> &mut Realm {
&mut self.realm
}
#[inline] #[inline]
pub fn executor(&mut self) -> &mut Interpreter { pub fn executor(&mut self) -> &mut Interpreter {
&mut self.executor &mut self.executor
@ -340,8 +330,8 @@ impl Context {
/// Return the global object. /// Return the global object.
#[inline] #[inline]
pub fn global_object(&self) -> &GcObject { pub fn global_object(&self) -> GcObject {
&self.realm().global_object self.realm.global_object.clone()
} }
/// Constructs a `RangeError` with the specified message. /// Constructs a `RangeError` with the specified message.
@ -503,7 +493,7 @@ impl Context {
flags, flags,
body: RcStatementList::from(body.into()), body: RcStatementList::from(body.into()),
params, params,
environment: self.realm.environment.get_current_environment().clone(), environment: self.get_current_environment().clone(),
}; };
let new_func = Object::function(func, function_prototype); let new_func = Object::function(func, function_prototype);
@ -555,7 +545,7 @@ impl Context {
body: NativeFunction, body: NativeFunction,
) -> Result<()> { ) -> Result<()> {
let function = self.create_builtin_function(name, length, body)?; let function = self.create_builtin_function(name, length, body)?;
let mut global = self.global_object().clone(); let mut global = self.global_object();
global.insert_property(name, function, Attribute::all()); global.insert_property(name, function, Attribute::all());
Ok(()) Ok(())
} }
@ -574,10 +564,7 @@ impl Context {
pub(crate) fn set_value(&mut self, node: &Node, value: Value) -> Result<Value> { pub(crate) fn set_value(&mut self, node: &Node, value: Value) -> Result<Value> {
match node { match node {
Node::Identifier(ref name) => { Node::Identifier(ref name) => {
self.realm self.set_mutable_binding(name.as_ref(), value.clone(), true)?;
.environment
.set_mutable_binding(name.as_ref(), value.clone(), true)
.map_err(|e| e.to_error(self))?;
Ok(value) Ok(value)
} }
Node::GetConstField(ref get_const_field_node) => Ok(get_const_field_node Node::GetConstField(ref get_const_field_node) => Ok(get_const_field_node
@ -616,7 +603,7 @@ impl Context {
let class = class_builder.build(); let class = class_builder.build();
let property = DataDescriptor::new(class, T::ATTRIBUTE); let property = DataDescriptor::new(class, T::ATTRIBUTE);
self.global_object().clone().insert(T::NAME, property); self.global_object().insert(T::NAME, property);
Ok(()) Ok(())
} }
@ -643,7 +630,7 @@ impl Context {
V: Into<Value>, V: Into<Value>,
{ {
let property = DataDescriptor::new(value, attribute); let property = DataDescriptor::new(value, attribute);
self.global_object().clone().insert(key, property); self.global_object().insert(key, property);
} }
/// Evaluates the given code. /// Evaluates the given code.

86
boa/src/environment/declarative_environment_record.rs

@ -5,15 +5,16 @@
//! A declarative Environment Record binds the set of identifiers defined by the declarations contained within its scope. //! A declarative Environment Record binds the set of identifiers defined by the declarations contained within its scope.
//! More info: [ECMA-262 sec-declarative-environment-records](https://tc39.es/ecma262/#sec-declarative-environment-records) //! More info: [ECMA-262 sec-declarative-environment-records](https://tc39.es/ecma262/#sec-declarative-environment-records)
use super::ErrorKind;
use crate::{ use crate::{
environment::{ environment::{
environment_record_trait::EnvironmentRecordTrait, environment_record_trait::EnvironmentRecordTrait,
lexical_environment::{Environment, EnvironmentType}, lexical_environment::{Environment, EnvironmentType},
}, },
gc::{Finalize, Trace}, gc::{Finalize, Trace},
Value, object::GcObject,
BoaProfiler, Context, Result, Value,
}; };
use gc::{Gc, GcCell};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
/// Declarative Bindings have a few properties for book keeping purposes, such as mutability (const vs let). /// Declarative Bindings have a few properties for book keeping purposes, such as mutability (const vs let).
@ -33,10 +34,23 @@ pub struct DeclarativeEnvironmentRecordBinding {
/// declarations contained within its scope. /// declarations contained within its scope.
#[derive(Debug, Trace, Finalize, Clone)] #[derive(Debug, Trace, Finalize, Clone)]
pub struct DeclarativeEnvironmentRecord { pub struct DeclarativeEnvironmentRecord {
pub env_rec: FxHashMap<String, DeclarativeEnvironmentRecordBinding>, pub env_rec: FxHashMap<Box<str>, DeclarativeEnvironmentRecordBinding>,
pub outer_env: Option<Environment>, pub outer_env: Option<Environment>,
} }
impl DeclarativeEnvironmentRecord {
#[allow(clippy::new_ret_no_self)]
pub fn new(env: Option<Environment>) -> Environment {
let _timer = BoaProfiler::global().start_event("new_declarative_environment", "env");
let boxed_env = Box::new(DeclarativeEnvironmentRecord {
env_rec: FxHashMap::default(),
outer_env: env,
});
Gc::new(GcCell::new(boxed_env))
}
}
impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord { impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
fn has_binding(&self, name: &str) -> bool { fn has_binding(&self, name: &str) -> bool {
self.env_rec.contains_key(name) self.env_rec.contains_key(name)
@ -47,17 +61,18 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
name: String, name: String,
deletion: bool, deletion: bool,
allow_name_reuse: bool, allow_name_reuse: bool,
) -> Result<(), ErrorKind> { _context: &mut Context,
) -> Result<()> {
if !allow_name_reuse { if !allow_name_reuse {
assert!( assert!(
!self.env_rec.contains_key(&name), !self.env_rec.contains_key(name.as_str()),
"Identifier {} has already been declared", "Identifier {} has already been declared",
name name
); );
} }
self.env_rec.insert( self.env_rec.insert(
name, name.into_boxed_str(),
DeclarativeEnvironmentRecordBinding { DeclarativeEnvironmentRecordBinding {
value: None, value: None,
can_delete: deletion, can_delete: deletion,
@ -68,15 +83,20 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
Ok(()) Ok(())
} }
fn create_immutable_binding(&mut self, name: String, strict: bool) -> Result<(), ErrorKind> { fn create_immutable_binding(
&mut self,
name: String,
strict: bool,
_context: &mut Context,
) -> Result<()> {
assert!( assert!(
!self.env_rec.contains_key(&name), !self.env_rec.contains_key(name.as_str()),
"Identifier {} has already been declared", "Identifier {} has already been declared",
name name
); );
self.env_rec.insert( self.env_rec.insert(
name, name.into_boxed_str(),
DeclarativeEnvironmentRecordBinding { DeclarativeEnvironmentRecordBinding {
value: None, value: None,
can_delete: true, can_delete: true,
@ -87,7 +107,12 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
Ok(()) Ok(())
} }
fn initialize_binding(&mut self, name: &str, value: Value) -> Result<(), ErrorKind> { fn initialize_binding(
&mut self,
name: &str,
value: Value,
_context: &mut Context,
) -> Result<()> {
if let Some(ref mut record) = self.env_rec.get_mut(name) { if let Some(ref mut record) = self.env_rec.get_mut(name) {
if record.value.is_none() { if record.value.is_none() {
record.value = Some(value); record.value = Some(value);
@ -103,17 +128,15 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
name: &str, name: &str,
value: Value, value: Value,
mut strict: bool, mut strict: bool,
) -> Result<(), ErrorKind> { context: &mut Context,
) -> Result<()> {
if self.env_rec.get(name).is_none() { if self.env_rec.get(name).is_none() {
if strict { if strict {
return Err(ErrorKind::new_reference_error(format!( return Err(context.construct_reference_error(format!("{} not found", name)));
"{} not found",
name
)));
} }
self.create_mutable_binding(name.to_owned(), true, false)?; self.create_mutable_binding(name.to_owned(), true, false, context)?;
self.initialize_binding(name, value)?; self.initialize_binding(name, value, context)?;
return Ok(()); return Ok(());
} }
@ -122,15 +145,14 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
strict = true strict = true
} }
if record.value.is_none() { if record.value.is_none() {
return Err(ErrorKind::new_reference_error(format!( return Err(
"{} has not been initialized", context.construct_reference_error(format!("{} has not been initialized", name))
name );
)));
} }
if record.mutable { if record.mutable {
record.value = Some(value); record.value = Some(value);
} else if strict { } else if strict {
return Err(ErrorKind::new_type_error(format!( return Err(context.construct_reference_error(format!(
"Cannot mutate an immutable binding {}", "Cannot mutate an immutable binding {}",
name name
))); )));
@ -139,15 +161,12 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
Ok(()) Ok(())
} }
fn get_binding_value(&self, name: &str, _strict: bool) -> Result<Value, ErrorKind> { fn get_binding_value(&self, name: &str, _strict: bool, context: &mut Context) -> Result<Value> {
if let Some(binding) = self.env_rec.get(name) { if let Some(binding) = self.env_rec.get(name) {
if let Some(ref val) = binding.value { if let Some(ref val) = binding.value {
Ok(val.clone()) Ok(val.clone())
} else { } else {
Err(ErrorKind::new_reference_error(format!( context.throw_reference_error(format!("{} is an uninitialized binding", name))
"{} is an uninitialized binding",
name
)))
} }
} else { } else {
panic!("Cannot get binding value for {}", name); panic!("Cannot get binding value for {}", name);
@ -172,7 +191,7 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
false false
} }
fn get_this_binding(&self) -> Result<Value, ErrorKind> { fn get_this_binding(&self, _context: &mut Context) -> Result<Value> {
Ok(Value::undefined()) Ok(Value::undefined())
} }
@ -180,8 +199,8 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
false false
} }
fn with_base_object(&self) -> Value { fn with_base_object(&self) -> Option<GcObject> {
Value::undefined() None
} }
fn get_outer_environment_ref(&self) -> Option<&Environment> { fn get_outer_environment_ref(&self) -> Option<&Environment> {
@ -195,11 +214,4 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
fn get_environment_type(&self) -> EnvironmentType { fn get_environment_type(&self) -> EnvironmentType {
EnvironmentType::Declarative EnvironmentType::Declarative
} }
fn get_global_object(&self) -> Option<Value> {
match &self.outer_env {
Some(outer) => outer.borrow().get_global_object(),
None => None,
}
}
} }

85
boa/src/environment/environment_record_trait.rs

@ -8,12 +8,12 @@
//! //!
//! There are 5 Environment record kinds. They all have methods in common, these are implemented as a the `EnvironmentRecordTrait` //! There are 5 Environment record kinds. They all have methods in common, these are implemented as a the `EnvironmentRecordTrait`
//! //!
use super::ErrorKind;
use crate::environment::lexical_environment::VariableScope; use crate::{environment::lexical_environment::VariableScope, object::GcObject};
use crate::{ use crate::{
environment::lexical_environment::{Environment, EnvironmentType}, environment::lexical_environment::{Environment, EnvironmentType},
gc::{Finalize, Trace}, gc::{Finalize, Trace},
Value, Context, Result, Value,
}; };
use std::fmt::Debug; use std::fmt::Debug;
@ -22,7 +22,8 @@ use std::fmt::Debug;
/// In the ECMAScript specification Environment Records are hierachical and have a base class with abstract methods. /// In the ECMAScript specification Environment Records are hierachical and have a base class with abstract methods.
/// In this implementation we have a trait which represents the behaviour of all `EnvironmentRecord` types. /// In this implementation we have a trait which represents the behaviour of all `EnvironmentRecord` types.
pub trait EnvironmentRecordTrait: Debug + Trace + Finalize { pub trait EnvironmentRecordTrait: Debug + Trace + Finalize {
/// Determine if an Environment Record has a binding for the String value N. Return true if it does and false if it does not. /// Determine if an Environment Record has a binding for the String value N.
/// Return true if it does and false if it does not.
fn has_binding(&self, name: &str) -> bool; fn has_binding(&self, name: &str) -> bool;
/// Create a new but uninitialized mutable binding in an Environment Record. The String value N is the text of the bound name. /// Create a new but uninitialized mutable binding in an Environment Record. The String value N is the text of the bound name.
@ -37,18 +38,25 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize {
name: String, name: String,
deletion: bool, deletion: bool,
allow_name_reuse: bool, allow_name_reuse: bool,
) -> Result<(), ErrorKind>; context: &mut Context,
) -> Result<()>;
/// Create a new but uninitialized immutable binding in an Environment Record. /// Create a new but uninitialized immutable binding in an Environment Record.
/// The String value N is the text of the bound name. /// The String value N is the text of the bound name.
/// If strict is true then attempts to set it after it has been initialized will always throw an exception, /// If strict is true then attempts to set it after it has been initialized will always throw an exception,
/// regardless of the strict mode setting of operations that reference that binding. /// regardless of the strict mode setting of operations that reference that binding.
fn create_immutable_binding(&mut self, name: String, strict: bool) -> Result<(), ErrorKind>; fn create_immutable_binding(
&mut self,
name: String,
strict: bool,
context: &mut Context,
) -> Result<()>;
/// Set the value of an already existing but uninitialized binding in an Environment Record. /// Set the value of an already existing but uninitialized binding in an Environment Record.
/// The String value N is the text of the bound name. /// The String value N is the text of the bound name.
/// V is the value for the binding and is a value of any ECMAScript language type. /// V is the value for the binding and is a value of any ECMAScript language type.
fn initialize_binding(&mut self, name: &str, value: Value) -> Result<(), ErrorKind>; fn initialize_binding(&mut self, name: &str, value: Value, context: &mut Context)
-> Result<()>;
/// Set the value of an already existing mutable binding in an Environment Record. /// Set the value of an already existing mutable binding in an Environment Record.
/// The String value `name` is the text of the bound name. /// The String value `name` is the text of the bound name.
@ -59,13 +67,14 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize {
name: &str, name: &str,
value: Value, value: Value,
strict: bool, strict: bool,
) -> Result<(), ErrorKind>; context: &mut Context,
) -> Result<()>;
/// Returns the value of an already existing binding from an Environment Record. /// Returns the value of an already existing binding from an Environment Record.
/// The String value N is the text of the bound name. /// The String value N is the text of the bound name.
/// S is used to identify references originating in strict mode code or that /// S is used to identify references originating in strict mode code or that
/// otherwise require strict mode reference semantics. /// otherwise require strict mode reference semantics.
fn get_binding_value(&self, name: &str, strict: bool) -> Result<Value, ErrorKind>; fn get_binding_value(&self, name: &str, strict: bool, context: &mut Context) -> Result<Value>;
/// Delete a binding from an Environment Record. /// Delete a binding from an Environment Record.
/// The String value name is the text of the bound name. /// The String value name is the text of the bound name.
@ -78,15 +87,15 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize {
fn has_this_binding(&self) -> bool; fn has_this_binding(&self) -> bool;
/// Return the `this` binding from the environment /// Return the `this` binding from the environment
fn get_this_binding(&self) -> Result<Value, ErrorKind>; fn get_this_binding(&self, context: &mut Context) -> Result<Value>;
/// Determine if an Environment Record establishes a super method binding. /// Determine if an Environment Record establishes a super method binding.
/// Return true if it does and false if it does not. /// Return true if it does and false if it does not.
fn has_super_binding(&self) -> bool; fn has_super_binding(&self) -> bool;
/// If this Environment Record is associated with a with statement, return the with object. /// If this Environment Record is associated with a with statement, return the with object.
/// Otherwise, return undefined. /// Otherwise, return None.
fn with_base_object(&self) -> Value; fn with_base_object(&self) -> Option<GcObject>;
/// Get the next environment up /// Get the next environment up
fn get_outer_environment_ref(&self) -> Option<&Environment>; fn get_outer_environment_ref(&self) -> Option<&Environment>;
@ -100,16 +109,13 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize {
/// Get the type of environment this is /// Get the type of environment this is
fn get_environment_type(&self) -> EnvironmentType; fn get_environment_type(&self) -> EnvironmentType;
/// Fetch global variable
fn get_global_object(&self) -> Option<Value>;
/// Return the `this` binding from the environment or try to get it from outer environments /// Return the `this` binding from the environment or try to get it from outer environments
fn recursive_get_this_binding(&self) -> Result<Value, ErrorKind> { fn recursive_get_this_binding(&self, context: &mut Context) -> Result<Value> {
if self.has_this_binding() { if self.has_this_binding() {
self.get_this_binding() self.get_this_binding(context)
} else { } else {
match self.get_outer_environment_ref() { match self.get_outer_environment_ref() {
Some(outer) => outer.borrow().recursive_get_this_binding(), Some(outer) => outer.borrow().recursive_get_this_binding(context),
None => Ok(Value::Undefined), None => Ok(Value::Undefined),
} }
} }
@ -121,14 +127,15 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize {
name: String, name: String,
deletion: bool, deletion: bool,
scope: VariableScope, scope: VariableScope,
) -> Result<(), ErrorKind> { context: &mut Context,
) -> Result<()> {
match scope { match scope {
VariableScope::Block => self.create_mutable_binding(name, deletion, false), VariableScope::Block => self.create_mutable_binding(name, deletion, false, context),
VariableScope::Function => self VariableScope::Function => self
.get_outer_environment_ref() .get_outer_environment_ref()
.expect("No function or global environment") .expect("No function or global environment")
.borrow_mut() .borrow_mut()
.recursive_create_mutable_binding(name, deletion, scope), .recursive_create_mutable_binding(name, deletion, scope, context),
} }
} }
@ -138,14 +145,15 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize {
name: String, name: String,
deletion: bool, deletion: bool,
scope: VariableScope, scope: VariableScope,
) -> Result<(), ErrorKind> { context: &mut Context,
) -> Result<()> {
match scope { match scope {
VariableScope::Block => self.create_immutable_binding(name, deletion), VariableScope::Block => self.create_immutable_binding(name, deletion, context),
VariableScope::Function => self VariableScope::Function => self
.get_outer_environment_ref() .get_outer_environment_ref()
.expect("No function or global environment") .expect("No function or global environment")
.borrow_mut() .borrow_mut()
.recursive_create_immutable_binding(name, deletion, scope), .recursive_create_immutable_binding(name, deletion, scope, context),
} }
} }
@ -155,26 +163,32 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize {
name: &str, name: &str,
value: Value, value: Value,
strict: bool, strict: bool,
) -> Result<(), ErrorKind> { context: &mut Context,
) -> Result<()> {
if self.has_binding(name) { if self.has_binding(name) {
self.set_mutable_binding(name, value, strict) self.set_mutable_binding(name, value, strict, context)
} else { } else {
self.get_outer_environment_ref() self.get_outer_environment_ref()
.expect("Environment stack underflow") .expect("Environment stack underflow")
.borrow_mut() .borrow_mut()
.recursive_set_mutable_binding(name, value, strict) .recursive_set_mutable_binding(name, value, strict, context)
} }
} }
/// Initialize binding while handling outer environments /// Initialize binding while handling outer environments
fn recursive_initialize_binding(&mut self, name: &str, value: Value) -> Result<(), ErrorKind> { fn recursive_initialize_binding(
&mut self,
name: &str,
value: Value,
context: &mut Context,
) -> Result<()> {
if self.has_binding(name) { if self.has_binding(name) {
self.initialize_binding(name, value) self.initialize_binding(name, value, context)
} else { } else {
self.get_outer_environment_ref() self.get_outer_environment_ref()
.expect("Environment stack underflow") .expect("Environment stack underflow")
.borrow_mut() .borrow_mut()
.recursive_initialize_binding(name, value) .recursive_initialize_binding(name, value, context)
} }
} }
@ -188,16 +202,13 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize {
} }
/// Retrieve binding from current or any outer environment /// Retrieve binding from current or any outer environment
fn recursive_get_binding_value(&self, name: &str) -> Result<Value, ErrorKind> { fn recursive_get_binding_value(&self, name: &str, context: &mut Context) -> Result<Value> {
if self.has_binding(name) { if self.has_binding(name) {
self.get_binding_value(name, false) self.get_binding_value(name, false, context)
} else { } else {
match self.get_outer_environment_ref() { match self.get_outer_environment_ref() {
Some(outer) => outer.borrow().recursive_get_binding_value(name), Some(outer) => outer.borrow().recursive_get_binding_value(name, context),
None => Err(ErrorKind::new_reference_error(format!( None => context.throw_reference_error(format!("{} is not defined", name)),
"{} is not defined",
name
))),
} }
} }
} }

103
boa/src/environment/function_environment_record.rs

@ -8,7 +8,9 @@
//! from within the function. //! from within the function.
//! More info: <https://tc39.es/ecma262/#sec-function-environment-records> //! More info: <https://tc39.es/ecma262/#sec-function-environment-records>
use super::ErrorKind; use gc::{Gc, GcCell};
use rustc_hash::FxHashMap;
use crate::{ use crate::{
environment::{ environment::{
declarative_environment_record::DeclarativeEnvironmentRecord, declarative_environment_record::DeclarativeEnvironmentRecord,
@ -17,7 +19,7 @@ use crate::{
}, },
gc::{empty_trace, Finalize, Trace}, gc::{empty_trace, Finalize, Trace},
object::GcObject, object::GcObject,
Value, Context, Result, Value,
}; };
/// Different binding status for `this`. /// Different binding status for `this`.
@ -57,17 +59,43 @@ pub struct FunctionEnvironmentRecord {
} }
impl FunctionEnvironmentRecord { impl FunctionEnvironmentRecord {
pub fn bind_this_value(&mut self, value: Value) -> Result<Value, ErrorKind> { #[allow(clippy::new_ret_no_self)]
pub fn new(
f: GcObject,
this: Option<Value>,
outer: Option<Environment>,
binding_status: BindingStatus,
new_target: Value,
) -> Environment {
let mut func_env = FunctionEnvironmentRecord {
declarative_record: DeclarativeEnvironmentRecord {
env_rec: FxHashMap::default(),
outer_env: outer, // this will come from Environment set as a private property of F - https://tc39.es/ecma262/#sec-ecmascript-function-objects
},
function: f,
this_binding_status: binding_status,
home_object: Value::undefined(),
new_target,
this_value: Value::undefined(),
};
// If a `this` value has been passed, bind it to the environment
if let Some(v) = this {
func_env.bind_this_value(v).unwrap();
}
Gc::new(GcCell::new(Box::new(func_env)))
}
pub fn bind_this_value(&mut self, value: Value) -> Result<Value> {
match self.this_binding_status { match self.this_binding_status {
// You can not bind an arrow function, their `this` value comes from the lexical scope above // You can not bind an arrow function, their `this` value comes from the lexical scope above
BindingStatus::Lexical => { BindingStatus::Lexical => {
panic!("Cannot bind to an arrow function!"); panic!("Cannot bind to an arrow function!");
} }
// You can not bind a function twice // You can not bind a function twice
BindingStatus::Initialized => Err(ErrorKind::new_reference_error( BindingStatus::Initialized => {
"Cannot bind to an initialised function!", todo!();
)), // context.throw_reference_error("Cannot bind to an initialised function!")
}
BindingStatus::Uninitialized => { BindingStatus::Uninitialized => {
self.this_value = value.clone(); self.this_value = value.clone();
self.this_binding_status = BindingStatus::Initialized; self.this_binding_status = BindingStatus::Initialized;
@ -99,18 +127,30 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
name: String, name: String,
deletion: bool, deletion: bool,
allow_name_reuse: bool, allow_name_reuse: bool,
) -> Result<(), ErrorKind> { context: &mut Context,
) -> Result<()> {
self.declarative_record self.declarative_record
.create_mutable_binding(name, deletion, allow_name_reuse) .create_mutable_binding(name, deletion, allow_name_reuse, context)
} }
fn create_immutable_binding(&mut self, name: String, strict: bool) -> Result<(), ErrorKind> { fn create_immutable_binding(
&mut self,
name: String,
strict: bool,
context: &mut Context,
) -> Result<()> {
self.declarative_record self.declarative_record
.create_immutable_binding(name, strict) .create_immutable_binding(name, strict, context)
} }
fn initialize_binding(&mut self, name: &str, value: Value) -> Result<(), ErrorKind> { fn initialize_binding(
self.declarative_record.initialize_binding(name, value) &mut self,
name: &str,
value: Value,
context: &mut Context,
) -> Result<()> {
self.declarative_record
.initialize_binding(name, value, context)
} }
fn set_mutable_binding( fn set_mutable_binding(
@ -118,13 +158,15 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
name: &str, name: &str,
value: Value, value: Value,
strict: bool, strict: bool,
) -> Result<(), ErrorKind> { context: &mut Context,
) -> Result<()> {
self.declarative_record self.declarative_record
.set_mutable_binding(name, value, strict) .set_mutable_binding(name, value, strict, context)
} }
fn get_binding_value(&self, name: &str, _strict: bool) -> Result<Value, ErrorKind> { fn get_binding_value(&self, name: &str, strict: bool, context: &mut Context) -> Result<Value> {
self.declarative_record.get_binding_value(name, _strict) self.declarative_record
.get_binding_value(name, strict, context)
} }
fn delete_binding(&mut self, name: &str) -> bool { fn delete_binding(&mut self, name: &str) -> bool {
@ -135,15 +177,14 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
!matches!(self.this_binding_status, BindingStatus::Lexical) !matches!(self.this_binding_status, BindingStatus::Lexical)
} }
fn get_this_binding(&self) -> Result<Value, ErrorKind> { fn get_this_binding(&self, context: &mut Context) -> Result<Value> {
match self.this_binding_status { match self.this_binding_status {
BindingStatus::Lexical => { BindingStatus::Lexical => {
panic!("There is no this for a lexical function record"); panic!("There is no this for a lexical function record");
} }
BindingStatus::Uninitialized => Err(ErrorKind::new_reference_error( BindingStatus::Uninitialized => {
"Uninitialised binding for this function", context.throw_reference_error("Uninitialised binding for this function")
)), }
BindingStatus::Initialized => Ok(self.this_value.clone()), BindingStatus::Initialized => Ok(self.this_value.clone()),
} }
} }
@ -156,8 +197,8 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
} }
} }
fn with_base_object(&self) -> Value { fn with_base_object(&self) -> Option<GcObject> {
Value::undefined() None
} }
fn get_outer_environment_ref(&self) -> Option<&Environment> { fn get_outer_environment_ref(&self) -> Option<&Environment> {
@ -172,17 +213,14 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
EnvironmentType::Function EnvironmentType::Function
} }
fn get_global_object(&self) -> Option<Value> {
self.declarative_record.get_global_object()
}
fn recursive_create_mutable_binding( fn recursive_create_mutable_binding(
&mut self, &mut self,
name: String, name: String,
deletion: bool, deletion: bool,
_scope: VariableScope, _scope: VariableScope,
) -> Result<(), ErrorKind> { context: &mut Context,
self.create_mutable_binding(name, deletion, false) ) -> Result<()> {
self.create_mutable_binding(name, deletion, false, context)
} }
fn recursive_create_immutable_binding( fn recursive_create_immutable_binding(
@ -190,7 +228,8 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
name: String, name: String,
deletion: bool, deletion: bool,
_scope: VariableScope, _scope: VariableScope,
) -> Result<(), ErrorKind> { context: &mut Context,
self.create_immutable_binding(name, deletion) ) -> Result<()> {
self.create_immutable_binding(name, deletion, context)
} }
} }

141
boa/src/environment/global_environment_record.rs

@ -7,7 +7,6 @@
//! that occur within a Script. //! that occur within a Script.
//! More info: <https://tc39.es/ecma262/#sec-global-environment-records> //! More info: <https://tc39.es/ecma262/#sec-global-environment-records>
use super::ErrorKind;
use crate::{ use crate::{
environment::{ environment::{
declarative_environment_record::DeclarativeEnvironmentRecord, declarative_environment_record::DeclarativeEnvironmentRecord,
@ -16,20 +15,48 @@ use crate::{
object_environment_record::ObjectEnvironmentRecord, object_environment_record::ObjectEnvironmentRecord,
}, },
gc::{Finalize, Trace}, gc::{Finalize, Trace},
object::GcObject,
property::{Attribute, DataDescriptor}, property::{Attribute, DataDescriptor},
Value, Context, Result, Value,
}; };
use rustc_hash::FxHashSet; use gc::{Gc, GcCell};
use rustc_hash::{FxHashMap, FxHashSet};
#[derive(Debug, Trace, Finalize, Clone)] #[derive(Debug, Trace, Finalize, Clone)]
pub struct GlobalEnvironmentRecord { pub struct GlobalEnvironmentRecord {
pub object_record: ObjectEnvironmentRecord, pub object_record: ObjectEnvironmentRecord,
pub global_this_binding: Value, pub global_this_binding: GcObject,
pub declarative_record: DeclarativeEnvironmentRecord, pub declarative_record: DeclarativeEnvironmentRecord,
pub var_names: FxHashSet<String>, pub var_names: FxHashSet<Box<str>>,
} }
impl GlobalEnvironmentRecord { impl GlobalEnvironmentRecord {
#[allow(clippy::new_ret_no_self)]
pub fn new(global: GcObject, this_value: GcObject) -> Environment {
let obj_rec = ObjectEnvironmentRecord {
bindings: global.into(),
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 = DeclarativeEnvironmentRecord {
env_rec: FxHashMap::default(),
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: FxHashSet::default(),
})))
}
pub fn has_var_declaration(&self, name: &str) -> bool { pub fn has_var_declaration(&self, name: &str) -> bool {
self.var_names.contains(name) self.var_names.contains(name)
} }
@ -80,19 +107,20 @@ impl GlobalEnvironmentRecord {
&mut self, &mut self,
name: String, name: String,
deletion: bool, deletion: bool,
) -> Result<(), ErrorKind> { context: &mut Context,
) -> Result<()> {
let obj_rec = &mut self.object_record; let obj_rec = &mut self.object_record;
let global_object = &obj_rec.bindings; let global_object = &obj_rec.bindings;
let has_property = global_object.has_field(name.as_str()); let has_property = global_object.has_field(name.as_str());
let extensible = global_object.is_extensible(); let extensible = global_object.is_extensible();
if !has_property && extensible { if !has_property && extensible {
obj_rec.create_mutable_binding(name.clone(), deletion, false)?; obj_rec.create_mutable_binding(name.clone(), deletion, false, context)?;
obj_rec.initialize_binding(&name, Value::undefined())?; obj_rec.initialize_binding(&name, Value::undefined(), context)?;
} }
let var_declared_names = &mut self.var_names; let var_declared_names = &mut self.var_names;
if !var_declared_names.contains(&name) { if !var_declared_names.contains(name.as_str()) {
var_declared_names.insert(name); var_declared_names.insert(name.into_boxed_str());
} }
Ok(()) Ok(())
} }
@ -132,40 +160,51 @@ impl EnvironmentRecordTrait for GlobalEnvironmentRecord {
name: String, name: String,
deletion: bool, deletion: bool,
allow_name_reuse: bool, allow_name_reuse: bool,
) -> Result<(), ErrorKind> { context: &mut Context,
) -> Result<()> {
if !allow_name_reuse && self.declarative_record.has_binding(&name) { if !allow_name_reuse && self.declarative_record.has_binding(&name) {
return Err(ErrorKind::new_type_error(format!( return Err(
"Binding already exists for {}", context.construct_type_error(format!("Binding already exists for {}", name))
name );
)));
} }
self.declarative_record self.declarative_record
.create_mutable_binding(name, deletion, allow_name_reuse) .create_mutable_binding(name, deletion, allow_name_reuse, context)
} }
fn create_immutable_binding(&mut self, name: String, strict: bool) -> Result<(), ErrorKind> { fn create_immutable_binding(
&mut self,
name: String,
strict: bool,
context: &mut Context,
) -> Result<()> {
if self.declarative_record.has_binding(&name) { if self.declarative_record.has_binding(&name) {
return Err(ErrorKind::new_type_error(format!( return Err(
"Binding already exists for {}", context.construct_type_error(format!("Binding already exists for {}", name))
name );
)));
} }
self.declarative_record self.declarative_record
.create_immutable_binding(name, strict) .create_immutable_binding(name, strict, context)
} }
fn initialize_binding(&mut self, name: &str, value: Value) -> Result<(), ErrorKind> { fn initialize_binding(
&mut self,
name: &str,
value: Value,
context: &mut Context,
) -> Result<()> {
if self.declarative_record.has_binding(&name) { if self.declarative_record.has_binding(&name) {
return self.declarative_record.initialize_binding(name, value); return self
.declarative_record
.initialize_binding(name, value, context);
} }
assert!( assert!(
self.object_record.has_binding(name), self.object_record.has_binding(name),
"Binding must be in object_record" "Binding must be in object_record"
); );
self.object_record.initialize_binding(name, value) self.object_record.initialize_binding(name, value, context)
} }
fn set_mutable_binding( fn set_mutable_binding(
@ -173,20 +212,24 @@ impl EnvironmentRecordTrait for GlobalEnvironmentRecord {
name: &str, name: &str,
value: Value, value: Value,
strict: bool, strict: bool,
) -> Result<(), ErrorKind> { context: &mut Context,
) -> Result<()> {
if self.declarative_record.has_binding(&name) { if self.declarative_record.has_binding(&name) {
return self return self
.declarative_record .declarative_record
.set_mutable_binding(name, value, strict); .set_mutable_binding(name, value, strict, context);
} }
self.object_record.set_mutable_binding(name, value, strict) self.object_record
.set_mutable_binding(name, value, strict, context)
} }
fn get_binding_value(&self, name: &str, strict: bool) -> Result<Value, ErrorKind> { fn get_binding_value(&self, name: &str, strict: bool, context: &mut Context) -> Result<Value> {
if self.declarative_record.has_binding(&name) { if self.declarative_record.has_binding(&name) {
return self.declarative_record.get_binding_value(name, strict); return self
.declarative_record
.get_binding_value(name, strict, context);
} }
self.object_record.get_binding_value(name, strict) self.object_record.get_binding_value(name, strict, context)
} }
fn delete_binding(&mut self, name: &str) -> bool { fn delete_binding(&mut self, name: &str) -> bool {
@ -212,16 +255,16 @@ impl EnvironmentRecordTrait for GlobalEnvironmentRecord {
true true
} }
fn get_this_binding(&self) -> Result<Value, ErrorKind> { fn get_this_binding(&self, _context: &mut Context) -> Result<Value> {
Ok(self.global_this_binding.clone()) Ok(self.global_this_binding.clone().into())
} }
fn has_super_binding(&self) -> bool { fn has_super_binding(&self) -> bool {
false false
} }
fn with_base_object(&self) -> Value { fn with_base_object(&self) -> Option<GcObject> {
Value::undefined() None
} }
fn get_outer_environment(&self) -> Option<Environment> { fn get_outer_environment(&self) -> Option<Environment> {
@ -241,17 +284,14 @@ impl EnvironmentRecordTrait for GlobalEnvironmentRecord {
EnvironmentType::Global EnvironmentType::Global
} }
fn get_global_object(&self) -> Option<Value> {
Some(self.global_this_binding.clone())
}
fn recursive_create_mutable_binding( fn recursive_create_mutable_binding(
&mut self, &mut self,
name: String, name: String,
deletion: bool, deletion: bool,
_scope: VariableScope, _scope: VariableScope,
) -> Result<(), ErrorKind> { context: &mut Context,
self.create_mutable_binding(name, deletion, false) ) -> Result<()> {
self.create_mutable_binding(name, deletion, false, context)
} }
fn recursive_create_immutable_binding( fn recursive_create_immutable_binding(
@ -259,8 +299,9 @@ impl EnvironmentRecordTrait for GlobalEnvironmentRecord {
name: String, name: String,
deletion: bool, deletion: bool,
_scope: VariableScope, _scope: VariableScope,
) -> Result<(), ErrorKind> { context: &mut Context,
self.create_immutable_binding(name, deletion) ) -> Result<()> {
self.create_immutable_binding(name, deletion, context)
} }
fn recursive_set_mutable_binding( fn recursive_set_mutable_binding(
@ -268,11 +309,17 @@ impl EnvironmentRecordTrait for GlobalEnvironmentRecord {
name: &str, name: &str,
value: Value, value: Value,
strict: bool, strict: bool,
) -> Result<(), ErrorKind> { context: &mut Context,
self.set_mutable_binding(name, value, strict) ) -> Result<()> {
self.set_mutable_binding(name, value, strict, context)
} }
fn recursive_initialize_binding(&mut self, name: &str, value: Value) -> Result<(), ErrorKind> { fn recursive_initialize_binding(
self.initialize_binding(name, value) &mut self,
name: &str,
value: Value,
context: &mut Context,
) -> Result<()> {
self.initialize_binding(name, value, context)
} }
} }

171
boa/src/environment/lexical_environment.rs

@ -5,20 +5,12 @@
//! The following operations are used to operate upon lexical environments //! The following operations are used to operate upon lexical environments
//! This is the entrypoint to lexical environments. //! This is the entrypoint to lexical environments.
use super::ErrorKind; use super::global_environment_record::GlobalEnvironmentRecord;
use crate::{ use crate::{
environment::{ environment::environment_record_trait::EnvironmentRecordTrait, object::GcObject, BoaProfiler,
declarative_environment_record::DeclarativeEnvironmentRecord, Context, Result, Value,
environment_record_trait::EnvironmentRecordTrait,
function_environment_record::{BindingStatus, FunctionEnvironmentRecord},
global_environment_record::GlobalEnvironmentRecord,
object_environment_record::ObjectEnvironmentRecord,
},
object::GcObject,
BoaProfiler, Value,
}; };
use gc::{Gc, GcCell}; use gc::{Gc, GcCell};
use rustc_hash::{FxHashMap, FxHashSet};
use std::{collections::VecDeque, error, fmt}; use std::{collections::VecDeque, error, fmt};
/// Environments are wrapped in a Box and then in a GC wrapper /// Environments are wrapped in a Box and then in a GC wrapper
@ -68,21 +60,12 @@ impl fmt::Display for EnvironmentError {
} }
} }
impl error::Error for EnvironmentError { impl error::Error for EnvironmentError {}
fn description(&self) -> &str {
&self.details
}
fn cause(&self) -> Option<&dyn error::Error> {
// Generic error, underlying cause isn't tracked.
None
}
}
impl LexicalEnvironment { impl LexicalEnvironment {
pub fn new(global: Value) -> Self { pub fn new(global: GcObject) -> Self {
let _timer = BoaProfiler::global().start_event("LexicalEnvironment::new", "env"); let _timer = BoaProfiler::global().start_event("LexicalEnvironment::new", "env");
let global_env = new_global_environment(global.clone(), global); let global_env = GlobalEnvironmentRecord::new(global.clone(), global);
let mut lexical_env = Self { let mut lexical_env = Self {
environment_stack: VecDeque::new(), environment_stack: VecDeque::new(),
}; };
@ -91,168 +74,86 @@ impl LexicalEnvironment {
lexical_env.environment_stack.push_back(global_env); lexical_env.environment_stack.push_back(global_env);
lexical_env lexical_env
} }
}
pub fn push(&mut self, env: Environment) { impl Context {
self.environment_stack.push_back(env); pub(crate) fn push_environment(&mut self, env: Environment) {
} self.realm.environment.environment_stack.push_back(env);
pub fn pop(&mut self) -> Option<Environment> {
self.environment_stack.pop_back()
} }
pub fn get_global_object(&self) -> Option<Value> { pub(crate) fn pop_environment(&mut self) -> Option<Environment> {
self.get_current_environment_ref() self.realm.environment.environment_stack.pop_back()
.borrow()
.get_global_object()
} }
pub fn get_this_binding(&self) -> Result<Value, ErrorKind> { pub(crate) fn get_this_binding(&mut self) -> Result<Value> {
self.get_current_environment_ref() self.get_current_environment()
.borrow() .borrow()
.recursive_get_this_binding() .recursive_get_this_binding(self)
} }
pub fn create_mutable_binding( pub(crate) fn create_mutable_binding(
&mut self, &mut self,
name: String, name: String,
deletion: bool, deletion: bool,
scope: VariableScope, scope: VariableScope,
) -> Result<(), ErrorKind> { ) -> Result<()> {
self.get_current_environment() self.get_current_environment()
.borrow_mut() .borrow_mut()
.recursive_create_mutable_binding(name, deletion, scope) .recursive_create_mutable_binding(name, deletion, scope, self)
} }
pub fn create_immutable_binding( pub(crate) fn create_immutable_binding(
&mut self, &mut self,
name: String, name: String,
deletion: bool, deletion: bool,
scope: VariableScope, scope: VariableScope,
) -> Result<(), ErrorKind> { ) -> Result<()> {
self.get_current_environment() self.get_current_environment()
.borrow_mut() .borrow_mut()
.recursive_create_immutable_binding(name, deletion, scope) .recursive_create_immutable_binding(name, deletion, scope, self)
} }
pub fn set_mutable_binding( pub(crate) fn set_mutable_binding(
&mut self, &mut self,
name: &str, name: &str,
value: Value, value: Value,
strict: bool, strict: bool,
) -> Result<(), ErrorKind> { ) -> Result<()> {
self.get_current_environment() self.get_current_environment()
.borrow_mut() .borrow_mut()
.recursive_set_mutable_binding(name, value, strict) .recursive_set_mutable_binding(name, value, strict, self)
} }
pub fn initialize_binding(&mut self, name: &str, value: Value) -> Result<(), ErrorKind> { pub(crate) fn initialize_binding(&mut self, name: &str, value: Value) -> Result<()> {
self.get_current_environment() self.get_current_environment()
.borrow_mut() .borrow_mut()
.recursive_initialize_binding(name, value) .recursive_initialize_binding(name, value, self)
}
/// get_current_environment_ref is used when you only need to borrow the environment
/// (you only need to add a new variable binding, or you want to fetch a value)
pub fn get_current_environment_ref(&self) -> &Environment {
self.environment_stack
.back()
.expect("Could not get current environment")
} }
/// When neededing to clone an environment (linking it with another environnment) /// When neededing to clone an environment (linking it with another environnment)
/// cloning is more suited. The GC will remove the env once nothing is linking to it anymore /// cloning is more suited. The GC will remove the env once nothing is linking to it anymore
pub fn get_current_environment(&mut self) -> &mut Environment { pub(crate) fn get_current_environment(&mut self) -> Environment {
self.environment_stack self.realm
.environment
.environment_stack
.back_mut() .back_mut()
.expect("Could not get mutable reference to back object") .expect("Could not get mutable reference to back object")
.clone()
} }
pub fn has_binding(&self, name: &str) -> bool { pub(crate) fn has_binding(&mut self, name: &str) -> bool {
self.get_current_environment_ref() self.get_current_environment()
.borrow() .borrow()
.recursive_has_binding(name) .recursive_has_binding(name)
} }
pub fn get_binding_value(&self, name: &str) -> Result<Value, ErrorKind> { pub(crate) fn get_binding_value(&mut self, name: &str) -> Result<Value> {
self.get_current_environment_ref() self.get_current_environment()
.borrow() .borrow()
.recursive_get_binding_value(name) .recursive_get_binding_value(name, self)
} }
} }
pub fn new_declarative_environment(env: Option<Environment>) -> Environment {
let _timer = BoaProfiler::global().start_event("new_declarative_environment", "env");
let boxed_env = Box::new(DeclarativeEnvironmentRecord {
env_rec: FxHashMap::default(),
outer_env: env,
});
Gc::new(GcCell::new(boxed_env))
}
pub fn new_function_environment(
f: GcObject,
this: Option<Value>,
outer: Option<Environment>,
binding_status: BindingStatus,
new_target: Value,
) -> Environment {
let mut func_env = FunctionEnvironmentRecord {
declarative_record: DeclarativeEnvironmentRecord {
env_rec: FxHashMap::default(),
outer_env: outer, // this will come from Environment set as a private property of F - https://tc39.es/ecma262/#sec-ecmascript-function-objects
},
function: f,
this_binding_status: binding_status,
home_object: Value::undefined(),
new_target,
this_value: Value::undefined(),
};
// If a `this` value has been passed, bind it to the environment
if let Some(v) = this {
func_env.bind_this_value(v).unwrap();
}
Gc::new(GcCell::new(Box::new(func_env)))
}
pub fn new_object_environment(object: Value, environment: Option<Environment>) -> Environment {
Gc::new(GcCell::new(Box::new(ObjectEnvironmentRecord {
bindings: object,
outer_env: environment,
/// 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,
})))
}
pub fn new_global_environment(global: Value, this_value: Value) -> Environment {
let obj_rec = 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 = DeclarativeEnvironmentRecord {
env_rec: FxHashMap::default(),
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: FxHashSet::default(),
})))
}
#[cfg(test)] #[cfg(test)]
mod tests { mod tests {
use crate::exec; use crate::exec;

32
boa/src/environment/mod.rs

@ -6,35 +6,3 @@ pub mod function_environment_record;
pub mod global_environment_record; pub mod global_environment_record;
pub mod lexical_environment; pub mod lexical_environment;
pub mod object_environment_record; pub mod object_environment_record;
#[derive(Debug)]
pub enum ErrorKind {
ReferenceError(Box<str>),
TypeError(Box<str>),
}
use crate::value::Value;
use crate::Context;
impl ErrorKind {
pub fn to_error(&self, ctx: &mut Context) -> Value {
match self {
ErrorKind::ReferenceError(msg) => ctx.construct_reference_error(msg.clone()),
ErrorKind::TypeError(msg) => ctx.construct_type_error(msg.clone()),
}
}
pub fn new_reference_error<M>(msg: M) -> Self
where
M: Into<Box<str>>,
{
Self::ReferenceError(msg.into())
}
pub fn new_type_error<M>(msg: M) -> Self
where
M: Into<Box<str>>,
{
Self::TypeError(msg.into())
}
}

70
boa/src/environment/object_environment_record.rs

@ -6,25 +6,44 @@
//! Property keys that are not strings in the form of an `IdentifierName` are not included in the set of bound identifiers. //! Property keys that are not strings in the form of an `IdentifierName` are not included in the set of bound identifiers.
//! More info: [Object Records](https://tc39.es/ecma262/#sec-object-environment-records) //! More info: [Object Records](https://tc39.es/ecma262/#sec-object-environment-records)
use super::*; use gc::{Gc, GcCell};
use crate::property::PropertyDescriptor;
use crate::{ use crate::{
environment::{ environment::{
environment_record_trait::EnvironmentRecordTrait, environment_record_trait::EnvironmentRecordTrait,
lexical_environment::{Environment, EnvironmentType}, lexical_environment::{Environment, EnvironmentType},
}, },
gc::{Finalize, Trace}, gc::{Finalize, Trace},
object::GcObject,
property::PropertyDescriptor,
property::{Attribute, DataDescriptor}, property::{Attribute, DataDescriptor},
Value, Context, Result, Value,
}; };
#[derive(Debug, Trace, Finalize, Clone)] #[derive(Debug, Trace, Finalize, Clone)]
pub struct ObjectEnvironmentRecord { pub struct ObjectEnvironmentRecord {
// TODO: bindings should be an object.
pub bindings: Value, pub bindings: Value,
pub with_environment: bool, pub with_environment: bool,
pub outer_env: Option<Environment>, pub outer_env: Option<Environment>,
} }
impl ObjectEnvironmentRecord {
#[allow(clippy::new_ret_no_self)]
pub fn new(object: Value, environment: Option<Environment>) -> Environment {
Gc::new(GcCell::new(Box::new(ObjectEnvironmentRecord {
bindings: object,
outer_env: environment,
/// 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,
})))
}
}
impl EnvironmentRecordTrait for ObjectEnvironmentRecord { impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
fn has_binding(&self, name: &str) -> bool { fn has_binding(&self, name: &str) -> bool {
if self.bindings.has_field(name) { if self.bindings.has_field(name) {
@ -42,7 +61,8 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
name: String, name: String,
deletion: bool, deletion: bool,
_allow_name_reuse: bool, _allow_name_reuse: bool,
) -> Result<(), ErrorKind> { _context: &mut Context,
) -> Result<()> {
// TODO: could save time here and not bother generating a new undefined object, // TODO: could save time here and not bother generating a new undefined object,
// only for it to be replace with the real value later. We could just add the name to a Vector instead // only for it to be replace with the real value later. We could just add the name to a Vector instead
let bindings = &mut self.bindings; let bindings = &mut self.bindings;
@ -56,16 +76,26 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
Ok(()) Ok(())
} }
fn create_immutable_binding(&mut self, _name: String, _strict: bool) -> Result<(), ErrorKind> { fn create_immutable_binding(
&mut self,
_name: String,
_strict: bool,
_context: &mut Context,
) -> Result<()> {
Ok(()) Ok(())
} }
fn initialize_binding(&mut self, name: &str, value: Value) -> Result<(), ErrorKind> { fn initialize_binding(
&mut self,
name: &str,
value: Value,
context: &mut Context,
) -> Result<()> {
// We should never need to check if a binding has been created, // We should never need to check if a binding has been created,
// As all calls to create_mutable_binding are followed by initialized binding // As all calls to create_mutable_binding are followed by initialized binding
// The below is just a check. // The below is just a check.
debug_assert!(self.has_binding(&name)); debug_assert!(self.has_binding(&name));
self.set_mutable_binding(name, value, false) self.set_mutable_binding(name, value, false, context)
} }
fn set_mutable_binding( fn set_mutable_binding(
@ -73,7 +103,8 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
name: &str, name: &str,
value: Value, value: Value,
strict: bool, strict: bool,
) -> Result<(), ErrorKind> { _context: &mut Context,
) -> Result<()> {
debug_assert!(value.is_object() || value.is_function()); debug_assert!(value.is_object() || value.is_function());
let mut property = DataDescriptor::new(value, Attribute::ENUMERABLE); let mut property = DataDescriptor::new(value, Attribute::ENUMERABLE);
property.set_configurable(strict); property.set_configurable(strict);
@ -84,17 +115,14 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
Ok(()) Ok(())
} }
fn get_binding_value(&self, name: &str, strict: bool) -> Result<Value, ErrorKind> { fn get_binding_value(&self, name: &str, strict: bool, context: &mut Context) -> Result<Value> {
if self.bindings.has_field(name) { if self.bindings.has_field(name) {
match self.bindings.get_property(name) { match self.bindings.get_property(name) {
Some(PropertyDescriptor::Data(ref d)) => Ok(d.value()), Some(PropertyDescriptor::Data(ref d)) => Ok(d.value()),
_ => Ok(Value::undefined()), _ => Ok(Value::undefined()),
} }
} else if strict { } else if strict {
Err(ErrorKind::new_reference_error(format!( context.throw_reference_error(format!("{} has no binding", name))
"{} has no binding",
name
)))
} else { } else {
Ok(Value::undefined()) Ok(Value::undefined())
} }
@ -109,7 +137,7 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
false false
} }
fn get_this_binding(&self) -> Result<Value, ErrorKind> { fn get_this_binding(&self, _context: &mut Context) -> Result<Value> {
Ok(Value::undefined()) Ok(Value::undefined())
} }
@ -117,14 +145,14 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
false false
} }
fn with_base_object(&self) -> Value { fn with_base_object(&self) -> Option<GcObject> {
// Object Environment Records return undefined as their // Object Environment Records return undefined as their
// WithBaseObject unless their withEnvironment flag is true. // WithBaseObject unless their withEnvironment flag is true.
if self.with_environment { if self.with_environment {
return self.bindings.clone(); return Some(self.bindings.as_object().unwrap());
} }
Value::undefined() None
} }
fn get_outer_environment_ref(&self) -> Option<&Environment> { fn get_outer_environment_ref(&self) -> Option<&Environment> {
@ -138,12 +166,4 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
fn get_environment_type(&self) -> EnvironmentType { fn get_environment_type(&self) -> EnvironmentType {
EnvironmentType::Function EnvironmentType::Function
} }
fn get_global_object(&self) -> Option<Value> {
if let Some(outer) = &self.outer_env {
outer.borrow().get_global_object()
} else {
None
}
}
} }

1
boa/src/gc.rs

@ -4,7 +4,6 @@
// when it should be `empty_trace`. // when it should be `empty_trace`.
#![allow(clippy::unsafe_removed_from_name)] #![allow(clippy::unsafe_removed_from_name)]
pub use crate::object::GcObject;
pub use gc::{ pub use gc::{
custom_trace, force_collect, unsafe_empty_trace as empty_trace, Finalize, GcCellRef as Ref, custom_trace, force_collect, unsafe_empty_trace as empty_trace, Finalize, GcCellRef as Ref,
GcCellRefMut as RefMut, Trace, GcCellRefMut as RefMut, Trace,

67
boa/src/object/gcobject.rs

@ -7,9 +7,7 @@ use crate::{
builtins::function::{ builtins::function::{
create_unmapped_arguments_object, BuiltInFunction, Function, NativeFunction, create_unmapped_arguments_object, BuiltInFunction, Function, NativeFunction,
}, },
environment::{ environment::function_environment_record::{BindingStatus, FunctionEnvironmentRecord},
function_environment_record::BindingStatus, lexical_environment::new_function_environment,
},
property::{AccessorDescriptor, Attribute, DataDescriptor, PropertyDescriptor, PropertyKey}, property::{AccessorDescriptor, Attribute, DataDescriptor, PropertyDescriptor, PropertyKey},
syntax::ast::node::RcStatementList, syntax::ast::node::RcStatementList,
value::PreferredType, value::PreferredType,
@ -135,7 +133,7 @@ impl GcObject {
} => { } => {
// Create a new Function environment whose parent is set to the scope of the function declaration (self.environment) // Create a new Function environment whose parent is set to the scope of the function declaration (self.environment)
// <https://tc39.es/ecma262/#sec-prepareforordinarycall> // <https://tc39.es/ecma262/#sec-prepareforordinarycall>
let local_env = new_function_environment( let local_env = FunctionEnvironmentRecord::new(
this_function_object, this_function_object,
if flags.is_lexical_this_mode() { if flags.is_lexical_this_mode() {
None None
@ -161,21 +159,25 @@ impl GcObject {
} }
let value = args.get(i).cloned().unwrap_or_else(Value::undefined); let value = args.get(i).cloned().unwrap_or_else(Value::undefined);
function.add_arguments_to_environment(param, value, &local_env); function
.add_arguments_to_environment(param, value, &local_env, context);
} }
// Add arguments object // Add arguments object
let arguments_obj = create_unmapped_arguments_object(args); let arguments_obj = create_unmapped_arguments_object(args);
local_env local_env.borrow_mut().create_mutable_binding(
.borrow_mut() "arguments".to_string(),
.create_mutable_binding("arguments".to_string(), false, true) false,
.map_err(|e| e.to_error(context))?; true,
local_env context,
.borrow_mut() )?;
.initialize_binding("arguments", arguments_obj) local_env.borrow_mut().initialize_binding(
.map_err(|e| e.to_error(context))?; "arguments",
arguments_obj,
context,
)?;
context.realm_mut().environment.push(local_env); context.push_environment(local_env);
FunctionBody::Ordinary(body.clone()) FunctionBody::Ordinary(body.clone())
} }
@ -192,7 +194,7 @@ impl GcObject {
FunctionBody::BuiltInConstructor(func) => func(&Value::undefined(), args, context), FunctionBody::BuiltInConstructor(func) => func(&Value::undefined(), args, context),
FunctionBody::Ordinary(body) => { FunctionBody::Ordinary(body) => {
let result = body.run(context); let result = body.run(context);
context.realm_mut().environment.pop(); context.pop_environment();
result result
} }
@ -247,7 +249,7 @@ impl GcObject {
// Create a new Function environment who's parent is set to the scope of the function declaration (self.environment) // Create a new Function environment who's parent is set to the scope of the function declaration (self.environment)
// <https://tc39.es/ecma262/#sec-prepareforordinarycall> // <https://tc39.es/ecma262/#sec-prepareforordinarycall>
let local_env = new_function_environment( let local_env = FunctionEnvironmentRecord::new(
this_function_object, this_function_object,
Some(this), Some(this),
Some(environment.clone()), Some(environment.clone()),
@ -269,21 +271,24 @@ impl GcObject {
} }
let value = args.get(i).cloned().unwrap_or_else(Value::undefined); let value = args.get(i).cloned().unwrap_or_else(Value::undefined);
function.add_arguments_to_environment(param, value, &local_env); function
.add_arguments_to_environment(param, value, &local_env, context);
} }
// Add arguments object // Add arguments object
let arguments_obj = create_unmapped_arguments_object(args); let arguments_obj = create_unmapped_arguments_object(args);
local_env local_env.borrow_mut().create_mutable_binding(
.borrow_mut() "arguments".to_string(),
.create_mutable_binding("arguments".to_string(), false, true) false,
.map_err(|e| e.to_error(context))?; true,
local_env context,
.borrow_mut() )?;
.initialize_binding("arguments", arguments_obj) local_env.borrow_mut().initialize_binding(
.map_err(|e| e.to_error(context))?; "arguments",
arguments_obj,
context.realm_mut().environment.push(local_env); context,
)?;
context.push_environment(local_env);
FunctionBody::Ordinary(body.clone()) FunctionBody::Ordinary(body.clone())
} }
@ -305,12 +310,8 @@ impl GcObject {
let _ = body.run(context); let _ = body.run(context);
// local_env gets dropped here, its no longer needed // local_env gets dropped here, its no longer needed
let result = context let result = context.get_this_binding();
.realm_mut() context.pop_environment();
.environment
.get_this_binding()
.map_err(|e| e.to_error(context));
context.realm_mut().environment.pop();
result result
} }
FunctionBody::BuiltInFunction(_) => unreachable!("Cannot have a function in construct"), FunctionBody::BuiltInFunction(_) => unreachable!("Cannot have a function in construct"),

12
boa/src/realm.rs

@ -4,7 +4,6 @@
//! //!
//! A realm is represented in this implementation as a Realm struct with the fields specified from the spec. //! A realm is represented in this implementation as a Realm struct with the fields specified from the spec.
use crate::object::{GcObject, Object, ObjectData};
use crate::{ use crate::{
environment::{ environment::{
declarative_environment_record::DeclarativeEnvironmentRecord, declarative_environment_record::DeclarativeEnvironmentRecord,
@ -12,7 +11,8 @@ use crate::{
lexical_environment::LexicalEnvironment, lexical_environment::LexicalEnvironment,
object_environment_record::ObjectEnvironmentRecord, object_environment_record::ObjectEnvironmentRecord,
}, },
BoaProfiler, Value, object::{GcObject, Object, ObjectData},
BoaProfiler,
}; };
use gc::{Gc, GcCell}; use gc::{Gc, GcCell};
use rustc_hash::{FxHashMap, FxHashSet}; use rustc_hash::{FxHashMap, FxHashSet};
@ -41,12 +41,12 @@ impl Realm {
let gc_global = GcObject::new(global); let gc_global = GcObject::new(global);
// We need to clone the global here because its referenced from separate places (only pointer is cloned) // We need to clone the global here because its referenced from separate places (only pointer is cloned)
let global_env = new_global_environment(gc_global.clone(), gc_global.clone().into()); let global_env = new_global_environment(gc_global.clone(), gc_global.clone());
Self { Self {
global_object: gc_global.clone(), global_object: gc_global.clone(),
global_env, global_env,
environment: LexicalEnvironment::new(gc_global.into()), environment: LexicalEnvironment::new(gc_global),
} }
} }
} }
@ -54,10 +54,10 @@ impl Realm {
// Similar to new_global_environment in lexical_environment, except we need to return a GlobalEnvirionment // Similar to new_global_environment in lexical_environment, except we need to return a GlobalEnvirionment
fn new_global_environment( fn new_global_environment(
global: GcObject, global: GcObject,
this_value: Value, this_value: GcObject,
) -> Gc<GcCell<GlobalEnvironmentRecord>> { ) -> Gc<GcCell<GlobalEnvironmentRecord>> {
let obj_rec = ObjectEnvironmentRecord { let obj_rec = ObjectEnvironmentRecord {
bindings: Value::Object(global), bindings: global.into(),
outer_env: None, outer_env: None,
/// Object Environment Records created for with statements (13.11) /// Object Environment Records created for with statements (13.11)
/// can provide their binding object as an implicit this value for use in function calls. /// can provide their binding object as an implicit this value for use in function calls.

12
boa/src/syntax/ast/node/block/mod.rs

@ -2,7 +2,7 @@
use super::{Node, StatementList}; use super::{Node, StatementList};
use crate::{ use crate::{
environment::lexical_environment::new_declarative_environment, environment::declarative_environment_record::DeclarativeEnvironmentRecord,
exec::Executable, exec::Executable,
exec::InterpreterState, exec::InterpreterState,
gc::{Finalize, Trace}, gc::{Finalize, Trace},
@ -54,10 +54,8 @@ impl Executable for Block {
fn run(&self, context: &mut Context) -> Result<Value> { fn run(&self, context: &mut Context) -> Result<Value> {
let _timer = BoaProfiler::global().start_event("Block", "exec"); let _timer = BoaProfiler::global().start_event("Block", "exec");
{ {
let env = &mut context.realm_mut().environment; let env = context.get_current_environment();
env.push(new_declarative_environment(Some( context.push_environment(DeclarativeEnvironmentRecord::new(Some(env)));
env.get_current_environment_ref().clone(),
)));
} }
// https://tc39.es/ecma262/#sec-block-runtime-semantics-evaluation // https://tc39.es/ecma262/#sec-block-runtime-semantics-evaluation
@ -67,7 +65,7 @@ impl Executable for Block {
obj = statement.run(context).map_err(|e| { obj = statement.run(context).map_err(|e| {
// No matter how control leaves the Block the LexicalEnvironment is always // No matter how control leaves the Block the LexicalEnvironment is always
// restored to its former state. // restored to its former state.
context.realm_mut().environment.pop(); context.pop_environment();
e e
})?; })?;
@ -93,7 +91,7 @@ impl Executable for Block {
} }
// pop the block env // pop the block env
let _ = context.realm_mut().environment.pop(); let _ = context.pop_environment();
Ok(obj) Ok(obj)
} }

2
boa/src/syntax/ast/node/call/mod.rs

@ -84,7 +84,7 @@ impl Executable for Call {
} }
_ => ( _ => (
// 'this' binding should come from the function's self-contained environment // 'this' binding should come from the function's self-contained environment
context.global_object().clone().into(), context.global_object().into(),
self.expr().run(context)?, self.expr().run(context)?,
), ),
}; };

21
boa/src/syntax/ast/node/declaration/function_decl/mod.rs

@ -95,21 +95,16 @@ impl Executable for FunctionDecl {
// Set the name and assign it in the current environment // Set the name and assign it in the current environment
val.set_field("name", self.name(), context)?; val.set_field("name", self.name(), context)?;
let environment = &mut context.realm_mut().environment; if context.has_binding(self.name()) {
if environment.has_binding(self.name()) { context.set_mutable_binding(self.name(), val, true)?;
environment
.set_mutable_binding(self.name(), val, true)
.map_err(|e| e.to_error(context))?;
} else { } else {
environment context.create_mutable_binding(
.create_mutable_binding(self.name().to_owned(), false, VariableScope::Function) self.name().to_owned(),
.map_err(|e| e.to_error(context))?; false,
VariableScope::Function,
)?;
context context.initialize_binding(self.name(), val)?;
.realm_mut()
.environment
.initialize_binding(self.name(), val)
.map_err(|e| e.to_error(context))?;
} }
Ok(Value::undefined()) Ok(Value::undefined())
} }

38
boa/src/syntax/ast/node/declaration/mod.rs

@ -101,33 +101,31 @@ impl Executable for DeclarationList {
None => Value::undefined(), None => Value::undefined(),
}; };
let environment = &mut context.realm_mut().environment; if self.is_var() && context.has_binding(decl.name()) {
if self.is_var() && environment.has_binding(decl.name()) {
if decl.init().is_some() { if decl.init().is_some() {
environment context.set_mutable_binding(decl.name(), val, true)?;
.set_mutable_binding(decl.name(), val, true)
.map_err(|e| e.to_error(context))?;
} }
continue; continue;
} }
match &self { match &self {
Const(_) => environment Const(_) => context.create_immutable_binding(
.create_immutable_binding(decl.name().to_owned(), false, VariableScope::Block) decl.name().to_owned(),
.map_err(|e| e.to_error(context))?, false,
Let(_) => environment VariableScope::Block,
.create_mutable_binding(decl.name().to_owned(), false, VariableScope::Block) )?,
.map_err(|e| e.to_error(context))?, Let(_) => context.create_mutable_binding(
Var(_) => environment decl.name().to_owned(),
.create_mutable_binding(decl.name().to_owned(), false, VariableScope::Function) false,
.map_err(|e| e.to_error(context))?, VariableScope::Block,
)?,
Var(_) => context.create_mutable_binding(
decl.name().to_owned(),
false,
VariableScope::Function,
)?,
} }
context context.initialize_binding(decl.name(), val)?;
.realm_mut()
.environment
.initialize_binding(decl.name(), val)
.map_err(|e| e.to_error(context))?;
} }
Ok(Value::undefined()) Ok(Value::undefined())

6
boa/src/syntax/ast/node/identifier/mod.rs

@ -36,11 +36,7 @@ pub struct Identifier {
impl Executable for Identifier { impl Executable for Identifier {
fn run(&self, context: &mut Context) -> Result<Value> { fn run(&self, context: &mut Context) -> Result<Value> {
context context.get_binding_value(self.as_ref())
.realm()
.environment
.get_binding_value(self.as_ref())
.map_err(|e| e.to_error(context))
} }
} }

103
boa/src/syntax/ast/node/iteration/for_in_loop/mod.rs

@ -1,6 +1,9 @@
use crate::{ use crate::{
builtins::{iterable::IteratorRecord, ForInIterator}, builtins::{iterable::IteratorRecord, ForInIterator},
environment::lexical_environment::{new_declarative_environment, VariableScope}, environment::{
declarative_environment_record::DeclarativeEnvironmentRecord,
lexical_environment::VariableScope,
},
exec::{Executable, InterpreterState}, exec::{Executable, InterpreterState},
gc::{Finalize, Trace}, gc::{Finalize, Trace},
syntax::ast::node::Node, syntax::ast::node::Node,
@ -93,65 +96,45 @@ impl Executable for ForInLoop {
loop { loop {
{ {
let env = &mut context.realm_mut().environment; let env = context.get_current_environment();
env.push(new_declarative_environment(Some( context.push_environment(DeclarativeEnvironmentRecord::new(Some(env)));
env.get_current_environment_ref().clone(),
)));
} }
let iterator_result = iterator.next(context)?; let iterator_result = iterator.next(context)?;
if iterator_result.is_done() { if iterator_result.is_done() {
context.realm_mut().environment.pop(); context.pop_environment();
break; break;
} }
let next_result = iterator_result.value(); let next_result = iterator_result.value();
match self.variable() { match self.variable() {
Node::Identifier(ref name) => { Node::Identifier(ref name) => {
let environment = &mut context.realm_mut().environment; if context.has_binding(name.as_ref()) {
if environment.has_binding(name.as_ref()) {
// Binding already exists // Binding already exists
environment context.set_mutable_binding(name.as_ref(), next_result.clone(), true)?;
.set_mutable_binding(name.as_ref(), next_result.clone(), true)
.map_err(|e| e.to_error(context))?;
} else { } else {
environment context.create_mutable_binding(
.create_mutable_binding( name.as_ref().to_owned(),
name.as_ref().to_owned(), true,
true, VariableScope::Function,
VariableScope::Function, )?;
) context.initialize_binding(name.as_ref(), next_result.clone())?;
.map_err(|e| e.to_error(context))?;
let environment = &mut context.realm_mut().environment;
environment
.initialize_binding(name.as_ref(), next_result.clone())
.map_err(|e| e.to_error(context))?;
} }
} }
Node::VarDeclList(ref list) => match list.as_ref() { Node::VarDeclList(ref list) => match list.as_ref() {
[var] => { [var] => {
let environment = &mut context.realm_mut().environment;
if var.init().is_some() { if var.init().is_some() {
return context.throw_syntax_error("a declaration in the head of a for-in loop can't have an initializer"); return context.throw_syntax_error("a declaration in the head of a for-in loop can't have an initializer");
} }
if environment.has_binding(var.name()) { if context.has_binding(var.name()) {
environment context.set_mutable_binding(var.name(), next_result, true)?;
.set_mutable_binding(var.name(), next_result, true)
.map_err(|e| e.to_error(context))?;
} else { } else {
environment context.create_mutable_binding(
.create_mutable_binding( var.name().to_owned(),
var.name().to_owned(), false,
false, VariableScope::Function,
VariableScope::Function, )?;
) context.initialize_binding(var.name(), next_result)?;
.map_err(|e| e.to_error(context))?;
let environment = &mut context.realm_mut().environment;
environment
.initialize_binding(var.name(), next_result)
.map_err(|e| e.to_error(context))?;
} }
} }
_ => { _ => {
@ -162,23 +145,16 @@ impl Executable for ForInLoop {
}, },
Node::LetDeclList(ref list) => match list.as_ref() { Node::LetDeclList(ref list) => match list.as_ref() {
[var] => { [var] => {
let environment = &mut context.realm_mut().environment;
if var.init().is_some() { if var.init().is_some() {
return context.throw_syntax_error("a declaration in the head of a for-in loop can't have an initializer"); return context.throw_syntax_error("a declaration in the head of a for-in loop can't have an initializer");
} }
environment context.create_mutable_binding(
.create_mutable_binding( var.name().to_owned(),
var.name().to_owned(), false,
false, VariableScope::Block,
VariableScope::Block, )?;
) context.initialize_binding(var.name(), next_result)?;
.map_err(|e| e.to_error(context))?;
let environment = &mut context.realm_mut().environment;
environment
.initialize_binding(var.name(), next_result)
.map_err(|e| e.to_error(context))?;
} }
_ => { _ => {
return context.throw_syntax_error( return context.throw_syntax_error(
@ -188,23 +164,16 @@ impl Executable for ForInLoop {
}, },
Node::ConstDeclList(ref list) => match list.as_ref() { Node::ConstDeclList(ref list) => match list.as_ref() {
[var] => { [var] => {
let environment = &mut context.realm_mut().environment;
if var.init().is_some() { if var.init().is_some() {
return context.throw_syntax_error("a declaration in the head of a for-in loop can't have an initializer"); return context.throw_syntax_error("a declaration in the head of a for-in loop can't have an initializer");
} }
environment context.create_immutable_binding(
.create_immutable_binding( var.name().to_owned(),
var.name().to_owned(), false,
false, VariableScope::Block,
VariableScope::Block, )?;
) context.initialize_binding(var.name(), next_result)?;
.map_err(|e| e.to_error(context))?;
let environment = &mut context.realm_mut().environment;
environment
.initialize_binding(var.name(), next_result)
.map_err(|e| e.to_error(context))?;
} }
_ => { _ => {
return context.throw_syntax_error( return context.throw_syntax_error(
@ -237,7 +206,7 @@ impl Executable for ForInLoop {
// Continue execution. // Continue execution.
} }
} }
let _ = context.realm_mut().environment.pop(); let _ = context.pop_environment();
} }
Ok(result) Ok(result)
} }

10
boa/src/syntax/ast/node/iteration/for_loop/mod.rs

@ -1,5 +1,5 @@
use crate::{ use crate::{
environment::lexical_environment::new_declarative_environment, environment::declarative_environment_record::DeclarativeEnvironmentRecord,
exec::{Executable, InterpreterState}, exec::{Executable, InterpreterState},
gc::{Finalize, Trace}, gc::{Finalize, Trace},
syntax::ast::node::Node, syntax::ast::node::Node,
@ -102,10 +102,8 @@ impl Executable for ForLoop {
// Create the block environment. // Create the block environment.
let _timer = BoaProfiler::global().start_event("ForLoop", "exec"); let _timer = BoaProfiler::global().start_event("ForLoop", "exec");
{ {
let env = &mut context.realm_mut().environment; let env = context.get_current_environment();
env.push(new_declarative_environment(Some( context.push_environment(DeclarativeEnvironmentRecord::new(Some(env)));
env.get_current_environment_ref().clone(),
)));
} }
if let Some(init) = self.init() { if let Some(init) = self.init() {
@ -143,7 +141,7 @@ impl Executable for ForLoop {
} }
// pop the block env // pop the block env
let _ = context.realm_mut().environment.pop(); let _ = context.pop_environment();
Ok(Value::undefined()) Ok(Value::undefined())
} }

105
boa/src/syntax/ast/node/iteration/for_of_loop/mod.rs

@ -1,6 +1,9 @@
use crate::{ use crate::{
builtins::iterable::get_iterator, builtins::iterable::get_iterator,
environment::lexical_environment::{new_declarative_environment, VariableScope}, environment::{
declarative_environment_record::DeclarativeEnvironmentRecord,
lexical_environment::VariableScope,
},
exec::{Executable, InterpreterState}, exec::{Executable, InterpreterState},
gc::{Finalize, Trace}, gc::{Finalize, Trace},
syntax::ast::node::Node, syntax::ast::node::Node,
@ -83,65 +86,45 @@ impl Executable for ForOfLoop {
loop { loop {
{ {
let env = &mut context.realm_mut().environment; let env = context.get_current_environment();
env.push(new_declarative_environment(Some( context.push_environment(DeclarativeEnvironmentRecord::new(Some(env)));
env.get_current_environment_ref().clone(),
)));
} }
let iterator_result = iterator.next(context)?; let iterator_result = iterator.next(context)?;
if iterator_result.is_done() { if iterator_result.is_done() {
context.realm_mut().environment.pop(); context.pop_environment();
break; break;
} }
let next_result = iterator_result.value(); let next_result = iterator_result.value();
match self.variable() { match self.variable() {
Node::Identifier(ref name) => { Node::Identifier(ref name) => {
let environment = &mut context.realm_mut().environment; if context.has_binding(name.as_ref()) {
if environment.has_binding(name.as_ref()) {
// Binding already exists // Binding already exists
environment context.set_mutable_binding(name.as_ref(), next_result.clone(), true)?;
.set_mutable_binding(name.as_ref(), next_result.clone(), true)
.map_err(|e| e.to_error(context))?;
} else { } else {
environment context.create_mutable_binding(
.create_mutable_binding( name.as_ref().to_owned(),
name.as_ref().to_owned(), true,
true, VariableScope::Function,
VariableScope::Function, )?;
) context.initialize_binding(name.as_ref(), next_result.clone())?;
.map_err(|e| e.to_error(context))?;
let environment = &mut context.realm_mut().environment;
environment
.initialize_binding(name.as_ref(), next_result.clone())
.map_err(|e| e.to_error(context))?;
} }
} }
Node::VarDeclList(ref list) => match list.as_ref() { Node::VarDeclList(ref list) => match list.as_ref() {
[var] => { [var] => {
let environment = &mut context.realm_mut().environment;
if var.init().is_some() { if var.init().is_some() {
return context.throw_syntax_error("a declaration in the head of a for-of loop can't have an initializer"); return context.throw_syntax_error("a declaration in the head of a for-of loop can't have an initializer");
} }
if environment.has_binding(var.name()) { if context.has_binding(var.name()) {
environment context.set_mutable_binding(var.name(), next_result, true)?;
.set_mutable_binding(var.name(), next_result, true)
.map_err(|e| e.to_error(context))?;
} else { } else {
environment context.create_mutable_binding(
.create_mutable_binding( var.name().to_owned(),
var.name().to_owned(), false,
false, VariableScope::Function,
VariableScope::Function, )?;
) context.initialize_binding(var.name(), next_result)?;
.map_err(|e| e.to_error(context))?;
let environment = &mut context.realm_mut().environment;
environment
.initialize_binding(var.name(), next_result)
.map_err(|e| e.to_error(context))?;
} }
} }
_ => { _ => {
@ -152,24 +135,17 @@ impl Executable for ForOfLoop {
}, },
Node::LetDeclList(ref list) => match list.as_ref() { Node::LetDeclList(ref list) => match list.as_ref() {
[var] => { [var] => {
let environment = &mut context.realm_mut().environment;
if var.init().is_some() { if var.init().is_some() {
return context.throw_syntax_error("a declaration in the head of a for-of loop can't have an initializer"); return context.throw_syntax_error("a declaration in the head of a for-of loop can't have an initializer");
} }
environment context.create_mutable_binding(
.create_mutable_binding( var.name().to_owned(),
var.name().to_owned(), false,
false, VariableScope::Block,
VariableScope::Block, )?;
)
.map_err(|e| e.to_error(context))?; context.initialize_binding(var.name(), next_result)?;
let environment = &mut context.realm_mut().environment;
environment
.initialize_binding(var.name(), next_result)
.map_err(|e| e.to_error(context))?;
} }
_ => { _ => {
return context.throw_syntax_error( return context.throw_syntax_error(
@ -179,23 +155,16 @@ impl Executable for ForOfLoop {
}, },
Node::ConstDeclList(ref list) => match list.as_ref() { Node::ConstDeclList(ref list) => match list.as_ref() {
[var] => { [var] => {
let environment = &mut context.realm_mut().environment;
if var.init().is_some() { if var.init().is_some() {
return context.throw_syntax_error("a declaration in the head of a for-of loop can't have an initializer"); return context.throw_syntax_error("a declaration in the head of a for-of loop can't have an initializer");
} }
environment context.create_immutable_binding(
.create_immutable_binding( var.name().to_owned(),
var.name().to_owned(), false,
false, VariableScope::Block,
VariableScope::Block, )?;
) context.initialize_binding(var.name(), next_result)?;
.map_err(|e| e.to_error(context))?;
let environment = &mut context.realm_mut().environment;
environment
.initialize_binding(var.name(), next_result)
.map_err(|e| e.to_error(context))?;
} }
_ => { _ => {
return context.throw_syntax_error( return context.throw_syntax_error(
@ -228,7 +197,7 @@ impl Executable for ForOfLoop {
// Continue execution. // Continue execution.
} }
} }
let _ = context.realm_mut().environment.pop(); let _ = context.pop_environment();
} }
Ok(result) Ok(result)
} }

6
boa/src/syntax/ast/node/mod.rs

@ -342,11 +342,7 @@ impl Executable for Node {
Node::Spread(ref spread) => spread.run(context), Node::Spread(ref spread) => spread.run(context),
Node::This => { Node::This => {
// Will either return `this` binding or undefined // Will either return `this` binding or undefined
context context.get_this_binding()
.realm()
.environment
.get_this_binding()
.map_err(|e| e.to_error(context))
} }
Node::Try(ref try_node) => try_node.run(context), Node::Try(ref try_node) => try_node.run(context),
Node::Break(ref break_node) => break_node.run(context), Node::Break(ref break_node) => break_node.run(context),

25
boa/src/syntax/ast/node/operator/assign/mod.rs

@ -58,25 +58,16 @@ impl Executable for Assign {
let val = self.rhs().run(context)?; let val = self.rhs().run(context)?;
match self.lhs() { match self.lhs() {
Node::Identifier(ref name) => { Node::Identifier(ref name) => {
let environment = &mut context.realm_mut().environment; if context.has_binding(name.as_ref()) {
if environment.has_binding(name.as_ref()) {
// Binding already exists // Binding already exists
environment context.set_mutable_binding(name.as_ref(), val.clone(), true)?;
.set_mutable_binding(name.as_ref(), val.clone(), true)
.map_err(|e| e.to_error(context))?;
} else { } else {
environment context.create_mutable_binding(
.create_mutable_binding( name.as_ref().to_owned(),
name.as_ref().to_owned(), true,
true, VariableScope::Function,
VariableScope::Function, )?;
) context.initialize_binding(name.as_ref(), val.clone())?;
.map_err(|e| e.to_error(context))?;
let environment = &mut context.realm_mut().environment;
environment
.initialize_binding(name.as_ref(), val.clone())
.map_err(|e| e.to_error(context))?;
} }
} }
Node::GetConstField(ref get_const_field) => { Node::GetConstField(ref get_const_field) => {

12
boa/src/syntax/ast/node/operator/bin_op/mod.rs

@ -205,18 +205,10 @@ impl Executable for BinOp {
}), }),
op::BinOp::Assign(op) => match self.lhs() { op::BinOp::Assign(op) => match self.lhs() {
Node::Identifier(ref name) => { Node::Identifier(ref name) => {
let v_a = context let v_a = context.get_binding_value(name.as_ref())?;
.realm()
.environment
.get_binding_value(name.as_ref())
.map_err(|e| e.to_error(context))?;
let value = Self::run_assign(op, v_a, self.rhs(), context)?; let value = Self::run_assign(op, v_a, self.rhs(), context)?;
context context.set_mutable_binding(name.as_ref(), value.clone(), true)?;
.realm_mut()
.environment
.set_mutable_binding(name.as_ref(), value.clone(), true)
.map_err(|e| e.to_error(context))?;
Ok(value) Ok(value)
} }
Node::GetConstField(ref get_const_field) => { Node::GetConstField(ref get_const_field) => {

5
boa/src/syntax/ast/node/template/mod.rs

@ -128,10 +128,7 @@ impl Executable for TaggedTemplate {
obj.get_field(field.to_property_key(context)?, context)?, obj.get_field(field.to_property_key(context)?, context)?,
) )
} }
_ => ( _ => (context.global_object().into(), self.tag.run(context)?),
context.global_object().clone().into(),
self.tag.run(context)?,
),
}; };
let mut args = vec![template_object]; let mut args = vec![template_object];

22
boa/src/syntax/ast/node/try_node/mod.rs

@ -1,5 +1,8 @@
use crate::{ use crate::{
environment::lexical_environment::{new_declarative_environment, VariableScope}, environment::{
declarative_environment_record::DeclarativeEnvironmentRecord,
lexical_environment::VariableScope,
},
exec::Executable, exec::Executable,
gc::{Finalize, Trace}, gc::{Finalize, Trace},
syntax::ast::node::{Block, Identifier, Node}, syntax::ast::node::{Block, Identifier, Node},
@ -98,28 +101,23 @@ impl Executable for Try {
|err| { |err| {
if let Some(catch) = self.catch() { if let Some(catch) = self.catch() {
{ {
let env = &mut context.realm_mut().environment; let env = context.get_current_environment();
env.push(new_declarative_environment(Some( context.push_environment(DeclarativeEnvironmentRecord::new(Some(env)));
env.get_current_environment_ref().clone(),
)));
if let Some(param) = catch.parameter() { if let Some(param) = catch.parameter() {
env.create_mutable_binding( context.create_mutable_binding(
param.to_owned(), param.to_owned(),
false, false,
VariableScope::Block, VariableScope::Block,
) )?;
.map_err(|e| e.to_error(context))?; context.initialize_binding(param, err)?;
let env = &mut context.realm_mut().environment;
env.initialize_binding(param, err)
.map_err(|e| e.to_error(context))?;
} }
} }
let res = catch.block().run(context); let res = catch.block().run(context);
// pop the block env // pop the block env
let _ = context.realm_mut().environment.pop(); let _ = context.pop_environment();
res res
} else { } else {

Loading…
Cancel
Save