Browse Source

Error handling in environment (#659)

* Map ErrorKind to JS Errors

* Fix fmt

* Modify code with suggestions

* rebase and fix compilation errors

* Remove redundant clone

* Remove duplicate file

* cargo fmt
pull/1002/head
54k1 4 years ago committed by GitHub
parent
commit
7b103a5c0b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 12
      boa/src/builtins/function/mod.rs
  2. 29
      boa/src/context.rs
  3. 92
      boa/src/environment/declarative_environment_record.rs
  4. 18
      boa/src/environment/environment_record_trait.rs
  5. 133
      boa/src/environment/function_environment_record.rs
  6. 74
      boa/src/environment/global_environment_record.rs
  7. 65
      boa/src/environment/lexical_environment.rs
  8. 32
      boa/src/environment/mod.rs
  9. 38
      boa/src/environment/object_environment_record.rs
  10. 14
      boa/src/object/gcobject.rs
  11. 14
      boa/src/syntax/ast/node/declaration/const_decl_list/mod.rs
  12. 13
      boa/src/syntax/ast/node/declaration/function_decl/mod.rs
  13. 13
      boa/src/syntax/ast/node/declaration/let_decl_list/mod.rs
  14. 17
      boa/src/syntax/ast/node/declaration/var_decl_list/mod.rs
  15. 2
      boa/src/syntax/ast/node/identifier/mod.rs
  16. 77
      boa/src/syntax/ast/node/iteration/for_of_loop/mod.rs
  17. 6
      boa/src/syntax/ast/node/mod.rs
  18. 21
      boa/src/syntax/ast/node/operator/assign/mod.rs
  19. 13
      boa/src/syntax/ast/node/operator/bin_op/mod.rs
  20. 8
      boa/src/syntax/ast/node/try_node/mod.rs
  21. 8
      boa/src/value/operations.rs

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

@ -124,12 +124,14 @@ impl Function {
// Create binding // Create binding
local_env local_env
.borrow_mut() .borrow_mut()
.create_mutable_binding(param.name().to_owned(), false); .create_mutable_binding(param.name().to_owned(), false)
.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)
.expect("Failed to initialize rest param");
} }
// Adds an argument to the environment // Adds an argument to the environment
@ -142,12 +144,14 @@ impl Function {
// Create binding // Create binding
local_env local_env
.borrow_mut() .borrow_mut()
.create_mutable_binding(param.name().to_owned(), false); .create_mutable_binding(param.name().to_owned(), false)
.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)
.expect("Failed to intialize binding");
} }
/// Returns true if the function object is callable. /// Returns true if the function object is callable.

29
boa/src/context.rs

@ -342,7 +342,7 @@ impl Context {
#[inline] #[inline]
pub fn construct_range_error<M>(&mut self, message: M) -> Value pub fn construct_range_error<M>(&mut self, message: M) -> Value
where where
M: Into<String>, M: Into<Box<str>>,
{ {
// Runs a `new RangeError(message)`. // Runs a `new RangeError(message)`.
New::from(Call::new( New::from(Call::new(
@ -357,7 +357,7 @@ impl Context {
#[inline] #[inline]
pub fn throw_range_error<M>(&mut self, message: M) -> Result<Value> pub fn throw_range_error<M>(&mut self, message: M) -> Result<Value>
where where
M: Into<String>, M: Into<Box<str>>,
{ {
Err(self.construct_range_error(message)) Err(self.construct_range_error(message))
} }
@ -366,7 +366,7 @@ impl Context {
#[inline] #[inline]
pub fn construct_type_error<M>(&mut self, message: M) -> Value pub fn construct_type_error<M>(&mut self, message: M) -> Value
where where
M: Into<String>, M: Into<Box<str>>,
{ {
// Runs a `new TypeError(message)`. // Runs a `new TypeError(message)`.
New::from(Call::new( New::from(Call::new(
@ -381,7 +381,7 @@ impl Context {
#[inline] #[inline]
pub fn throw_type_error<M>(&mut self, message: M) -> Result<Value> pub fn throw_type_error<M>(&mut self, message: M) -> Result<Value>
where where
M: Into<String>, M: Into<Box<str>>,
{ {
Err(self.construct_type_error(message)) Err(self.construct_type_error(message))
} }
@ -390,11 +390,11 @@ impl Context {
#[inline] #[inline]
pub fn construct_reference_error<M>(&mut self, message: M) -> Value pub fn construct_reference_error<M>(&mut self, message: M) -> Value
where where
M: Into<String>, M: Into<Box<str>>,
{ {
New::from(Call::new( New::from(Call::new(
Identifier::from("ReferenceError"), Identifier::from("ReferenceError"),
vec![Const::from(message.into() + " is not defined").into()], vec![Const::from(message.into()).into()],
)) ))
.run(self) .run(self)
.expect("Into<String> used as message") .expect("Into<String> used as message")
@ -404,7 +404,7 @@ impl Context {
#[inline] #[inline]
pub fn throw_reference_error<M>(&mut self, message: M) -> Result<Value> pub fn throw_reference_error<M>(&mut self, message: M) -> Result<Value>
where where
M: Into<String>, M: Into<Box<str>>,
{ {
Err(self.construct_reference_error(message)) Err(self.construct_reference_error(message))
} }
@ -413,7 +413,7 @@ impl Context {
#[inline] #[inline]
pub fn construct_syntax_error<M>(&mut self, message: M) -> Value pub fn construct_syntax_error<M>(&mut self, message: M) -> Value
where where
M: Into<String>, M: Into<Box<str>>,
{ {
New::from(Call::new( New::from(Call::new(
Identifier::from("SyntaxError"), Identifier::from("SyntaxError"),
@ -427,7 +427,7 @@ impl Context {
#[inline] #[inline]
pub fn throw_syntax_error<M>(&mut self, message: M) -> Result<Value> pub fn throw_syntax_error<M>(&mut self, message: M) -> Result<Value>
where where
M: Into<String>, M: Into<Box<str>>,
{ {
Err(self.construct_syntax_error(message)) Err(self.construct_syntax_error(message))
} }
@ -435,7 +435,7 @@ impl Context {
/// Constructs a `EvalError` with the specified message. /// Constructs a `EvalError` with the specified message.
pub fn construct_eval_error<M>(&mut self, message: M) -> Value pub fn construct_eval_error<M>(&mut self, message: M) -> Value
where where
M: Into<String>, M: Into<Box<str>>,
{ {
New::from(Call::new( New::from(Call::new(
Identifier::from("EvalError"), Identifier::from("EvalError"),
@ -448,7 +448,7 @@ impl Context {
/// Constructs a `URIError` with the specified message. /// Constructs a `URIError` with the specified message.
pub fn construct_uri_error<M>(&mut self, message: M) -> Value pub fn construct_uri_error<M>(&mut self, message: M) -> Value
where where
M: Into<String>, M: Into<Box<str>>,
{ {
New::from(Call::new( New::from(Call::new(
Identifier::from("URIError"), Identifier::from("URIError"),
@ -461,7 +461,7 @@ impl Context {
/// Throws a `EvalError` with the specified message. /// Throws a `EvalError` with the specified message.
pub fn throw_eval_error<M>(&mut self, message: M) -> Result<Value> pub fn throw_eval_error<M>(&mut self, message: M) -> Result<Value>
where where
M: Into<String>, M: Into<Box<str>>,
{ {
Err(self.construct_eval_error(message)) Err(self.construct_eval_error(message))
} }
@ -469,7 +469,7 @@ impl Context {
/// Throws a `URIError` with the specified message. /// Throws a `URIError` with the specified message.
pub fn throw_uri_error<M>(&mut self, message: M) -> Result<Value> pub fn throw_uri_error<M>(&mut self, message: M) -> Result<Value>
where where
M: Into<String>, M: Into<Box<str>>,
{ {
Err(self.construct_uri_error(message)) Err(self.construct_uri_error(message))
} }
@ -619,7 +619,8 @@ impl Context {
Node::Identifier(ref name) => { Node::Identifier(ref name) => {
self.realm self.realm
.environment .environment
.set_mutable_binding(name.as_ref(), value.clone(), true); .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

92
boa/src/environment/declarative_environment_record.rs

@ -5,6 +5,7 @@
//! 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,
@ -41,11 +42,12 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
self.env_rec.contains_key(name) self.env_rec.contains_key(name)
} }
fn create_mutable_binding(&mut self, name: String, deletion: bool) { fn create_mutable_binding(&mut self, name: String, deletion: bool) -> Result<(), ErrorKind> {
if self.env_rec.contains_key(&name) { assert!(
// TODO: change this when error handling comes into play !self.env_rec.contains_key(&name),
panic!("Identifier {} has already been declared", name); "Identifier {} has already been declared",
} name
);
self.env_rec.insert( self.env_rec.insert(
name, name,
@ -56,13 +58,15 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
strict: false, strict: false,
}, },
); );
Ok(())
} }
fn create_immutable_binding(&mut self, name: String, strict: bool) -> bool { fn create_immutable_binding(&mut self, name: String, strict: bool) -> Result<(), ErrorKind> {
if self.env_rec.contains_key(&name) { assert!(
// TODO: change this when error handling comes into play !self.env_rec.contains_key(&name),
panic!("Identifier {} has already been declared", name); "Identifier {} has already been declared",
} name
);
self.env_rec.insert( self.env_rec.insert(
name, name,
@ -73,32 +77,37 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
strict, strict,
}, },
); );
Ok(())
true
} }
fn initialize_binding(&mut self, name: &str, value: Value) { fn initialize_binding(&mut self, name: &str, value: Value) -> Result<(), ErrorKind> {
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);
} else { return Ok(());
// TODO: change this when error handling comes into play
panic!("Identifier {} has already been defined", name);
} }
} }
panic!("record must have binding for {}", name);
} }
#[allow(clippy::else_if_without_else)] #[allow(clippy::else_if_without_else)]
fn set_mutable_binding(&mut self, name: &str, value: Value, mut strict: bool) { fn set_mutable_binding(
&mut self,
name: &str,
value: Value,
mut strict: bool,
) -> Result<(), ErrorKind> {
if self.env_rec.get(name).is_none() { if self.env_rec.get(name).is_none() {
if strict { if strict {
// TODO: change this when error handling comes into play return Err(ErrorKind::new_reference_error(format!(
panic!("Reference Error: Cannot set mutable binding for {}", name); "{} not found",
name
)));
} }
self.create_mutable_binding(name.to_owned(), true); self.create_mutable_binding(name.to_owned(), true)?;
self.initialize_binding(name, value); self.initialize_binding(name, value)?;
return; return Ok(());
} }
let record: &mut DeclarativeEnvironmentRecordBinding = self.env_rec.get_mut(name).unwrap(); let record: &mut DeclarativeEnvironmentRecordBinding = self.env_rec.get_mut(name).unwrap();
@ -106,28 +115,35 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
strict = true strict = true
} }
if record.value.is_none() { if record.value.is_none() {
// TODO: change this when error handling comes into play return Err(ErrorKind::new_reference_error(format!(
panic!("Reference Error: Cannot set mutable binding for {}", name); "{} has not been initialized",
name
)));
} }
if record.mutable { if record.mutable {
record.value = Some(value); record.value = Some(value);
} else if strict { } else if strict {
// TODO: change this when error handling comes into play return Err(ErrorKind::new_type_error(format!(
panic!("TypeError: Cannot mutate an immutable binding {}", name); "Cannot mutate an immutable binding {}",
name
)));
} }
Ok(())
} }
fn get_binding_value(&self, name: &str, _strict: bool) -> Value { fn get_binding_value(&self, name: &str, _strict: bool) -> Result<Value, ErrorKind> {
if let Some(binding) = self.env_rec.get(name) { if let Some(binding) = self.env_rec.get(name) {
binding if let Some(ref val) = binding.value {
.value Ok(val.clone())
.as_ref() } else {
.expect("Could not get record as reference") Err(ErrorKind::new_reference_error(format!(
.clone() "{} is an uninitialized binding",
name
)))
}
} else { } else {
// TODO: change this when error handling comes into play panic!("Cannot get binding value for {}", name);
panic!("ReferenceError: Cannot get binding value for {}", name);
} }
} }
@ -141,7 +157,7 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
false false
} }
} }
None => false, None => panic!("env_rec has no binding for {}", name),
} }
} }
@ -149,8 +165,8 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
false false
} }
fn get_this_binding(&self) -> Value { fn get_this_binding(&self) -> Result<Value, ErrorKind> {
Value::undefined() Ok(Value::undefined())
} }
fn has_super_binding(&self) -> bool { fn has_super_binding(&self) -> bool {

18
boa/src/environment/environment_record_trait.rs

@ -8,6 +8,7 @@
//! //!
//! 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::{ use crate::{
environment::lexical_environment::{Environment, EnvironmentType}, environment::lexical_environment::{Environment, EnvironmentType},
gc::{Finalize, Trace}, gc::{Finalize, Trace},
@ -25,30 +26,35 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize {
/// 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.
/// If the Boolean argument deletion is true the binding may be subsequently deleted. /// If the Boolean argument deletion is true the binding may be subsequently deleted.
fn create_mutable_binding(&mut self, name: String, deletion: bool); fn create_mutable_binding(&mut self, name: String, deletion: bool) -> Result<(), ErrorKind>;
/// 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) -> bool; fn create_immutable_binding(&mut self, name: String, strict: bool) -> Result<(), ErrorKind>;
/// 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); fn initialize_binding(&mut self, name: &str, value: Value) -> Result<(), ErrorKind>;
/// 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.
/// value is the `value` for the binding and may be a value of any ECMAScript language type. S is a Boolean flag. /// value is the `value` for the binding and may be a value of any ECMAScript language type. S is a Boolean flag.
/// If `strict` is true and the binding cannot be set throw a TypeError exception. /// If `strict` is true and the binding cannot be set throw a TypeError exception.
fn set_mutable_binding(&mut self, name: &str, value: Value, strict: bool); fn set_mutable_binding(
&mut self,
name: &str,
value: Value,
strict: bool,
) -> Result<(), ErrorKind>;
/// 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) -> Value; fn get_binding_value(&self, name: &str, strict: bool) -> Result<Value, ErrorKind>;
/// 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.
@ -61,7 +67,7 @@ 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) -> Value; fn get_this_binding(&self) -> Result<Value, ErrorKind>;
/// 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.

133
boa/src/environment/function_environment_record.rs

@ -8,6 +8,7 @@
//! 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 crate::{ use crate::{
environment::{ environment::{
declarative_environment_record::DeclarativeEnvironmentRecordBinding, declarative_environment_record::DeclarativeEnvironmentRecordBinding,
@ -60,39 +61,49 @@ pub struct FunctionEnvironmentRecord {
} }
impl FunctionEnvironmentRecord { impl FunctionEnvironmentRecord {
pub fn bind_this_value(&mut self, value: Value) { pub fn bind_this_value(&mut self, value: Value) -> Result<Value, ErrorKind> {
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 => {
// TODO: change this when error handling comes into play
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 => { BindingStatus::Initialized => Err(ErrorKind::new_reference_error(
// TODO: change this when error handling comes into play "Cannot bind to an initialised function!",
panic!("Reference Error: Cannot bind to an initialised function!"); )),
}
BindingStatus::Uninitialized => { BindingStatus::Uninitialized => {
self.this_value = value; self.this_value = value.clone();
self.this_binding_status = BindingStatus::Initialized; self.this_binding_status = BindingStatus::Initialized;
Ok(value)
} }
} }
} }
pub fn get_super_base(&self) -> Value {
let home = &self.home_object;
if home.is_undefined() {
Value::Undefined
} else {
assert!(home.is_object());
home.as_object()
.expect("home_object must be an Object")
.prototype_instance()
}
}
} }
impl EnvironmentRecordTrait for FunctionEnvironmentRecord { impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
// TODO: get_super_base can't implement until GetPrototypeof is implemented on object
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)
} }
fn create_mutable_binding(&mut self, name: String, deletion: bool) { fn create_mutable_binding(&mut self, name: String, deletion: bool) -> Result<(), ErrorKind> {
if self.env_rec.contains_key(&name) { assert!(
// TODO: change this when error handling comes into play !self.env_rec.contains_key(&name),
panic!("Identifier {} has already been declared", name); "Identifier {} has already been declared",
} name
);
self.env_rec.insert( self.env_rec.insert(
name, name,
@ -103,28 +114,28 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
strict: false, strict: false,
}, },
); );
Ok(())
} }
fn get_this_binding(&self) -> Value { fn get_this_binding(&self) -> Result<Value, ErrorKind> {
match self.this_binding_status { match self.this_binding_status {
BindingStatus::Lexical => { BindingStatus::Lexical => {
// TODO: change this when error handling comes into play
panic!("There is no this for a lexical function record"); panic!("There is no this for a lexical function record");
} }
BindingStatus::Uninitialized => { BindingStatus::Uninitialized => Err(ErrorKind::new_reference_error(
// TODO: change this when error handling comes into play "Uninitialised binding for this function",
panic!("Reference Error: Uninitialised binding for this function"); )),
}
BindingStatus::Initialized => self.this_value.clone(), BindingStatus::Initialized => Ok(self.this_value.clone()),
} }
} }
fn create_immutable_binding(&mut self, name: String, strict: bool) -> bool { fn create_immutable_binding(&mut self, name: String, strict: bool) -> Result<(), ErrorKind> {
if self.env_rec.contains_key(&name) { assert!(
// TODO: change this when error handling comes into play !self.env_rec.contains_key(&name),
panic!("Identifier {} has already been declared", name); "Identifier {} has already been declared",
} name
);
self.env_rec.insert( self.env_rec.insert(
name, name,
@ -135,63 +146,73 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
strict, strict,
}, },
); );
Ok(())
true
} }
fn initialize_binding(&mut self, name: &str, value: Value) { fn initialize_binding(&mut self, name: &str, value: Value) -> Result<(), ErrorKind> {
if let Some(ref mut record) = self.env_rec.get_mut(name) { if let Some(ref mut record) = self.env_rec.get_mut(name) {
match record.value { if record.value.is_none() {
Some(_) => { record.value = Some(value);
// TODO: change this when error handling comes into play return Ok(());
panic!("Identifier {} has already been defined", name);
}
None => record.value = Some(value),
} }
} }
panic!("record must have binding for {}", name)
} }
#[allow(clippy::else_if_without_else)] #[allow(clippy::else_if_without_else)]
fn set_mutable_binding(&mut self, name: &str, value: Value, mut strict: bool) { fn set_mutable_binding(
&mut self,
name: &str,
value: Value,
mut strict: bool,
) -> Result<(), ErrorKind> {
if self.env_rec.get(name).is_none() { if self.env_rec.get(name).is_none() {
if strict { if strict {
// TODO: change this when error handling comes into play return Err(ErrorKind::new_reference_error(format!(
panic!("Reference Error: Cannot set mutable binding for {}", name); "{} not found",
name
)));
} }
self.create_mutable_binding(name.to_owned(), true); self.create_mutable_binding(name.to_owned(), true)?;
self.initialize_binding(name, value); self.initialize_binding(name, value)?;
return; return Ok(());
} }
let record: &mut DeclarativeEnvironmentRecordBinding = self.env_rec.get_mut(name).unwrap(); let record: &mut DeclarativeEnvironmentRecordBinding = self.env_rec.get_mut(name).unwrap();
if record.strict { if record.strict {
strict = true strict = true
} }
if record.value.is_none() { if record.value.is_none() {
// TODO: change this when error handling comes into play return Err(ErrorKind::new_reference_error(format!(
panic!("Reference Error: Cannot set mutable binding for {}", name); "{} has not been initialized",
name
)));
} }
if record.mutable { if record.mutable {
record.value = Some(value); record.value = Some(value);
} else if strict { } else if strict {
// TODO: change this when error handling comes into play return Err(ErrorKind::new_type_error(format!(
panic!("TypeError: Cannot mutate an immutable binding {}", name); "Cannot mutate an immutable binding {}",
name
)));
} }
Ok(())
} }
fn get_binding_value(&self, name: &str, _strict: bool) -> Value { fn get_binding_value(&self, name: &str, _strict: bool) -> Result<Value, ErrorKind> {
if let Some(binding) = self.env_rec.get(name) { if let Some(binding) = self.env_rec.get(name) {
binding if let Some(ref val) = binding.value {
.value Ok(val.clone())
.as_ref() } else {
.expect("Could not get record as reference") Err(ErrorKind::new_reference_error(format!(
.clone() "{} is an uninitialized binding",
name
)))
}
} else { } else {
// TODO: change this when error handling comes into play panic!("Cannot get binding value for {}", name);
panic!("ReferenceError: Cannot get binding value for {}", name);
} }
} }
@ -205,7 +226,7 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
false false
} }
} }
None => false, None => panic!("env_rec has no binding for {}", name),
} }
} }

74
boa/src/environment/global_environment_record.rs

@ -7,6 +7,7 @@
//! 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,
@ -51,20 +52,49 @@ impl GlobalEnvironmentRecord {
} }
} }
pub fn create_global_var_binding(&mut self, name: String, deletion: bool) { pub fn can_declare_global_var(&self, name: &str) -> bool {
let global_object = &self.object_record.bindings;
if global_object.has_field(name) {
true
} else {
global_object.is_extensible()
}
}
pub fn can_declare_global_function(&self, name: &str) -> bool {
let global_object = &self.object_record.bindings;
let existing_prop = global_object.get_property(name);
match existing_prop {
Some(prop) => {
if prop.configurable() {
true
} else {
prop.is_data_descriptor() && prop.attributes().writable() && prop.enumerable()
}
}
None => global_object.is_extensible(),
}
}
pub fn create_global_var_binding(
&mut self,
name: String,
deletion: bool,
) -> Result<(), ErrorKind> {
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); obj_rec.create_mutable_binding(name.clone(), deletion)?;
obj_rec.initialize_binding(&name, Value::undefined()); obj_rec.initialize_binding(&name, Value::undefined())?;
} }
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) {
var_declared_names.insert(name); var_declared_names.insert(name);
} }
Ok(())
} }
pub fn create_global_function_binding(&mut self, name: &str, value: Value, deletion: bool) { pub fn create_global_function_binding(&mut self, name: &str, value: Value, deletion: bool) {
@ -90,8 +120,8 @@ impl GlobalEnvironmentRecord {
} }
impl EnvironmentRecordTrait for GlobalEnvironmentRecord { impl EnvironmentRecordTrait for GlobalEnvironmentRecord {
fn get_this_binding(&self) -> Value { fn get_this_binding(&self) -> Result<Value, ErrorKind> {
self.global_this_binding.clone() Ok(self.global_this_binding.clone())
} }
fn has_binding(&self, name: &str) -> bool { fn has_binding(&self, name: &str) -> bool {
@ -101,36 +131,48 @@ impl EnvironmentRecordTrait for GlobalEnvironmentRecord {
self.object_record.has_binding(name) self.object_record.has_binding(name)
} }
fn create_mutable_binding(&mut self, name: String, deletion: bool) { fn create_mutable_binding(&mut self, name: String, deletion: bool) -> Result<(), ErrorKind> {
if self.declarative_record.has_binding(&name) { if self.declarative_record.has_binding(&name) {
// TODO: change to exception return Err(ErrorKind::new_type_error(format!(
panic!("Binding already exists!"); "Binding already exists for {}",
name
)));
} }
self.declarative_record self.declarative_record
.create_mutable_binding(name, deletion) .create_mutable_binding(name, deletion)
} }
fn create_immutable_binding(&mut self, name: String, strict: bool) -> bool { fn create_immutable_binding(&mut self, name: String, strict: bool) -> Result<(), ErrorKind> {
if self.declarative_record.has_binding(&name) { if self.declarative_record.has_binding(&name) {
// TODO: change to exception return Err(ErrorKind::new_type_error(format!(
panic!("Binding already exists!"); "Binding already exists for {}",
name
)));
} }
self.declarative_record self.declarative_record
.create_immutable_binding(name, strict) .create_immutable_binding(name, strict)
} }
fn initialize_binding(&mut self, name: &str, value: Value) { fn initialize_binding(&mut self, name: &str, value: Value) -> Result<(), ErrorKind> {
if self.declarative_record.has_binding(&name) { if self.declarative_record.has_binding(&name) {
// TODO: assert binding is in the object environment record
return self.declarative_record.initialize_binding(name, value); return self.declarative_record.initialize_binding(name, value);
} }
panic!("Should not initialized binding without creating first."); assert!(
self.object_record.has_binding(name),
"Binding must be in object_record"
);
self.object_record.initialize_binding(name, value)
} }
fn set_mutable_binding(&mut self, name: &str, value: Value, strict: bool) { fn set_mutable_binding(
&mut self,
name: &str,
value: Value,
strict: bool,
) -> Result<(), ErrorKind> {
if self.declarative_record.has_binding(&name) { if self.declarative_record.has_binding(&name) {
return self return self
.declarative_record .declarative_record
@ -139,7 +181,7 @@ impl EnvironmentRecordTrait for GlobalEnvironmentRecord {
self.object_record.set_mutable_binding(name, value, strict) self.object_record.set_mutable_binding(name, value, strict)
} }
fn get_binding_value(&self, name: &str, strict: bool) -> Value { fn get_binding_value(&self, name: &str, strict: bool) -> Result<Value, ErrorKind> {
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);
} }

65
boa/src/environment/lexical_environment.rs

@ -5,6 +5,7 @@
//! 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 crate::{ use crate::{
environment::{ environment::{
declarative_environment_record::DeclarativeEnvironmentRecord, declarative_environment_record::DeclarativeEnvironmentRecord,
@ -113,14 +114,19 @@ impl LexicalEnvironment {
.get_global_object() .get_global_object()
} }
pub fn get_this_binding(&self) -> Value { pub fn get_this_binding(&self) -> Result<Value, ErrorKind> {
self.environments() self.environments()
.find(|env| env.borrow().has_this_binding()) .find(|env| env.borrow().has_this_binding())
.map(|env| env.borrow().get_this_binding()) .map(|env| env.borrow().get_this_binding())
.unwrap_or_else(Value::undefined) .unwrap_or_else(|| Ok(Value::Undefined))
} }
pub fn create_mutable_binding(&mut self, name: String, deletion: bool, scope: VariableScope) { pub fn create_mutable_binding(
&mut self,
name: String,
deletion: bool,
scope: VariableScope,
) -> Result<(), ErrorKind> {
match scope { match scope {
VariableScope::Block => self VariableScope::Block => self
.get_current_environment() .get_current_environment()
@ -138,7 +144,7 @@ impl LexicalEnvironment {
}) })
.expect("No function or global environment"); .expect("No function or global environment");
env.borrow_mut().create_mutable_binding(name, deletion); env.borrow_mut().create_mutable_binding(name, deletion)
} }
} }
} }
@ -148,7 +154,7 @@ impl LexicalEnvironment {
name: String, name: String,
deletion: bool, deletion: bool,
scope: VariableScope, scope: VariableScope,
) -> bool { ) -> Result<(), ErrorKind> {
match scope { match scope {
VariableScope::Block => self VariableScope::Block => self
.get_current_environment() .get_current_environment()
@ -171,24 +177,43 @@ impl LexicalEnvironment {
} }
} }
pub fn set_mutable_binding(&mut self, name: &str, value: Value, strict: bool) { pub fn set_mutable_binding(
&mut self,
name: &str,
value: Value,
strict: bool,
) -> Result<(), ErrorKind> {
// Find the first environment which has the given binding // Find the first environment which has the given binding
let env = self let env = self
.environments() .environments()
.find(|env| env.borrow().has_binding(name)) .find(|env| env.borrow().has_binding(name));
.expect("Binding does not exists"); // TODO graceful error handling
let env = if let Some(env) = env {
env.borrow_mut().set_mutable_binding(name, value, strict); env
} else {
// global_env doesn't need has_binding to be satisfied in non strict mode
self.environment_stack
.get(0)
.expect("Environment stack underflow")
};
env.borrow_mut().set_mutable_binding(name, value, strict)
} }
pub fn initialize_binding(&mut self, name: &str, value: Value) { pub fn initialize_binding(&mut self, name: &str, value: Value) -> Result<(), ErrorKind> {
// Find the first environment which has the given binding // Find the first environment which has the given binding
let env = self let env = self
.environments() .environments()
.find(|env| env.borrow().has_binding(name)) .find(|env| env.borrow().has_binding(name));
.expect("Binding does not exists"); // TODO graceful error handling
let env = if let Some(env) = env {
env.borrow_mut().initialize_binding(name, value); env
} else {
// global_env doesn't need has_binding to be satisfied in non strict mode
self.environment_stack
.get(0)
.expect("Environment stack underflow")
};
env.borrow_mut().initialize_binding(name, value)
} }
/// get_current_environment_ref is used when you only need to borrow the environment /// get_current_environment_ref is used when you only need to borrow the environment
@ -212,10 +237,16 @@ impl LexicalEnvironment {
.any(|env| env.borrow().has_binding(name)) .any(|env| env.borrow().has_binding(name))
} }
pub fn get_binding_value(&self, name: &str) -> Option<Value> { pub fn get_binding_value(&self, name: &str) -> Result<Value, ErrorKind> {
self.environments() self.environments()
.find(|env| env.borrow().has_binding(name)) .find(|env| env.borrow().has_binding(name))
.map(|env| env.borrow().get_binding_value(name, false)) .map(|env| env.borrow().get_binding_value(name, false))
.unwrap_or_else(|| {
Err(ErrorKind::new_reference_error(format!(
"{} is not defined",
name
)))
})
} }
} }
@ -246,7 +277,7 @@ pub fn new_function_environment(
}; };
// If a `this` value has been passed, bind it to the environment // If a `this` value has been passed, bind it to the environment
if let Some(v) = this { if let Some(v) = this {
func_env.bind_this_value(v); func_env.bind_this_value(v).unwrap();
} }
Gc::new(GcCell::new(Box::new(func_env))) Gc::new(GcCell::new(Box::new(func_env)))
} }

32
boa/src/environment/mod.rs

@ -6,3 +6,35 @@ 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())
}
}

38
boa/src/environment/object_environment_record.rs

@ -6,6 +6,7 @@
//! 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 crate::{ use crate::{
environment::{ environment::{
environment_record_trait::EnvironmentRecordTrait, environment_record_trait::EnvironmentRecordTrait,
@ -35,7 +36,7 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
} }
} }
fn create_mutable_binding(&mut self, name: String, deletion: bool) { fn create_mutable_binding(&mut self, name: String, deletion: bool) -> Result<(), ErrorKind> {
// 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;
@ -46,13 +47,14 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
prop.set_configurable(deletion); prop.set_configurable(deletion);
bindings.set_property(name, prop); bindings.set_property(name, prop);
Ok(())
} }
fn create_immutable_binding(&mut self, _name: String, _strict: bool) -> bool { fn create_immutable_binding(&mut self, _name: String, _strict: bool) -> Result<(), ErrorKind> {
true Ok(())
} }
fn initialize_binding(&mut self, name: &str, value: Value) { fn initialize_binding(&mut self, name: &str, value: Value) -> Result<(), ErrorKind> {
// 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.
@ -60,26 +62,32 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
self.set_mutable_binding(name, value, false) self.set_mutable_binding(name, value, false)
} }
fn set_mutable_binding(&mut self, name: &str, value: Value, strict: bool) { fn set_mutable_binding(
&mut self,
name: &str,
value: Value,
strict: bool,
) -> Result<(), ErrorKind> {
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);
self.bindings self.bindings
.as_object() .as_object()
.expect("binding object") .expect("binding object")
.insert(name, property); .insert(name, property);
Ok(())
} }
fn get_binding_value(&self, name: &str, strict: bool) -> Value { fn get_binding_value(&self, name: &str, strict: bool) -> Result<Value, ErrorKind> {
if self.bindings.has_field(name) { if self.bindings.has_field(name) {
self.bindings.get_field(name) Ok(self.bindings.get_field(name))
} else if strict {
Err(ErrorKind::new_reference_error(format!(
"{} has no binding",
name
)))
} else { } else {
if strict { Ok(Value::undefined())
// TODO: throw error here
// Error handling not implemented yet
}
Value::undefined()
} }
} }
@ -92,8 +100,8 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
false false
} }
fn get_this_binding(&self) -> Value { fn get_this_binding(&self) -> Result<Value, ErrorKind> {
Value::undefined() Ok(Value::undefined())
} }
fn has_super_binding(&self) -> bool { fn has_super_binding(&self) -> bool {

14
boa/src/object/gcobject.rs

@ -162,10 +162,12 @@ impl GcObject {
let arguments_obj = create_unmapped_arguments_object(args); let arguments_obj = create_unmapped_arguments_object(args);
local_env local_env
.borrow_mut() .borrow_mut()
.create_mutable_binding("arguments".to_string(), false); .create_mutable_binding("arguments".to_string(), false)
.map_err(|e| e.to_error(context))?;
local_env local_env
.borrow_mut() .borrow_mut()
.initialize_binding("arguments", arguments_obj); .initialize_binding("arguments", arguments_obj)
.map_err(|e| e.to_error(context))?;
context.realm_mut().environment.push(local_env); context.realm_mut().environment.push(local_env);
@ -257,10 +259,12 @@ impl GcObject {
let arguments_obj = create_unmapped_arguments_object(args); let arguments_obj = create_unmapped_arguments_object(args);
local_env local_env
.borrow_mut() .borrow_mut()
.create_mutable_binding("arguments".to_string(), false); .create_mutable_binding("arguments".to_string(), false)
.map_err(|e| e.to_error(context))?;
local_env local_env
.borrow_mut() .borrow_mut()
.initialize_binding("arguments", arguments_obj); .initialize_binding("arguments", arguments_obj)
.map_err(|e| e.to_error(context))?;
context.realm_mut().environment.push(local_env); context.realm_mut().environment.push(local_env);
@ -285,7 +289,7 @@ impl GcObject {
// local_env gets dropped here, its no longer needed // local_env gets dropped here, its no longer needed
let binding = context.realm_mut().environment.get_this_binding(); let binding = context.realm_mut().environment.get_this_binding();
Ok(binding) binding.map_err(|e| e.to_error(context))
} }
} }
} }

14
boa/src/syntax/ast/node/declaration/const_decl_list/mod.rs

@ -43,17 +43,17 @@ impl Executable for ConstDeclList {
} else { } else {
return context.throw_syntax_error("missing = in const declaration"); return context.throw_syntax_error("missing = in const declaration");
}; };
context
context.realm_mut().environment.create_immutable_binding( .realm_mut()
decl.name().to_owned(), .environment
false, .create_immutable_binding(decl.name().to_owned(), false, VariableScope::Block)
VariableScope::Block, .map_err(|e| e.to_error(context))?;
);
context context
.realm_mut() .realm_mut()
.environment .environment
.initialize_binding(decl.name(), val); .initialize_binding(decl.name(), val)
.map_err(|e| e.to_error(context))?;
} }
Ok(Value::undefined()) Ok(Value::undefined())
} }

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

@ -94,16 +94,17 @@ 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()); val.set_field("name", self.name());
context.realm_mut().environment.create_mutable_binding( context
self.name().to_owned(), .realm_mut()
false, .environment
VariableScope::Function, .create_mutable_binding(self.name().to_owned(), false, VariableScope::Function)
); .map_err(|e| e.to_error(context))?;
context context
.realm_mut() .realm_mut()
.environment .environment
.initialize_binding(self.name(), val); .initialize_binding(self.name(), val)
.map_err(|e| e.to_error(context))?;
Ok(Value::undefined()) Ok(Value::undefined())
} }

13
boa/src/syntax/ast/node/declaration/let_decl_list/mod.rs

@ -41,15 +41,16 @@ impl Executable for LetDeclList {
Some(v) => v.run(context)?, Some(v) => v.run(context)?,
None => Value::undefined(), None => Value::undefined(),
}; };
context.realm_mut().environment.create_mutable_binding(
var.name().to_owned(),
false,
VariableScope::Block,
);
context context
.realm_mut() .realm_mut()
.environment .environment
.initialize_binding(var.name(), val); .create_mutable_binding(var.name().to_owned(), false, VariableScope::Block)
.map_err(|e| e.to_error(context))?;
context
.realm_mut()
.environment
.initialize_binding(var.name(), val)
.map_err(|e| e.to_error(context))?;
} }
Ok(Value::undefined()) Ok(Value::undefined())
} }

17
boa/src/syntax/ast/node/declaration/var_decl_list/mod.rs

@ -46,15 +46,18 @@ impl Executable for VarDeclList {
if environment.has_binding(var.name()) { if environment.has_binding(var.name()) {
if var.init().is_some() { if var.init().is_some() {
environment.set_mutable_binding(var.name(), val, true); environment
.set_mutable_binding(var.name(), val, true)
.map_err(|e| e.to_error(context))?;
} }
} else { } else {
environment.create_mutable_binding( environment
var.name().to_owned(), .create_mutable_binding(var.name().to_owned(), false, VariableScope::Function)
false, .map_err(|e| e.to_error(context))?;
VariableScope::Function, let environment = &mut context.realm_mut().environment;
); environment
environment.initialize_binding(var.name(), val); .initialize_binding(var.name(), val)
.map_err(|e| e.to_error(context))?;
} }
} }
Ok(Value::undefined()) Ok(Value::undefined())

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

@ -40,7 +40,7 @@ impl Executable for Identifier {
.realm() .realm()
.environment .environment
.get_binding_value(self.as_ref()) .get_binding_value(self.as_ref())
.ok_or_else(|| context.construct_reference_error(self.as_ref())) .map_err(|e| e.to_error(context))
} }
} }

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

@ -90,14 +90,21 @@ impl Executable for ForOfLoop {
if environment.has_binding(name.as_ref()) { if environment.has_binding(name.as_ref()) {
// Binding already exists // Binding already exists
environment.set_mutable_binding(name.as_ref(), next_result.clone(), true); environment
.set_mutable_binding(name.as_ref(), next_result.clone(), true)
.map_err(|e| e.to_error(context))?;
} else { } else {
environment.create_mutable_binding( environment
name.as_ref().to_owned(), .create_mutable_binding(
true, name.as_ref().to_owned(),
VariableScope::Function, true,
); VariableScope::Function,
environment.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() {
@ -109,14 +116,21 @@ impl Executable for ForOfLoop {
} }
if environment.has_binding(var.name()) { if environment.has_binding(var.name()) {
environment.set_mutable_binding(var.name(), next_result, true); environment
.set_mutable_binding(var.name(), next_result, true)
.map_err(|e| e.to_error(context))?;
} else { } else {
environment.create_mutable_binding( environment
var.name().to_owned(), .create_mutable_binding(
false, var.name().to_owned(),
VariableScope::Function, false,
); VariableScope::Function,
environment.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))?;
} }
} }
_ => { _ => {
@ -133,12 +147,18 @@ impl Executable for ForOfLoop {
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.create_mutable_binding( environment
var.name().to_owned(), .create_mutable_binding(
false, var.name().to_owned(),
VariableScope::Block, false,
); VariableScope::Block,
environment.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(
@ -154,12 +174,17 @@ impl Executable for ForOfLoop {
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.create_immutable_binding( environment
var.name().to_owned(), .create_immutable_binding(
false, var.name().to_owned(),
VariableScope::Block, false,
); VariableScope::Block,
environment.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(

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

@ -312,7 +312,11 @@ 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
Ok(context.realm().environment.get_this_binding()) context
.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),

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

@ -62,14 +62,21 @@ impl Executable for Assign {
if environment.has_binding(name.as_ref()) { if environment.has_binding(name.as_ref()) {
// Binding already exists // Binding already exists
environment.set_mutable_binding(name.as_ref(), val.clone(), true); environment
.set_mutable_binding(name.as_ref(), val.clone(), true)
.map_err(|e| e.to_error(context))?;
} else { } else {
environment.create_mutable_binding( environment
name.as_ref().to_owned(), .create_mutable_binding(
true, name.as_ref().to_owned(),
VariableScope::Function, true,
); VariableScope::Function,
environment.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) => {

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

@ -174,14 +174,15 @@ impl Executable for BinOp {
.realm() .realm()
.environment .environment
.get_binding_value(name.as_ref()) .get_binding_value(name.as_ref())
.ok_or_else(|| context.construct_reference_error(name.as_ref()))?; .map_err(|e| e.to_error(context))?;
let v_b = self.rhs().run(context)?; let v_b = self.rhs().run(context)?;
let value = Self::run_assign(op, v_a, v_b, context)?; let value = Self::run_assign(op, v_a, v_b, context)?;
context.realm_mut().environment.set_mutable_binding( context
name.as_ref(), .realm_mut()
value.clone(), .environment
true, .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) => {

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

@ -108,9 +108,11 @@ impl Executable for Try {
param.to_owned(), param.to_owned(),
false, false,
VariableScope::Block, VariableScope::Block,
); )
.map_err(|e| e.to_error(context))?;
env.initialize_binding(param, err); let env = &mut context.realm_mut().environment;
env.initialize_binding(param, err)
.map_err(|e| e.to_error(context))?;
} }
} }

8
boa/src/value/operations.rs

@ -320,7 +320,7 @@ impl Value {
a.as_inner() a.as_inner()
.clone() .clone()
.shift_left(b.as_inner().clone()) .shift_left(b.as_inner().clone())
.map_err(|msg| context.construct_range_error(&msg))?, .map_err(|msg| context.construct_range_error(msg))?,
), ),
// Slow path: // Slow path:
@ -332,7 +332,7 @@ impl Value {
x.as_inner() x.as_inner()
.clone() .clone()
.shift_left(y.as_inner().clone()) .shift_left(y.as_inner().clone())
.map_err(|msg| context.construct_range_error(&msg))?, .map_err(|msg| context.construct_range_error(msg))?,
), ),
(_, _) => { (_, _) => {
return context.throw_type_error( return context.throw_type_error(
@ -362,7 +362,7 @@ impl Value {
a.as_inner() a.as_inner()
.clone() .clone()
.shift_right(b.as_inner().clone()) .shift_right(b.as_inner().clone())
.map_err(|msg| context.construct_range_error(&msg))?, .map_err(|msg| context.construct_range_error(msg))?,
), ),
// Slow path: // Slow path:
@ -374,7 +374,7 @@ impl Value {
x.as_inner() x.as_inner()
.clone() .clone()
.shift_right(y.as_inner().clone()) .shift_right(y.as_inner().clone())
.map_err(|msg| context.construct_range_error(&msg))?, .map_err(|msg| context.construct_range_error(msg))?,
), ),
(_, _) => { (_, _) => {
return context.throw_type_error( return context.throw_type_error(

Loading…
Cancel
Save