Browse Source

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

pull/1246/head
Halid Odat 3 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. 42
      boa/src/syntax/ast/node/declaration/mod.rs
  21. 6
      boa/src/syntax/ast/node/identifier/mod.rs
  22. 79
      boa/src/syntax/ast/node/iteration/for_in_loop/mod.rs
  23. 10
      boa/src/syntax/ast/node/iteration/for_loop/mod.rs
  24. 79
      boa/src/syntax/ast/node/iteration/for_of_loop/mod.rs
  25. 6
      boa/src/syntax/ast/node/mod.rs
  26. 19
      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::BuiltIn,
builtins::Number,
gc::GcObject,
object::{ConstructorBuilder, FunctionBuilder, ObjectData, PROTOTYPE},
object::{ConstructorBuilder, FunctionBuilder, GcObject, ObjectData, PROTOTYPE},
property::{Attribute, DataDescriptor},
value::{same_value_zero, IntegerOrInfinity, Value},
BoaProfiler, Context, Result,

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

@ -126,13 +126,13 @@ impl Function {
local_env
.borrow_mut()
// 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");
// Set Binding to value
local_env
.borrow_mut()
.initialize_binding(param.name(), array)
.initialize_binding(param.name(), array, context)
.expect("Failed to initialize rest param");
}
@ -142,17 +142,18 @@ impl Function {
param: &FormalParameter,
value: Value,
local_env: &Environment,
context: &mut Context,
) {
// Create binding
local_env
.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");
// Set Binding to value
local_env
.borrow_mut()
.initialize_binding(param.name(), value)
.initialize_binding(param.name(), value, context)
.expect("Failed to intialize binding");
}

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

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

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

@ -79,12 +79,7 @@ impl Map {
}
let map_prototype = context
.global_object()
.clone()
.get(
&"Map".into(),
context.global_object().clone().into(),
context,
)?
.get(&"Map".into(), context.global_object().into(), context)?
.get_field(PROTOTYPE, context)?
.as_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,
];
let global_object = context.global_object().clone();
let global_object = context.global_object();
for init in &globals {
let (name, value, attribute) = init(context);

29
boa/src/context.rs

@ -210,7 +210,7 @@ impl StandardObjects {
#[derive(Debug)]
pub struct Context {
/// realm holds both the global object and the environment
realm: Realm,
pub(crate) realm: Realm,
/// The current executor.
executor: Interpreter,
@ -270,16 +270,6 @@ impl Context {
Default::default()
}
#[inline]
pub fn realm(&self) -> &Realm {
&self.realm
}
#[inline]
pub fn realm_mut(&mut self) -> &mut Realm {
&mut self.realm
}
#[inline]
pub fn executor(&mut self) -> &mut Interpreter {
&mut self.executor
@ -340,8 +330,8 @@ impl Context {
/// Return the global object.
#[inline]
pub fn global_object(&self) -> &GcObject {
&self.realm().global_object
pub fn global_object(&self) -> GcObject {
self.realm.global_object.clone()
}
/// Constructs a `RangeError` with the specified message.
@ -503,7 +493,7 @@ impl Context {
flags,
body: RcStatementList::from(body.into()),
params,
environment: self.realm.environment.get_current_environment().clone(),
environment: self.get_current_environment().clone(),
};
let new_func = Object::function(func, function_prototype);
@ -555,7 +545,7 @@ impl Context {
body: NativeFunction,
) -> Result<()> {
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());
Ok(())
}
@ -574,10 +564,7 @@ impl Context {
pub(crate) fn set_value(&mut self, node: &Node, value: Value) -> Result<Value> {
match node {
Node::Identifier(ref name) => {
self.realm
.environment
.set_mutable_binding(name.as_ref(), value.clone(), true)
.map_err(|e| e.to_error(self))?;
self.set_mutable_binding(name.as_ref(), value.clone(), true)?;
Ok(value)
}
Node::GetConstField(ref get_const_field_node) => Ok(get_const_field_node
@ -616,7 +603,7 @@ impl Context {
let class = class_builder.build();
let property = DataDescriptor::new(class, T::ATTRIBUTE);
self.global_object().clone().insert(T::NAME, property);
self.global_object().insert(T::NAME, property);
Ok(())
}
@ -643,7 +630,7 @@ impl Context {
V: Into<Value>,
{
let property = DataDescriptor::new(value, attribute);
self.global_object().clone().insert(key, property);
self.global_object().insert(key, property);
}
/// 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.
//! More info: [ECMA-262 sec-declarative-environment-records](https://tc39.es/ecma262/#sec-declarative-environment-records)
use super::ErrorKind;
use crate::{
environment::{
environment_record_trait::EnvironmentRecordTrait,
lexical_environment::{Environment, EnvironmentType},
},
gc::{Finalize, Trace},
Value,
object::GcObject,
BoaProfiler, Context, Result, Value,
};
use gc::{Gc, GcCell};
use rustc_hash::FxHashMap;
/// 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.
#[derive(Debug, Trace, Finalize, Clone)]
pub struct DeclarativeEnvironmentRecord {
pub env_rec: FxHashMap<String, DeclarativeEnvironmentRecordBinding>,
pub env_rec: FxHashMap<Box<str>, DeclarativeEnvironmentRecordBinding>,
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 {
fn has_binding(&self, name: &str) -> bool {
self.env_rec.contains_key(name)
@ -47,17 +61,18 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
name: String,
deletion: bool,
allow_name_reuse: bool,
) -> Result<(), ErrorKind> {
_context: &mut Context,
) -> Result<()> {
if !allow_name_reuse {
assert!(
!self.env_rec.contains_key(&name),
!self.env_rec.contains_key(name.as_str()),
"Identifier {} has already been declared",
name
);
}
self.env_rec.insert(
name,
name.into_boxed_str(),
DeclarativeEnvironmentRecordBinding {
value: None,
can_delete: deletion,
@ -68,15 +83,20 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
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!(
!self.env_rec.contains_key(&name),
!self.env_rec.contains_key(name.as_str()),
"Identifier {} has already been declared",
name
);
self.env_rec.insert(
name,
name.into_boxed_str(),
DeclarativeEnvironmentRecordBinding {
value: None,
can_delete: true,
@ -87,7 +107,12 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
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 record.value.is_none() {
record.value = Some(value);
@ -103,17 +128,15 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
name: &str,
value: Value,
mut strict: bool,
) -> Result<(), ErrorKind> {
context: &mut Context,
) -> Result<()> {
if self.env_rec.get(name).is_none() {
if strict {
return Err(ErrorKind::new_reference_error(format!(
"{} not found",
name
)));
return Err(context.construct_reference_error(format!("{} not found", name)));
}
self.create_mutable_binding(name.to_owned(), true, false)?;
self.initialize_binding(name, value)?;
self.create_mutable_binding(name.to_owned(), true, false, context)?;
self.initialize_binding(name, value, context)?;
return Ok(());
}
@ -122,15 +145,14 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
strict = true
}
if record.value.is_none() {
return Err(ErrorKind::new_reference_error(format!(
"{} has not been initialized",
name
)));
return Err(
context.construct_reference_error(format!("{} has not been initialized", name))
);
}
if record.mutable {
record.value = Some(value);
} else if strict {
return Err(ErrorKind::new_type_error(format!(
return Err(context.construct_reference_error(format!(
"Cannot mutate an immutable binding {}",
name
)));
@ -139,15 +161,12 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
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(ref val) = binding.value {
Ok(val.clone())
} else {
Err(ErrorKind::new_reference_error(format!(
"{} is an uninitialized binding",
name
)))
context.throw_reference_error(format!("{} is an uninitialized binding", name))
}
} else {
panic!("Cannot get binding value for {}", name);
@ -172,7 +191,7 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
false
}
fn get_this_binding(&self) -> Result<Value, ErrorKind> {
fn get_this_binding(&self, _context: &mut Context) -> Result<Value> {
Ok(Value::undefined())
}
@ -180,8 +199,8 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
false
}
fn with_base_object(&self) -> Value {
Value::undefined()
fn with_base_object(&self) -> Option<GcObject> {
None
}
fn get_outer_environment_ref(&self) -> Option<&Environment> {
@ -195,11 +214,4 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
fn get_environment_type(&self) -> EnvironmentType {
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`
//!
use super::ErrorKind;
use crate::environment::lexical_environment::VariableScope;
use crate::{environment::lexical_environment::VariableScope, object::GcObject};
use crate::{
environment::lexical_environment::{Environment, EnvironmentType},
gc::{Finalize, Trace},
Value,
Context, Result, Value,
};
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 this implementation we have a trait which represents the behaviour of all `EnvironmentRecord` types.
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;
/// 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,
deletion: bool,
allow_name_reuse: bool,
) -> Result<(), ErrorKind>;
context: &mut Context,
) -> Result<()>;
/// Create a new but uninitialized immutable binding in an Environment Record.
/// 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,
/// 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.
/// 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.
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.
/// The String value `name` is the text of the bound name.
@ -59,13 +67,14 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize {
name: &str,
value: Value,
strict: bool,
) -> Result<(), ErrorKind>;
context: &mut Context,
) -> Result<()>;
/// Returns the value of an already existing binding from an Environment Record.
/// The String value N is the text of the bound name.
/// S is used to identify references originating in strict mode code or that
/// 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.
/// 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;
/// 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.
/// Return true if it does and false if it does not.
fn has_super_binding(&self) -> bool;
/// If this Environment Record is associated with a with statement, return the with object.
/// Otherwise, return undefined.
fn with_base_object(&self) -> Value;
/// Otherwise, return None.
fn with_base_object(&self) -> Option<GcObject>;
/// Get the next environment up
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
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
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() {
self.get_this_binding()
self.get_this_binding(context)
} else {
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),
}
}
@ -121,14 +127,15 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize {
name: String,
deletion: bool,
scope: VariableScope,
) -> Result<(), ErrorKind> {
context: &mut Context,
) -> Result<()> {
match scope {
VariableScope::Block => self.create_mutable_binding(name, deletion, false),
VariableScope::Block => self.create_mutable_binding(name, deletion, false, context),
VariableScope::Function => self
.get_outer_environment_ref()
.expect("No function or global environment")
.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,
deletion: bool,
scope: VariableScope,
) -> Result<(), ErrorKind> {
context: &mut Context,
) -> Result<()> {
match scope {
VariableScope::Block => self.create_immutable_binding(name, deletion),
VariableScope::Block => self.create_immutable_binding(name, deletion, context),
VariableScope::Function => self
.get_outer_environment_ref()
.expect("No function or global environment")
.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,
value: Value,
strict: bool,
) -> Result<(), ErrorKind> {
context: &mut Context,
) -> Result<()> {
if self.has_binding(name) {
self.set_mutable_binding(name, value, strict)
self.set_mutable_binding(name, value, strict, context)
} else {
self.get_outer_environment_ref()
.expect("Environment stack underflow")
.borrow_mut()
.recursive_set_mutable_binding(name, value, strict)
.recursive_set_mutable_binding(name, value, strict, context)
}
}
/// 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) {
self.initialize_binding(name, value)
self.initialize_binding(name, value, context)
} else {
self.get_outer_environment_ref()
.expect("Environment stack underflow")
.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
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) {
self.get_binding_value(name, false)
self.get_binding_value(name, false, context)
} else {
match self.get_outer_environment_ref() {
Some(outer) => outer.borrow().recursive_get_binding_value(name),
None => Err(ErrorKind::new_reference_error(format!(
"{} is not defined",
name
))),
Some(outer) => outer.borrow().recursive_get_binding_value(name, context),
None => context.throw_reference_error(format!("{} is not defined", name)),
}
}
}

103
boa/src/environment/function_environment_record.rs

@ -8,7 +8,9 @@
//! from within the function.
//! More info: <https://tc39.es/ecma262/#sec-function-environment-records>
use super::ErrorKind;
use gc::{Gc, GcCell};
use rustc_hash::FxHashMap;
use crate::{
environment::{
declarative_environment_record::DeclarativeEnvironmentRecord,
@ -17,7 +19,7 @@ use crate::{
},
gc::{empty_trace, Finalize, Trace},
object::GcObject,
Value,
Context, Result, Value,
};
/// Different binding status for `this`.
@ -57,17 +59,43 @@ pub struct 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 {
// You can not bind an arrow function, their `this` value comes from the lexical scope above
BindingStatus::Lexical => {
panic!("Cannot bind to an arrow function!");
}
// You can not bind a function twice
BindingStatus::Initialized => Err(ErrorKind::new_reference_error(
"Cannot bind to an initialised function!",
)),
BindingStatus::Initialized => {
todo!();
// context.throw_reference_error("Cannot bind to an initialised function!")
}
BindingStatus::Uninitialized => {
self.this_value = value.clone();
self.this_binding_status = BindingStatus::Initialized;
@ -99,18 +127,30 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
name: String,
deletion: bool,
allow_name_reuse: bool,
) -> Result<(), ErrorKind> {
context: &mut Context,
) -> Result<()> {
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
.create_immutable_binding(name, strict)
.create_immutable_binding(name, strict, context)
}
fn initialize_binding(&mut self, name: &str, value: Value) -> Result<(), ErrorKind> {
self.declarative_record.initialize_binding(name, value)
fn initialize_binding(
&mut self,
name: &str,
value: Value,
context: &mut Context,
) -> Result<()> {
self.declarative_record
.initialize_binding(name, value, context)
}
fn set_mutable_binding(
@ -118,13 +158,15 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
name: &str,
value: Value,
strict: bool,
) -> Result<(), ErrorKind> {
context: &mut Context,
) -> Result<()> {
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> {
self.declarative_record.get_binding_value(name, _strict)
fn get_binding_value(&self, name: &str, strict: bool, context: &mut Context) -> Result<Value> {
self.declarative_record
.get_binding_value(name, strict, context)
}
fn delete_binding(&mut self, name: &str) -> bool {
@ -135,15 +177,14 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
!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 {
BindingStatus::Lexical => {
panic!("There is no this for a lexical function record");
}
BindingStatus::Uninitialized => Err(ErrorKind::new_reference_error(
"Uninitialised binding for this function",
)),
BindingStatus::Uninitialized => {
context.throw_reference_error("Uninitialised binding for this function")
}
BindingStatus::Initialized => Ok(self.this_value.clone()),
}
}
@ -156,8 +197,8 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
}
}
fn with_base_object(&self) -> Value {
Value::undefined()
fn with_base_object(&self) -> Option<GcObject> {
None
}
fn get_outer_environment_ref(&self) -> Option<&Environment> {
@ -172,17 +213,14 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
EnvironmentType::Function
}
fn get_global_object(&self) -> Option<Value> {
self.declarative_record.get_global_object()
}
fn recursive_create_mutable_binding(
&mut self,
name: String,
deletion: bool,
_scope: VariableScope,
) -> Result<(), ErrorKind> {
self.create_mutable_binding(name, deletion, false)
context: &mut Context,
) -> Result<()> {
self.create_mutable_binding(name, deletion, false, context)
}
fn recursive_create_immutable_binding(
@ -190,7 +228,8 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
name: String,
deletion: bool,
_scope: VariableScope,
) -> Result<(), ErrorKind> {
self.create_immutable_binding(name, deletion)
context: &mut Context,
) -> 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.
//! More info: <https://tc39.es/ecma262/#sec-global-environment-records>
use super::ErrorKind;
use crate::{
environment::{
declarative_environment_record::DeclarativeEnvironmentRecord,
@ -16,20 +15,48 @@ use crate::{
object_environment_record::ObjectEnvironmentRecord,
},
gc::{Finalize, Trace},
object::GcObject,
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)]
pub struct GlobalEnvironmentRecord {
pub object_record: ObjectEnvironmentRecord,
pub global_this_binding: Value,
pub global_this_binding: GcObject,
pub declarative_record: DeclarativeEnvironmentRecord,
pub var_names: FxHashSet<String>,
pub var_names: FxHashSet<Box<str>>,
}
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 {
self.var_names.contains(name)
}
@ -80,19 +107,20 @@ impl GlobalEnvironmentRecord {
&mut self,
name: String,
deletion: bool,
) -> Result<(), ErrorKind> {
context: &mut Context,
) -> Result<()> {
let obj_rec = &mut self.object_record;
let global_object = &obj_rec.bindings;
let has_property = global_object.has_field(name.as_str());
let extensible = global_object.is_extensible();
if !has_property && extensible {
obj_rec.create_mutable_binding(name.clone(), deletion, false)?;
obj_rec.initialize_binding(&name, Value::undefined())?;
obj_rec.create_mutable_binding(name.clone(), deletion, false, context)?;
obj_rec.initialize_binding(&name, Value::undefined(), context)?;
}
let var_declared_names = &mut self.var_names;
if !var_declared_names.contains(&name) {
var_declared_names.insert(name);
if !var_declared_names.contains(name.as_str()) {
var_declared_names.insert(name.into_boxed_str());
}
Ok(())
}
@ -132,40 +160,51 @@ impl EnvironmentRecordTrait for GlobalEnvironmentRecord {
name: String,
deletion: bool,
allow_name_reuse: bool,
) -> Result<(), ErrorKind> {
context: &mut Context,
) -> Result<()> {
if !allow_name_reuse && self.declarative_record.has_binding(&name) {
return Err(ErrorKind::new_type_error(format!(
"Binding already exists for {}",
name
)));
return Err(
context.construct_type_error(format!("Binding already exists for {}", name))
);
}
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) {
return Err(ErrorKind::new_type_error(format!(
"Binding already exists for {}",
name
)));
return Err(
context.construct_type_error(format!("Binding already exists for {}", name))
);
}
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) {
return self.declarative_record.initialize_binding(name, value);
return self
.declarative_record
.initialize_binding(name, value, context);
}
assert!(
self.object_record.has_binding(name),
"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(
@ -173,20 +212,24 @@ impl EnvironmentRecordTrait for GlobalEnvironmentRecord {
name: &str,
value: Value,
strict: bool,
) -> Result<(), ErrorKind> {
context: &mut Context,
) -> Result<()> {
if self.declarative_record.has_binding(&name) {
return self
.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) {
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 {
@ -212,16 +255,16 @@ impl EnvironmentRecordTrait for GlobalEnvironmentRecord {
true
}
fn get_this_binding(&self) -> Result<Value, ErrorKind> {
Ok(self.global_this_binding.clone())
fn get_this_binding(&self, _context: &mut Context) -> Result<Value> {
Ok(self.global_this_binding.clone().into())
}
fn has_super_binding(&self) -> bool {
false
}
fn with_base_object(&self) -> Value {
Value::undefined()
fn with_base_object(&self) -> Option<GcObject> {
None
}
fn get_outer_environment(&self) -> Option<Environment> {
@ -241,17 +284,14 @@ impl EnvironmentRecordTrait for GlobalEnvironmentRecord {
EnvironmentType::Global
}
fn get_global_object(&self) -> Option<Value> {
Some(self.global_this_binding.clone())
}
fn recursive_create_mutable_binding(
&mut self,
name: String,
deletion: bool,
_scope: VariableScope,
) -> Result<(), ErrorKind> {
self.create_mutable_binding(name, deletion, false)
context: &mut Context,
) -> Result<()> {
self.create_mutable_binding(name, deletion, false, context)
}
fn recursive_create_immutable_binding(
@ -259,8 +299,9 @@ impl EnvironmentRecordTrait for GlobalEnvironmentRecord {
name: String,
deletion: bool,
_scope: VariableScope,
) -> Result<(), ErrorKind> {
self.create_immutable_binding(name, deletion)
context: &mut Context,
) -> Result<()> {
self.create_immutable_binding(name, deletion, context)
}
fn recursive_set_mutable_binding(
@ -268,11 +309,17 @@ impl EnvironmentRecordTrait for GlobalEnvironmentRecord {
name: &str,
value: Value,
strict: bool,
) -> Result<(), ErrorKind> {
self.set_mutable_binding(name, value, strict)
context: &mut Context,
) -> Result<()> {
self.set_mutable_binding(name, value, strict, context)
}
fn recursive_initialize_binding(&mut self, name: &str, value: Value) -> Result<(), ErrorKind> {
self.initialize_binding(name, value)
fn recursive_initialize_binding(
&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
//! This is the entrypoint to lexical environments.
use super::ErrorKind;
use super::global_environment_record::GlobalEnvironmentRecord;
use crate::{
environment::{
declarative_environment_record::DeclarativeEnvironmentRecord,
environment_record_trait::EnvironmentRecordTrait,
function_environment_record::{BindingStatus, FunctionEnvironmentRecord},
global_environment_record::GlobalEnvironmentRecord,
object_environment_record::ObjectEnvironmentRecord,
},
object::GcObject,
BoaProfiler, Value,
environment::environment_record_trait::EnvironmentRecordTrait, object::GcObject, BoaProfiler,
Context, Result, Value,
};
use gc::{Gc, GcCell};
use rustc_hash::{FxHashMap, FxHashSet};
use std::{collections::VecDeque, error, fmt};
/// 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 {
fn description(&self) -> &str {
&self.details
}
fn cause(&self) -> Option<&dyn error::Error> {
// Generic error, underlying cause isn't tracked.
None
}
}
impl error::Error for EnvironmentError {}
impl LexicalEnvironment {
pub fn new(global: Value) -> Self {
pub fn new(global: GcObject) -> Self {
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 {
environment_stack: VecDeque::new(),
};
@ -91,166 +74,84 @@ impl LexicalEnvironment {
lexical_env.environment_stack.push_back(global_env);
lexical_env
}
}
pub fn push(&mut self, env: Environment) {
self.environment_stack.push_back(env);
}
pub fn pop(&mut self) -> Option<Environment> {
self.environment_stack.pop_back()
impl Context {
pub(crate) fn push_environment(&mut self, env: Environment) {
self.realm.environment.environment_stack.push_back(env);
}
pub fn get_global_object(&self) -> Option<Value> {
self.get_current_environment_ref()
.borrow()
.get_global_object()
pub(crate) fn pop_environment(&mut self) -> Option<Environment> {
self.realm.environment.environment_stack.pop_back()
}
pub fn get_this_binding(&self) -> Result<Value, ErrorKind> {
self.get_current_environment_ref()
pub(crate) fn get_this_binding(&mut self) -> Result<Value> {
self.get_current_environment()
.borrow()
.recursive_get_this_binding()
.recursive_get_this_binding(self)
}
pub fn create_mutable_binding(
pub(crate) fn create_mutable_binding(
&mut self,
name: String,
deletion: bool,
scope: VariableScope,
) -> Result<(), ErrorKind> {
) -> Result<()> {
self.get_current_environment()
.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,
name: String,
deletion: bool,
scope: VariableScope,
) -> Result<(), ErrorKind> {
) -> Result<()> {
self.get_current_environment()
.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,
name: &str,
value: Value,
strict: bool,
) -> Result<(), ErrorKind> {
) -> Result<()> {
self.get_current_environment()
.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()
.borrow_mut()
.recursive_initialize_binding(name, value)
}
/// 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")
.recursive_initialize_binding(name, value, self)
}
/// 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
pub fn get_current_environment(&mut self) -> &mut Environment {
self.environment_stack
pub(crate) fn get_current_environment(&mut self) -> Environment {
self.realm
.environment
.environment_stack
.back_mut()
.expect("Could not get mutable reference to back object")
.clone()
}
pub fn has_binding(&self, name: &str) -> bool {
self.get_current_environment_ref()
pub(crate) fn has_binding(&mut self, name: &str) -> bool {
self.get_current_environment()
.borrow()
.recursive_has_binding(name)
}
pub fn get_binding_value(&self, name: &str) -> Result<Value, ErrorKind> {
self.get_current_environment_ref()
pub(crate) fn get_binding_value(&mut self, name: &str) -> Result<Value> {
self.get_current_environment()
.borrow()
.recursive_get_binding_value(name)
}
}
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();
.recursive_get_binding_value(name, self)
}
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)]

32
boa/src/environment/mod.rs

@ -6,35 +6,3 @@ pub mod function_environment_record;
pub mod global_environment_record;
pub mod lexical_environment;
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.
//! More info: [Object Records](https://tc39.es/ecma262/#sec-object-environment-records)
use super::*;
use crate::property::PropertyDescriptor;
use gc::{Gc, GcCell};
use crate::{
environment::{
environment_record_trait::EnvironmentRecordTrait,
lexical_environment::{Environment, EnvironmentType},
},
gc::{Finalize, Trace},
object::GcObject,
property::PropertyDescriptor,
property::{Attribute, DataDescriptor},
Value,
Context, Result, Value,
};
#[derive(Debug, Trace, Finalize, Clone)]
pub struct ObjectEnvironmentRecord {
// TODO: bindings should be an object.
pub bindings: Value,
pub with_environment: bool,
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 {
fn has_binding(&self, name: &str) -> bool {
if self.bindings.has_field(name) {
@ -42,7 +61,8 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
name: String,
deletion: bool,
_allow_name_reuse: bool,
) -> Result<(), ErrorKind> {
_context: &mut Context,
) -> Result<()> {
// 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
let bindings = &mut self.bindings;
@ -56,16 +76,26 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
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(())
}
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,
// As all calls to create_mutable_binding are followed by initialized binding
// The below is just a check.
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(
@ -73,7 +103,8 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
name: &str,
value: Value,
strict: bool,
) -> Result<(), ErrorKind> {
_context: &mut Context,
) -> Result<()> {
debug_assert!(value.is_object() || value.is_function());
let mut property = DataDescriptor::new(value, Attribute::ENUMERABLE);
property.set_configurable(strict);
@ -84,17 +115,14 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
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) {
match self.bindings.get_property(name) {
Some(PropertyDescriptor::Data(ref d)) => Ok(d.value()),
_ => Ok(Value::undefined()),
}
} else if strict {
Err(ErrorKind::new_reference_error(format!(
"{} has no binding",
name
)))
context.throw_reference_error(format!("{} has no binding", name))
} else {
Ok(Value::undefined())
}
@ -109,7 +137,7 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
false
}
fn get_this_binding(&self) -> Result<Value, ErrorKind> {
fn get_this_binding(&self, _context: &mut Context) -> Result<Value> {
Ok(Value::undefined())
}
@ -117,14 +145,14 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
false
}
fn with_base_object(&self) -> Value {
fn with_base_object(&self) -> Option<GcObject> {
// Object Environment Records return undefined as their
// WithBaseObject unless their withEnvironment flag is true.
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> {
@ -138,12 +166,4 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
fn get_environment_type(&self) -> EnvironmentType {
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`.
#![allow(clippy::unsafe_removed_from_name)]
pub use crate::object::GcObject;
pub use gc::{
custom_trace, force_collect, unsafe_empty_trace as empty_trace, Finalize, GcCellRef as Ref,
GcCellRefMut as RefMut, Trace,

67
boa/src/object/gcobject.rs

@ -7,9 +7,7 @@ use crate::{
builtins::function::{
create_unmapped_arguments_object, BuiltInFunction, Function, NativeFunction,
},
environment::{
function_environment_record::BindingStatus, lexical_environment::new_function_environment,
},
environment::function_environment_record::{BindingStatus, FunctionEnvironmentRecord},
property::{AccessorDescriptor, Attribute, DataDescriptor, PropertyDescriptor, PropertyKey},
syntax::ast::node::RcStatementList,
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)
// <https://tc39.es/ecma262/#sec-prepareforordinarycall>
let local_env = new_function_environment(
let local_env = FunctionEnvironmentRecord::new(
this_function_object,
if flags.is_lexical_this_mode() {
None
@ -161,21 +159,25 @@ impl GcObject {
}
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
let arguments_obj = create_unmapped_arguments_object(args);
local_env
.borrow_mut()
.create_mutable_binding("arguments".to_string(), false, true)
.map_err(|e| e.to_error(context))?;
local_env
.borrow_mut()
.initialize_binding("arguments", arguments_obj)
.map_err(|e| e.to_error(context))?;
local_env.borrow_mut().create_mutable_binding(
"arguments".to_string(),
false,
true,
context,
)?;
local_env.borrow_mut().initialize_binding(
"arguments",
arguments_obj,
context,
)?;
context.realm_mut().environment.push(local_env);
context.push_environment(local_env);
FunctionBody::Ordinary(body.clone())
}
@ -192,7 +194,7 @@ impl GcObject {
FunctionBody::BuiltInConstructor(func) => func(&Value::undefined(), args, context),
FunctionBody::Ordinary(body) => {
let result = body.run(context);
context.realm_mut().environment.pop();
context.pop_environment();
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)
// <https://tc39.es/ecma262/#sec-prepareforordinarycall>
let local_env = new_function_environment(
let local_env = FunctionEnvironmentRecord::new(
this_function_object,
Some(this),
Some(environment.clone()),
@ -269,21 +271,24 @@ impl GcObject {
}
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
let arguments_obj = create_unmapped_arguments_object(args);
local_env
.borrow_mut()
.create_mutable_binding("arguments".to_string(), false, true)
.map_err(|e| e.to_error(context))?;
local_env
.borrow_mut()
.initialize_binding("arguments", arguments_obj)
.map_err(|e| e.to_error(context))?;
context.realm_mut().environment.push(local_env);
local_env.borrow_mut().create_mutable_binding(
"arguments".to_string(),
false,
true,
context,
)?;
local_env.borrow_mut().initialize_binding(
"arguments",
arguments_obj,
context,
)?;
context.push_environment(local_env);
FunctionBody::Ordinary(body.clone())
}
@ -305,12 +310,8 @@ impl GcObject {
let _ = body.run(context);
// local_env gets dropped here, its no longer needed
let result = context
.realm_mut()
.environment
.get_this_binding()
.map_err(|e| e.to_error(context));
context.realm_mut().environment.pop();
let result = context.get_this_binding();
context.pop_environment();
result
}
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.
use crate::object::{GcObject, Object, ObjectData};
use crate::{
environment::{
declarative_environment_record::DeclarativeEnvironmentRecord,
@ -12,7 +11,8 @@ use crate::{
lexical_environment::LexicalEnvironment,
object_environment_record::ObjectEnvironmentRecord,
},
BoaProfiler, Value,
object::{GcObject, Object, ObjectData},
BoaProfiler,
};
use gc::{Gc, GcCell};
use rustc_hash::{FxHashMap, FxHashSet};
@ -41,12 +41,12 @@ impl Realm {
let gc_global = GcObject::new(global);
// 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 {
global_object: gc_global.clone(),
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
fn new_global_environment(
global: GcObject,
this_value: Value,
this_value: GcObject,
) -> Gc<GcCell<GlobalEnvironmentRecord>> {
let obj_rec = ObjectEnvironmentRecord {
bindings: Value::Object(global),
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.

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

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

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

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

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

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

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

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

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

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

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

@ -1,6 +1,9 @@
use crate::{
builtins::iterable::get_iterator,
environment::lexical_environment::{new_declarative_environment, VariableScope},
environment::{
declarative_environment_record::DeclarativeEnvironmentRecord,
lexical_environment::VariableScope,
},
exec::{Executable, InterpreterState},
gc::{Finalize, Trace},
syntax::ast::node::Node,
@ -83,65 +86,45 @@ impl Executable for ForOfLoop {
loop {
{
let env = &mut context.realm_mut().environment;
env.push(new_declarative_environment(Some(
env.get_current_environment_ref().clone(),
)));
let env = context.get_current_environment();
context.push_environment(DeclarativeEnvironmentRecord::new(Some(env)));
}
let iterator_result = iterator.next(context)?;
if iterator_result.is_done() {
context.realm_mut().environment.pop();
context.pop_environment();
break;
}
let next_result = iterator_result.value();
match self.variable() {
Node::Identifier(ref name) => {
let environment = &mut context.realm_mut().environment;
if environment.has_binding(name.as_ref()) {
if context.has_binding(name.as_ref()) {
// Binding already exists
environment
.set_mutable_binding(name.as_ref(), next_result.clone(), true)
.map_err(|e| e.to_error(context))?;
context.set_mutable_binding(name.as_ref(), next_result.clone(), true)?;
} else {
environment
.create_mutable_binding(
context.create_mutable_binding(
name.as_ref().to_owned(),
true,
VariableScope::Function,
)
.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))?;
)?;
context.initialize_binding(name.as_ref(), next_result.clone())?;
}
}
Node::VarDeclList(ref list) => match list.as_ref() {
[var] => {
let environment = &mut context.realm_mut().environment;
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");
}
if environment.has_binding(var.name()) {
environment
.set_mutable_binding(var.name(), next_result, true)
.map_err(|e| e.to_error(context))?;
if context.has_binding(var.name()) {
context.set_mutable_binding(var.name(), next_result, true)?;
} else {
environment
.create_mutable_binding(
context.create_mutable_binding(
var.name().to_owned(),
false,
VariableScope::Function,
)
.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))?;
)?;
context.initialize_binding(var.name(), next_result)?;
}
}
_ => {
@ -152,24 +135,17 @@ impl Executable for ForOfLoop {
},
Node::LetDeclList(ref list) => match list.as_ref() {
[var] => {
let environment = &mut context.realm_mut().environment;
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");
}
environment
.create_mutable_binding(
context.create_mutable_binding(
var.name().to_owned(),
false,
VariableScope::Block,
)
.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))?;
context.initialize_binding(var.name(), next_result)?;
}
_ => {
return context.throw_syntax_error(
@ -179,23 +155,16 @@ impl Executable for ForOfLoop {
},
Node::ConstDeclList(ref list) => match list.as_ref() {
[var] => {
let environment = &mut context.realm_mut().environment;
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");
}
environment
.create_immutable_binding(
context.create_immutable_binding(
var.name().to_owned(),
false,
VariableScope::Block,
)
.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))?;
)?;
context.initialize_binding(var.name(), next_result)?;
}
_ => {
return context.throw_syntax_error(
@ -228,7 +197,7 @@ impl Executable for ForOfLoop {
// Continue execution.
}
}
let _ = context.realm_mut().environment.pop();
let _ = context.pop_environment();
}
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::This => {
// Will either return `this` binding or undefined
context
.realm()
.environment
.get_this_binding()
.map_err(|e| e.to_error(context))
context.get_this_binding()
}
Node::Try(ref try_node) => try_node.run(context),
Node::Break(ref break_node) => break_node.run(context),

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

@ -58,25 +58,16 @@ impl Executable for Assign {
let val = self.rhs().run(context)?;
match self.lhs() {
Node::Identifier(ref name) => {
let environment = &mut context.realm_mut().environment;
if environment.has_binding(name.as_ref()) {
if context.has_binding(name.as_ref()) {
// Binding already exists
environment
.set_mutable_binding(name.as_ref(), val.clone(), true)
.map_err(|e| e.to_error(context))?;
context.set_mutable_binding(name.as_ref(), val.clone(), true)?;
} else {
environment
.create_mutable_binding(
context.create_mutable_binding(
name.as_ref().to_owned(),
true,
VariableScope::Function,
)
.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))?;
)?;
context.initialize_binding(name.as_ref(), val.clone())?;
}
}
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() {
Node::Identifier(ref name) => {
let v_a = context
.realm()
.environment
.get_binding_value(name.as_ref())
.map_err(|e| e.to_error(context))?;
let v_a = context.get_binding_value(name.as_ref())?;
let value = Self::run_assign(op, v_a, self.rhs(), context)?;
context
.realm_mut()
.environment
.set_mutable_binding(name.as_ref(), value.clone(), true)
.map_err(|e| e.to_error(context))?;
context.set_mutable_binding(name.as_ref(), value.clone(), true)?;
Ok(value)
}
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)?,
)
}
_ => (
context.global_object().clone().into(),
self.tag.run(context)?,
),
_ => (context.global_object().into(), self.tag.run(context)?),
};
let mut args = vec![template_object];

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

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

Loading…
Cancel
Save