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
local_env
.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
local_env
.borrow_mut()
.initialize_binding(param.name(), array);
.initialize_binding(param.name(), array)
.expect("Failed to initialize rest param");
}
// Adds an argument to the environment
@ -142,12 +144,14 @@ impl Function {
// Create binding
local_env
.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
local_env
.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.

29
boa/src/context.rs

@ -342,7 +342,7 @@ impl Context {
#[inline]
pub fn construct_range_error<M>(&mut self, message: M) -> Value
where
M: Into<String>,
M: Into<Box<str>>,
{
// Runs a `new RangeError(message)`.
New::from(Call::new(
@ -357,7 +357,7 @@ impl Context {
#[inline]
pub fn throw_range_error<M>(&mut self, message: M) -> Result<Value>
where
M: Into<String>,
M: Into<Box<str>>,
{
Err(self.construct_range_error(message))
}
@ -366,7 +366,7 @@ impl Context {
#[inline]
pub fn construct_type_error<M>(&mut self, message: M) -> Value
where
M: Into<String>,
M: Into<Box<str>>,
{
// Runs a `new TypeError(message)`.
New::from(Call::new(
@ -381,7 +381,7 @@ impl Context {
#[inline]
pub fn throw_type_error<M>(&mut self, message: M) -> Result<Value>
where
M: Into<String>,
M: Into<Box<str>>,
{
Err(self.construct_type_error(message))
}
@ -390,11 +390,11 @@ impl Context {
#[inline]
pub fn construct_reference_error<M>(&mut self, message: M) -> Value
where
M: Into<String>,
M: Into<Box<str>>,
{
New::from(Call::new(
Identifier::from("ReferenceError"),
vec![Const::from(message.into() + " is not defined").into()],
vec![Const::from(message.into()).into()],
))
.run(self)
.expect("Into<String> used as message")
@ -404,7 +404,7 @@ impl Context {
#[inline]
pub fn throw_reference_error<M>(&mut self, message: M) -> Result<Value>
where
M: Into<String>,
M: Into<Box<str>>,
{
Err(self.construct_reference_error(message))
}
@ -413,7 +413,7 @@ impl Context {
#[inline]
pub fn construct_syntax_error<M>(&mut self, message: M) -> Value
where
M: Into<String>,
M: Into<Box<str>>,
{
New::from(Call::new(
Identifier::from("SyntaxError"),
@ -427,7 +427,7 @@ impl Context {
#[inline]
pub fn throw_syntax_error<M>(&mut self, message: M) -> Result<Value>
where
M: Into<String>,
M: Into<Box<str>>,
{
Err(self.construct_syntax_error(message))
}
@ -435,7 +435,7 @@ impl Context {
/// Constructs a `EvalError` with the specified message.
pub fn construct_eval_error<M>(&mut self, message: M) -> Value
where
M: Into<String>,
M: Into<Box<str>>,
{
New::from(Call::new(
Identifier::from("EvalError"),
@ -448,7 +448,7 @@ impl Context {
/// Constructs a `URIError` with the specified message.
pub fn construct_uri_error<M>(&mut self, message: M) -> Value
where
M: Into<String>,
M: Into<Box<str>>,
{
New::from(Call::new(
Identifier::from("URIError"),
@ -461,7 +461,7 @@ impl Context {
/// Throws a `EvalError` with the specified message.
pub fn throw_eval_error<M>(&mut self, message: M) -> Result<Value>
where
M: Into<String>,
M: Into<Box<str>>,
{
Err(self.construct_eval_error(message))
}
@ -469,7 +469,7 @@ impl Context {
/// Throws a `URIError` with the specified message.
pub fn throw_uri_error<M>(&mut self, message: M) -> Result<Value>
where
M: Into<String>,
M: Into<Box<str>>,
{
Err(self.construct_uri_error(message))
}
@ -619,7 +619,8 @@ impl Context {
Node::Identifier(ref name) => {
self.realm
.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)
}
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.
//! 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,
@ -41,11 +42,12 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
self.env_rec.contains_key(name)
}
fn create_mutable_binding(&mut self, name: String, deletion: bool) {
if self.env_rec.contains_key(&name) {
// TODO: change this when error handling comes into play
panic!("Identifier {} has already been declared", name);
}
fn create_mutable_binding(&mut self, name: String, deletion: bool) -> Result<(), ErrorKind> {
assert!(
!self.env_rec.contains_key(&name),
"Identifier {} has already been declared",
name
);
self.env_rec.insert(
name,
@ -56,13 +58,15 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
strict: false,
},
);
Ok(())
}
fn create_immutable_binding(&mut self, name: String, strict: bool) -> bool {
if self.env_rec.contains_key(&name) {
// TODO: change this when error handling comes into play
panic!("Identifier {} has already been declared", name);
}
fn create_immutable_binding(&mut self, name: String, strict: bool) -> Result<(), ErrorKind> {
assert!(
!self.env_rec.contains_key(&name),
"Identifier {} has already been declared",
name
);
self.env_rec.insert(
name,
@ -73,32 +77,37 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
strict,
},
);
true
Ok(())
}
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 record.value.is_none() {
record.value = Some(value);
} else {
// TODO: change this when error handling comes into play
panic!("Identifier {} has already been defined", name);
return Ok(());
}
}
panic!("record must have binding for {}", name);
}
#[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 strict {
// TODO: change this when error handling comes into play
panic!("Reference Error: Cannot set mutable binding for {}", name);
return Err(ErrorKind::new_reference_error(format!(
"{} not found",
name
)));
}
self.create_mutable_binding(name.to_owned(), true);
self.initialize_binding(name, value);
return;
self.create_mutable_binding(name.to_owned(), true)?;
self.initialize_binding(name, value)?;
return Ok(());
}
let record: &mut DeclarativeEnvironmentRecordBinding = self.env_rec.get_mut(name).unwrap();
@ -106,28 +115,35 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
strict = true
}
if record.value.is_none() {
// TODO: change this when error handling comes into play
panic!("Reference Error: Cannot set mutable binding for {}", name);
return Err(ErrorKind::new_reference_error(format!(
"{} has not been initialized",
name
)));
}
if record.mutable {
record.value = Some(value);
} else if strict {
// TODO: change this when error handling comes into play
panic!("TypeError: Cannot mutate an immutable binding {}", name);
return Err(ErrorKind::new_type_error(format!(
"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) {
binding
.value
.as_ref()
.expect("Could not get record as reference")
.clone()
if let Some(ref val) = binding.value {
Ok(val.clone())
} else {
Err(ErrorKind::new_reference_error(format!(
"{} is an uninitialized binding",
name
)))
}
} else {
// TODO: change this when error handling comes into play
panic!("ReferenceError: Cannot get binding value for {}", name);
panic!("Cannot get binding value for {}", name);
}
}
@ -141,7 +157,7 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
false
}
}
None => false,
None => panic!("env_rec has no binding for {}", name),
}
}
@ -149,8 +165,8 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
false
}
fn get_this_binding(&self) -> Value {
Value::undefined()
fn get_this_binding(&self) -> Result<Value, ErrorKind> {
Ok(Value::undefined())
}
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`
//!
use super::ErrorKind;
use crate::{
environment::lexical_environment::{Environment, EnvironmentType},
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.
/// 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.
/// 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) -> 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.
/// 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);
fn initialize_binding(&mut self, name: &str, value: Value) -> Result<(), ErrorKind>;
/// Set the value of an already existing mutable binding in an Environment Record.
/// 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.
/// 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.
/// 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) -> Value;
fn get_binding_value(&self, name: &str, strict: bool) -> Result<Value, ErrorKind>;
/// Delete a binding from an Environment Record.
/// 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;
/// 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.
/// 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.
//! More info: <https://tc39.es/ecma262/#sec-function-environment-records>
use super::ErrorKind;
use crate::{
environment::{
declarative_environment_record::DeclarativeEnvironmentRecordBinding,
@ -60,39 +61,49 @@ pub struct 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 {
// You can not bind an arrow function, their `this` value comes from the lexical scope above
BindingStatus::Lexical => {
// TODO: change this when error handling comes into play
panic!("Cannot bind to an arrow function!");
}
// You can not bind a function twice
BindingStatus::Initialized => {
// TODO: change this when error handling comes into play
panic!("Reference Error: Cannot bind to an initialised function!");
}
BindingStatus::Initialized => Err(ErrorKind::new_reference_error(
"Cannot bind to an initialised function!",
)),
BindingStatus::Uninitialized => {
self.this_value = value;
self.this_value = value.clone();
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 {
// TODO: get_super_base can't implement until GetPrototypeof is implemented on object
fn has_binding(&self, name: &str) -> bool {
self.env_rec.contains_key(name)
}
fn create_mutable_binding(&mut self, name: String, deletion: bool) {
if self.env_rec.contains_key(&name) {
// TODO: change this when error handling comes into play
panic!("Identifier {} has already been declared", name);
}
fn create_mutable_binding(&mut self, name: String, deletion: bool) -> Result<(), ErrorKind> {
assert!(
!self.env_rec.contains_key(&name),
"Identifier {} has already been declared",
name
);
self.env_rec.insert(
name,
@ -103,28 +114,28 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
strict: false,
},
);
Ok(())
}
fn get_this_binding(&self) -> Value {
fn get_this_binding(&self) -> Result<Value, ErrorKind> {
match self.this_binding_status {
BindingStatus::Lexical => {
// TODO: change this when error handling comes into play
panic!("There is no this for a lexical function record");
}
BindingStatus::Uninitialized => {
// TODO: change this when error handling comes into play
panic!("Reference Error: Uninitialised binding for this function");
}
BindingStatus::Uninitialized => Err(ErrorKind::new_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 {
if self.env_rec.contains_key(&name) {
// TODO: change this when error handling comes into play
panic!("Identifier {} has already been declared", name);
}
fn create_immutable_binding(&mut self, name: String, strict: bool) -> Result<(), ErrorKind> {
assert!(
!self.env_rec.contains_key(&name),
"Identifier {} has already been declared",
name
);
self.env_rec.insert(
name,
@ -135,63 +146,73 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
strict,
},
);
true
Ok(())
}
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) {
match record.value {
Some(_) => {
// TODO: change this when error handling comes into play
panic!("Identifier {} has already been defined", name);
}
None => record.value = Some(value),
if record.value.is_none() {
record.value = Some(value);
return Ok(());
}
}
panic!("record must have binding for {}", name)
}
#[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 strict {
// TODO: change this when error handling comes into play
panic!("Reference Error: Cannot set mutable binding for {}", name);
return Err(ErrorKind::new_reference_error(format!(
"{} not found",
name
)));
}
self.create_mutable_binding(name.to_owned(), true);
self.initialize_binding(name, value);
return;
self.create_mutable_binding(name.to_owned(), true)?;
self.initialize_binding(name, value)?;
return Ok(());
}
let record: &mut DeclarativeEnvironmentRecordBinding = self.env_rec.get_mut(name).unwrap();
if record.strict {
strict = true
}
if record.value.is_none() {
// TODO: change this when error handling comes into play
panic!("Reference Error: Cannot set mutable binding for {}", name);
return Err(ErrorKind::new_reference_error(format!(
"{} has not been initialized",
name
)));
}
if record.mutable {
record.value = Some(value);
} else if strict {
// TODO: change this when error handling comes into play
panic!("TypeError: Cannot mutate an immutable binding {}", name);
return Err(ErrorKind::new_type_error(format!(
"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) {
binding
.value
.as_ref()
.expect("Could not get record as reference")
.clone()
if let Some(ref val) = binding.value {
Ok(val.clone())
} else {
Err(ErrorKind::new_reference_error(format!(
"{} is an uninitialized binding",
name
)))
}
} else {
// TODO: change this when error handling comes into play
panic!("ReferenceError: Cannot get binding value for {}", name);
panic!("Cannot get binding value for {}", name);
}
}
@ -205,7 +226,7 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
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.
//! More info: <https://tc39.es/ecma262/#sec-global-environment-records>
use super::ErrorKind;
use crate::{
environment::{
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 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);
obj_rec.initialize_binding(&name, Value::undefined());
obj_rec.create_mutable_binding(name.clone(), deletion)?;
obj_rec.initialize_binding(&name, Value::undefined())?;
}
let var_declared_names = &mut self.var_names;
if !var_declared_names.contains(&name) {
var_declared_names.insert(name);
}
Ok(())
}
pub fn create_global_function_binding(&mut self, name: &str, value: Value, deletion: bool) {
@ -90,8 +120,8 @@ impl GlobalEnvironmentRecord {
}
impl EnvironmentRecordTrait for GlobalEnvironmentRecord {
fn get_this_binding(&self) -> Value {
self.global_this_binding.clone()
fn get_this_binding(&self) -> Result<Value, ErrorKind> {
Ok(self.global_this_binding.clone())
}
fn has_binding(&self, name: &str) -> bool {
@ -101,36 +131,48 @@ impl EnvironmentRecordTrait for GlobalEnvironmentRecord {
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) {
// TODO: change to exception
panic!("Binding already exists!");
return Err(ErrorKind::new_type_error(format!(
"Binding already exists for {}",
name
)));
}
self.declarative_record
.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) {
// TODO: change to exception
panic!("Binding already exists!");
return Err(ErrorKind::new_type_error(format!(
"Binding already exists for {}",
name
)));
}
self.declarative_record
.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) {
// TODO: assert binding is in the object environment record
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) {
return self
.declarative_record
@ -139,7 +181,7 @@ impl EnvironmentRecordTrait for GlobalEnvironmentRecord {
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) {
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
//! This is the entrypoint to lexical environments.
use super::ErrorKind;
use crate::{
environment::{
declarative_environment_record::DeclarativeEnvironmentRecord,
@ -113,14 +114,19 @@ impl LexicalEnvironment {
.get_global_object()
}
pub fn get_this_binding(&self) -> Value {
pub fn get_this_binding(&self) -> Result<Value, ErrorKind> {
self.environments()
.find(|env| env.borrow().has_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 {
VariableScope::Block => self
.get_current_environment()
@ -138,7 +144,7 @@ impl LexicalEnvironment {
})
.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,
deletion: bool,
scope: VariableScope,
) -> bool {
) -> Result<(), ErrorKind> {
match scope {
VariableScope::Block => self
.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
let env = self
.environments()
.find(|env| env.borrow().has_binding(name))
.expect("Binding does not exists"); // TODO graceful error handling
env.borrow_mut().set_mutable_binding(name, value, strict);
.find(|env| env.borrow().has_binding(name));
let env = if let Some(env) = env {
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
let env = self
.environments()
.find(|env| env.borrow().has_binding(name))
.expect("Binding does not exists"); // TODO graceful error handling
env.borrow_mut().initialize_binding(name, value);
.find(|env| env.borrow().has_binding(name));
let env = if let Some(env) = env {
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
@ -212,10 +237,16 @@ impl LexicalEnvironment {
.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()
.find(|env| env.borrow().has_binding(name))
.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 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)))
}

32
boa/src/environment/mod.rs

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

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.
//! More info: [Object Records](https://tc39.es/ecma262/#sec-object-environment-records)
use super::*;
use crate::{
environment::{
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,
// 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;
@ -46,13 +47,14 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
prop.set_configurable(deletion);
bindings.set_property(name, prop);
Ok(())
}
fn create_immutable_binding(&mut self, _name: String, _strict: bool) -> bool {
true
fn create_immutable_binding(&mut self, _name: String, _strict: bool) -> Result<(), ErrorKind> {
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,
// As all calls to create_mutable_binding are followed by initialized binding
// The below is just a check.
@ -60,26 +62,32 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
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());
let mut property = DataDescriptor::new(value, Attribute::ENUMERABLE);
property.set_configurable(strict);
self.bindings
.as_object()
.expect("binding object")
.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) {
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 {
if strict {
// TODO: throw error here
// Error handling not implemented yet
}
Value::undefined()
Ok(Value::undefined())
}
}
@ -92,8 +100,8 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
false
}
fn get_this_binding(&self) -> Value {
Value::undefined()
fn get_this_binding(&self) -> Result<Value, ErrorKind> {
Ok(Value::undefined())
}
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);
local_env
.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
.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);
@ -257,10 +259,12 @@ impl GcObject {
let arguments_obj = create_unmapped_arguments_object(args);
local_env
.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
.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);
@ -285,7 +289,7 @@ impl GcObject {
// local_env gets dropped here, its no longer needed
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 {
return context.throw_syntax_error("missing = in const declaration");
};
context.realm_mut().environment.create_immutable_binding(
decl.name().to_owned(),
false,
VariableScope::Block,
);
context
.realm_mut()
.environment
.create_immutable_binding(decl.name().to_owned(), false, VariableScope::Block)
.map_err(|e| e.to_error(context))?;
context
.realm_mut()
.environment
.initialize_binding(decl.name(), val);
.initialize_binding(decl.name(), val)
.map_err(|e| e.to_error(context))?;
}
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
val.set_field("name", self.name());
context.realm_mut().environment.create_mutable_binding(
self.name().to_owned(),
false,
VariableScope::Function,
);
context
.realm_mut()
.environment
.create_mutable_binding(self.name().to_owned(), false, VariableScope::Function)
.map_err(|e| e.to_error(context))?;
context
.realm_mut()
.environment
.initialize_binding(self.name(), val);
.initialize_binding(self.name(), val)
.map_err(|e| e.to_error(context))?;
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)?,
None => Value::undefined(),
};
context.realm_mut().environment.create_mutable_binding(
var.name().to_owned(),
false,
VariableScope::Block,
);
context
.realm_mut()
.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())
}

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 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 {
environment.create_mutable_binding(
var.name().to_owned(),
false,
VariableScope::Function,
);
environment.initialize_binding(var.name(), val);
environment
.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(), val)
.map_err(|e| e.to_error(context))?;
}
}
Ok(Value::undefined())

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

@ -40,7 +40,7 @@ impl Executable for Identifier {
.realm()
.environment
.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()) {
// 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 {
environment.create_mutable_binding(
name.as_ref().to_owned(),
true,
VariableScope::Function,
);
environment.initialize_binding(name.as_ref(), next_result.clone());
environment
.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))?;
}
}
Node::VarDeclList(ref list) => match list.as_ref() {
@ -109,14 +116,21 @@ impl Executable for ForOfLoop {
}
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 {
environment.create_mutable_binding(
var.name().to_owned(),
false,
VariableScope::Function,
);
environment.initialize_binding(var.name(), next_result);
environment
.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))?;
}
}
_ => {
@ -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");
}
environment.create_mutable_binding(
var.name().to_owned(),
false,
VariableScope::Block,
);
environment.initialize_binding(var.name(), next_result);
environment
.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))?;
}
_ => {
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");
}
environment.create_immutable_binding(
var.name().to_owned(),
false,
VariableScope::Block,
);
environment.initialize_binding(var.name(), next_result);
environment
.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))?;
}
_ => {
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::This => {
// 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::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()) {
// 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 {
environment.create_mutable_binding(
name.as_ref().to_owned(),
true,
VariableScope::Function,
);
environment.initialize_binding(name.as_ref(), val.clone());
environment
.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))?;
}
}
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()
.environment
.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 value = Self::run_assign(op, v_a, v_b, context)?;
context.realm_mut().environment.set_mutable_binding(
name.as_ref(),
value.clone(),
true,
);
context
.realm_mut()
.environment
.set_mutable_binding(name.as_ref(), value.clone(), true)
.map_err(|e| e.to_error(context))?;
Ok(value)
}
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(),
false,
VariableScope::Block,
);
env.initialize_binding(param, err);
)
.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))?;
}
}

8
boa/src/value/operations.rs

@ -320,7 +320,7 @@ impl Value {
a.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:
@ -332,7 +332,7 @@ impl Value {
x.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(
@ -362,7 +362,7 @@ impl Value {
a.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:
@ -374,7 +374,7 @@ impl Value {
x.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(

Loading…
Cancel
Save