Skip to content

Commit

Permalink
Refactor some environment functions
Browse files Browse the repository at this point in the history
  • Loading branch information
raskad committed Sep 9, 2021
1 parent f1b5358 commit 25d75a5
Show file tree
Hide file tree
Showing 17 changed files with 764 additions and 320 deletions.
4 changes: 2 additions & 2 deletions boa/src/builtins/function/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ impl Function {
// Create binding
local_env
// Function parameters can share names in JavaScript...
.create_mutable_binding(param.name().to_owned(), false, true, context)
.create_mutable_binding(param.name(), false, true, context)
.expect("Failed to create binding for rest param");

// Set Binding to value
Expand All @@ -178,7 +178,7 @@ impl Function {
) {
// Create binding
local_env
.create_mutable_binding(param.name().to_owned(), false, true, context)
.create_mutable_binding(param.name(), false, true, context)
.expect("Failed to create binding");

// Set Binding to value
Expand Down
151 changes: 125 additions & 26 deletions boa/src/environment/declarative_environment_record.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,61 +49,97 @@ impl DeclarativeEnvironmentRecord {
}

impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
fn has_binding(&self, name: &str) -> bool {
self.env_rec.borrow().contains_key(name)
/// `9.1.1.1.1 HasBinding ( N )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-declarative-environment-records-hasbinding-n
fn has_binding(&self, name: &str, _context: &mut Context) -> JsResult<bool> {
// 1. If envRec has a binding for the name that is the value of N, return true.
// 2. Return false.
Ok(self.env_rec.borrow().contains_key(name))
}

/// `9.1.1.1.2 CreateMutableBinding ( N, D )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-declarative-environment-records-createmutablebinding-n-d
fn create_mutable_binding(
&self,
name: String,
name: &str,
deletion: bool,
allow_name_reuse: bool,
_context: &mut Context,
) -> JsResult<()> {
// 1. Assert: envRec does not already have a binding for N.
if !allow_name_reuse {
assert!(
!self.env_rec.borrow().contains_key(name.as_str()),
!self.env_rec.borrow().contains_key(name),
"Identifier {} has already been declared",
name
);
}

// 2. Create a mutable binding in envRec for N and record that it is uninitialized.
// If D is true, record that the newly created binding may be deleted by a subsequent DeleteBinding call.
self.env_rec.borrow_mut().insert(
name.into_boxed_str(),
name.into(),
DeclarativeEnvironmentRecordBinding {
value: None,
can_delete: deletion,
mutable: true,
strict: false,
},
);

// 3. Return NormalCompletion(empty).
Ok(())
}

/// `9.1.1.1.3 CreateImmutableBinding ( N, S )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-declarative-environment-records-createimmutablebinding-n-s
fn create_immutable_binding(
&self,
name: String,
name: &str,
strict: bool,
_context: &mut Context,
) -> JsResult<()> {
// 1. Assert: envRec does not already have a binding for N.
assert!(
!self.env_rec.borrow().contains_key(name.as_str()),
!self.env_rec.borrow().contains_key(name),
"Identifier {} has already been declared",
name
);

// 2. Create an immutable binding in envRec for N and record that it is uninitialized.
// If S is true, record that the newly created binding is a strict binding.
self.env_rec.borrow_mut().insert(
name.into_boxed_str(),
name.into(),
DeclarativeEnvironmentRecordBinding {
value: None,
can_delete: true,
mutable: false,
strict,
},
);

// 3. Return NormalCompletion(empty).
Ok(())
}

/// `9.1.1.1.4 InitializeBinding ( N, V )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-declarative-environment-records-initializebinding-n-v
fn initialize_binding(
&self,
name: &str,
Expand All @@ -112,13 +148,25 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
) -> JsResult<()> {
if let Some(ref mut record) = self.env_rec.borrow_mut().get_mut(name) {
if record.value.is_none() {
// 2. Set the bound value for N in envRec to V.
// 3. Record that the binding for N in envRec has been initialized.
record.value = Some(value);

// 4. Return NormalCompletion(empty).
return Ok(());
}
}

// 1. Assert: envRec must have an uninitialized binding for N.
panic!("record must have binding for {}", name);
}

/// `9.1.1.1.5 SetMutableBinding ( N, V, S )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-declarative-environment-records-setmutablebinding-n-v-s
#[allow(clippy::else_if_without_else)]
fn set_mutable_binding(
&self,
Expand All @@ -127,49 +175,70 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
mut strict: bool,
context: &mut Context,
) -> JsResult<()> {
// 1. If envRec does not have a binding for N, then
if self.env_rec.borrow().get(name).is_none() {
// a. If S is true, throw a ReferenceError exception.
if strict {
return Err(context.construct_reference_error(format!("{} not found", name)));
}

self.create_mutable_binding(name.to_owned(), true, false, context)?;
// b. Perform envRec.CreateMutableBinding(N, true).
self.create_mutable_binding(name, true, false, context)?;
// c. Perform envRec.InitializeBinding(N, V).
self.initialize_binding(name, value, context)?;

// d. Return NormalCompletion(empty).
return Ok(());
}

let (record_strict, record_has_no_value, record_mutable) = {
let (binding_strict, binding_value_is_none, binding_mutable) = {
let env_rec = self.env_rec.borrow();
let record = env_rec.get(name).unwrap();
(record.strict, record.value.is_none(), record.mutable)
let binding = env_rec.get(name).unwrap();
(binding.strict, binding.value.is_none(), binding.mutable)
};
if record_strict {
strict = true

// 2. If the binding for N in envRec is a strict binding, set S to true.
if binding_strict {
strict = true;
}
if record_has_no_value {

// 3. If the binding for N in envRec has not yet been initialized, throw a ReferenceError exception.
if binding_value_is_none {
return Err(
context.construct_reference_error(format!("{} has not been initialized", name))
);
}
if record_mutable {
// 4. Else if the binding for N in envRec is a mutable binding, change its bound value to V.
} else if binding_mutable {
let mut env_rec = self.env_rec.borrow_mut();
let record = env_rec.get_mut(name).unwrap();
record.value = Some(value);
let binding = env_rec.get_mut(name).unwrap();
binding.value = Some(value);
// 5. Else,
// a. Assert: This is an attempt to change the value of an immutable binding.
// b. If S is true, throw a TypeError exception.
} else if strict {
return Err(context.construct_reference_error(format!(
"Cannot mutate an immutable binding {}",
name
)));
return Err(context
.construct_type_error(format!("Cannot mutate an immutable binding {}", name)));
}

// 6. Return NormalCompletion(empty).
Ok(())
}

/// `9.1.1.1.6 GetBindingValue ( N, S )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-declarative-environment-records-getbindingvalue-n-s
fn get_binding_value(
&self,
name: &str,
_strict: bool,
context: &mut Context,
) -> JsResult<JsValue> {
// 1. Assert: envRec has a binding for N.
// 2. If the binding for N in envRec is an uninitialized binding, throw a ReferenceError exception.
// 3. Return the value currently bound to N in envRec.
if let Some(binding) = self.env_rec.borrow().get(name) {
if let Some(ref val) = binding.value {
Ok(val.clone())
Expand All @@ -181,32 +250,62 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
}
}

fn delete_binding(&self, name: &str) -> bool {
/// `9.1.1.1.7 DeleteBinding ( N )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-declarative-environment-records-deletebinding-n
fn delete_binding(&self, name: &str, _context: &mut Context) -> JsResult<bool> {
// 1. Assert: envRec has a binding for the name that is the value of N.
// 2. If the binding for N in envRec cannot be deleted, return false.
// 3. Remove the binding for N from envRec.
// 4. Return true.
match self.env_rec.borrow().get(name) {
Some(binding) => {
if binding.can_delete {
self.env_rec.borrow_mut().remove(name);
true
Ok(true)
} else {
false
Ok(false)
}
}
None => panic!("env_rec has no binding for {}", name),
}
}

/// `9.1.1.1.8 HasThisBinding ( )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-declarative-environment-records-hasthisbinding
fn has_this_binding(&self) -> bool {
// 1. Return false.
false
}

fn get_this_binding(&self, _context: &mut Context) -> JsResult<JsValue> {
Ok(JsValue::undefined())
}

/// `9.1.1.1.9 HasSuperBinding ( )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-declarative-environment-records-hassuperbinding
fn has_super_binding(&self) -> bool {
// 1. Return false.
false
}

/// `9.1.1.1.10 WithBaseObject ( )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-declarative-environment-records-withbaseobject
fn with_base_object(&self) -> Option<JsObject> {
None
}
Expand Down
26 changes: 13 additions & 13 deletions boa/src/environment/environment_record_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ use std::fmt::Debug;
pub trait EnvironmentRecordTrait: Debug + Trace + Finalize {
/// Determine if an Environment Record has a binding for the String value N.
/// Return true if it does and false if it does not.
fn has_binding(&self, name: &str) -> bool;
fn has_binding(&self, name: &str, context: &mut Context) -> JsResult<bool>;

/// 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.
Expand All @@ -35,7 +35,7 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize {
/// paraments with the same name.
fn create_mutable_binding(
&self,
name: String,
name: &str,
deletion: bool,
allow_name_reuse: bool,
context: &mut Context,
Expand All @@ -47,7 +47,7 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize {
/// regardless of the strict mode setting of operations that reference that binding.
fn create_immutable_binding(
&self,
name: String,
name: &str,
strict: bool,
context: &mut Context,
) -> JsResult<()>;
Expand Down Expand Up @@ -85,7 +85,7 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize {
/// The String value name is the text of the bound name.
/// If a binding for name exists, remove the binding and return true.
/// If the binding exists but cannot be removed return false. If the binding does not exist return true.
fn delete_binding(&self, name: &str) -> bool;
fn delete_binding(&self, name: &str, context: &mut Context) -> JsResult<bool>;

/// Determine if an Environment Record establishes a this binding.
/// Return true if it does and false if it does not.
Expand Down Expand Up @@ -129,7 +129,7 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize {
/// Create mutable binding while handling outer environments
fn recursive_create_mutable_binding(
&self,
name: String,
name: &str,
deletion: bool,
scope: VariableScope,
context: &mut Context,
Expand All @@ -146,7 +146,7 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize {
/// Create immutable binding while handling outer environments
fn recursive_create_immutable_binding(
&self,
name: String,
name: &str,
deletion: bool,
scope: VariableScope,
context: &mut Context,
Expand All @@ -168,7 +168,7 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize {
strict: bool,
context: &mut Context,
) -> JsResult<()> {
if self.has_binding(name) {
if self.has_binding(name, context)? {
self.set_mutable_binding(name, value, strict, context)
} else {
self.get_outer_environment_ref()
Expand All @@ -184,7 +184,7 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize {
value: JsValue,
context: &mut Context,
) -> JsResult<()> {
if self.has_binding(name) {
if self.has_binding(name, context)? {
self.initialize_binding(name, value, context)
} else {
self.get_outer_environment_ref()
Expand All @@ -194,17 +194,17 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize {
}

/// Check if a binding exists in current or any outer environment
fn recursive_has_binding(&self, name: &str) -> bool {
self.has_binding(name)
fn recursive_has_binding(&self, name: &str, context: &mut Context) -> JsResult<bool> {
Ok(self.has_binding(name, context)?
|| match self.get_outer_environment_ref() {
Some(outer) => outer.recursive_has_binding(name),
Some(outer) => outer.recursive_has_binding(name, context)?,
None => false,
}
})
}

/// Retrieve binding from current or any outer environment
fn recursive_get_binding_value(&self, name: &str, context: &mut Context) -> JsResult<JsValue> {
if self.has_binding(name) {
if self.has_binding(name, context)? {
self.get_binding_value(name, false, context)
} else {
match self.get_outer_environment_ref() {
Expand Down
Loading

0 comments on commit 25d75a5

Please sign in to comment.