Browse Source

Refactor `EnvironmentRecordTrait` functions (#1569)

pull/1578/head
raskad 3 years ago committed by GitHub
parent
commit
c5a4be02e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 4
      boa/src/builtins/function/mod.rs
  2. 151
      boa/src/environment/declarative_environment_record.rs
  3. 26
      boa/src/environment/environment_record_trait.rs
  4. 95
      boa/src/environment/function_environment_record.rs
  5. 410
      boa/src/environment/global_environment_record.rs
  6. 9
      boa/src/environment/lexical_environment.rs
  7. 196
      boa/src/environment/object_environment_record.rs
  8. 13
      boa/src/object/gcobject.rs
  9. 10
      boa/src/syntax/ast/node/declaration/function_decl/mod.rs
  10. 24
      boa/src/syntax/ast/node/declaration/mod.rs
  11. 34
      boa/src/syntax/ast/node/iteration/for_in_loop/mod.rs
  12. 34
      boa/src/syntax/ast/node/iteration/for_of_loop/mod.rs
  13. 10
      boa/src/syntax/ast/node/operator/assign/mod.rs
  14. 2
      boa/src/syntax/ast/node/operator/bin_op/mod.rs
  15. 6
      boa/src/syntax/ast/node/try_node/mod.rs
  16. 26
      boa/src/value/mod.rs
  17. 28
      boa/src/vm/mod.rs

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

@ -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
@ -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

151
boa/src/environment/declarative_environment_record.rs

@ -49,27 +49,44 @@ 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,
@ -77,23 +94,34 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
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,
@ -101,9 +129,17 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
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,
@ -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,
@ -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())
@ -181,21 +250,38 @@ 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
}
@ -203,10 +289,23 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
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
}

26
boa/src/environment/environment_record_trait.rs

@ -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.
@ -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,
@ -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<()>;
@ -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.
@ -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,
@ -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,
@ -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()
@ -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()
@ -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() {

95
boa/src/environment/function_environment_record.rs

@ -64,7 +64,8 @@ impl FunctionEnvironmentRecord {
outer: Option<Environment>,
binding_status: BindingStatus,
new_target: JsValue,
) -> FunctionEnvironmentRecord {
context: &mut Context,
) -> JsResult<FunctionEnvironmentRecord> {
let mut func_env = FunctionEnvironmentRecord {
declarative_record: DeclarativeEnvironmentRecord::new(outer), // the outer environment will come from Environment set as a private property of F - https://tc39.es/ecma262/#sec-ecmascript-function-objects
function: f,
@ -75,36 +76,56 @@ impl FunctionEnvironmentRecord {
};
// If a `this` value has been passed, bind it to the environment
if let Some(v) = this {
func_env.bind_this_value(v).unwrap();
func_env.bind_this_value(v, context)?;
}
func_env
Ok(func_env)
}
pub fn bind_this_value(&mut self, value: JsValue) -> JsResult<JsValue> {
/// `9.1.1.3.1 BindThisValue ( V )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-bindthisvalue
pub fn bind_this_value(&mut self, value: JsValue, context: &mut Context) -> JsResult<JsValue> {
match self.this_binding_status {
// You can not bind an arrow function, their `this` value comes from the lexical scope above
// 1. Assert: envRec.[[ThisBindingStatus]] is not lexical.
BindingStatus::Lexical => {
panic!("Cannot bind to an arrow function!");
}
// You can not bind a function twice
// 2. If envRec.[[ThisBindingStatus]] is initialized, throw a ReferenceError exception.
BindingStatus::Initialized => {
todo!();
// context.throw_reference_error("Cannot bind to an initialised function!")
context.throw_reference_error("Cannot bind to an initialized function!")
}
BindingStatus::Uninitialized => {
// 3. Set envRec.[[ThisValue]] to V.
self.this_value = value.clone();
// 4. Set envRec.[[ThisBindingStatus]] to initialized.
self.this_binding_status = BindingStatus::Initialized;
// 5. Return V.
Ok(value)
}
}
}
/// `9.1.1.3.5 GetSuperBase ( )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-getsuperbase
pub fn get_super_base(&self) -> JsValue {
// 1. Let home be envRec.[[FunctionObject]].[[HomeObject]].
let home = &self.home_object;
// 2. If home has the value undefined, return undefined.
if home.is_undefined() {
JsValue::undefined()
} else {
// 3. Assert: Type(home) is Object.
assert!(home.is_object());
// 4. Return ? home.[[GetPrototypeOf]]().
home.as_object()
.expect("home_object must be an Object")
.prototype_instance()
@ -113,13 +134,13 @@ impl FunctionEnvironmentRecord {
}
impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
fn has_binding(&self, name: &str) -> bool {
self.declarative_record.has_binding(name)
fn has_binding(&self, name: &str, context: &mut Context) -> JsResult<bool> {
self.declarative_record.has_binding(name, context)
}
fn create_mutable_binding(
&self,
name: String,
name: &str,
deletion: bool,
allow_name_reuse: bool,
context: &mut Context,
@ -130,7 +151,7 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
fn create_immutable_binding(
&self,
name: String,
name: &str,
strict: bool,
context: &mut Context,
) -> JsResult<()> {
@ -169,34 +190,58 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
.get_binding_value(name, strict, context)
}
fn delete_binding(&self, name: &str) -> bool {
self.declarative_record.delete_binding(name)
fn delete_binding(&self, name: &str, context: &mut Context) -> JsResult<bool> {
self.declarative_record.delete_binding(name, context)
}
/// `9.1.1.3.2 HasThisBinding ( )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-function-environment-records-hasthisbinding
fn has_this_binding(&self) -> bool {
// 1. If envRec.[[ThisBindingStatus]] is lexical, return false; otherwise, return true.
!matches!(self.this_binding_status, BindingStatus::Lexical)
}
/// `9.1.1.3.3 HasSuperBinding ( )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-function-environment-records-hassuperbinding
fn has_super_binding(&self) -> bool {
// 1. If envRec.[[ThisBindingStatus]] is lexical, return false.
// 2. If envRec.[[FunctionObject]].[[HomeObject]] has the value undefined, return false; otherwise, return true.
if let BindingStatus::Lexical = self.this_binding_status {
false
} else {
!self.home_object.is_undefined()
}
}
/// `9.1.1.3.4 GetThisBinding ( )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-function-environment-records-getthisbinding
fn get_this_binding(&self, context: &mut Context) -> JsResult<JsValue> {
match self.this_binding_status {
// 1. Assert: envRec.[[ThisBindingStatus]] is not lexical.
BindingStatus::Lexical => {
panic!("There is no this for a lexical function record");
}
// 2. If envRec.[[ThisBindingStatus]] is uninitialized, throw a ReferenceError exception.
BindingStatus::Uninitialized => {
context.throw_reference_error("Uninitialised binding for this function")
context.throw_reference_error("Uninitialized binding for this function")
}
// 3. Return envRec.[[ThisValue]].
BindingStatus::Initialized => Ok(self.this_value.clone()),
}
}
fn has_super_binding(&self) -> bool {
if let BindingStatus::Lexical = self.this_binding_status {
false
} else {
!self.home_object.is_undefined()
}
}
fn with_base_object(&self) -> Option<JsObject> {
None
}
@ -215,7 +260,7 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
fn recursive_create_mutable_binding(
&self,
name: String,
name: &str,
deletion: bool,
_scope: VariableScope,
context: &mut Context,
@ -225,7 +270,7 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
fn recursive_create_immutable_binding(
&self,
name: String,
name: &str,
deletion: bool,
_scope: VariableScope,
context: &mut Context,

410
boa/src/environment/global_environment_record.rs

@ -33,7 +33,7 @@ pub struct GlobalEnvironmentRecord {
impl GlobalEnvironmentRecord {
pub fn new(global: JsObject, this_value: JsObject) -> GlobalEnvironmentRecord {
let obj_rec = ObjectEnvironmentRecord {
bindings: global.into(),
bindings: global,
outer_env: None,
/// Object Environment Records created for with statements (13.11)
/// can provide their binding object as an implicit this value for use in function calls.
@ -53,156 +53,326 @@ impl GlobalEnvironmentRecord {
}
}
/// `9.1.1.4.12 HasVarDeclaration ( N )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-hasvardeclaration
pub fn has_var_declaration(&self, name: &str) -> bool {
// 1. Let varDeclaredNames be envRec.[[VarNames]].
// 2. If varDeclaredNames contains N, return true.
// 3. Return false.
self.var_names.borrow().contains(name)
}
pub fn has_lexical_declaration(&self, name: &str) -> bool {
self.declarative_record.has_binding(name)
}
pub fn has_restricted_global_property(&self, name: &str) -> bool {
/// `9.1.1.4.13 HasLexicalDeclaration ( N )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-haslexicaldeclaration
pub fn has_lexical_declaration(&self, name: &str, context: &mut Context) -> JsResult<bool> {
// 1. Let DclRec be envRec.[[DeclarativeRecord]].
// 2. Return DclRec.HasBinding(N).
self.declarative_record.has_binding(name, context)
}
/// `9.1.1.4.14 HasRestrictedGlobalProperty ( N )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-hasrestrictedglobalproperty
pub fn has_restricted_global_property(
&self,
name: &str,
context: &mut Context,
) -> JsResult<bool> {
// 1. Let ObjRec be envRec.[[ObjectRecord]].
// 2. Let globalObject be ObjRec.[[BindingObject]].
let global_object = &self.object_record.bindings;
let existing_prop = global_object.get_property(name);
match existing_prop {
Some(desc) => {
if desc.expect_configurable() {
return false;
}
true
}
None => false,
// 3. Let existingProp be ? globalObject.[[GetOwnProperty]](N).
let existing_prop = global_object.__get_own_property__(&name.into(), context)?;
if let Some(existing_prop) = existing_prop {
// 5. If existingProp.[[Configurable]] is true, return false.
// 6. Return true.
Ok(!existing_prop.expect_configurable())
} else {
// 4. If existingProp is undefined, return false.
Ok(false)
}
}
pub fn can_declare_global_var(&self, name: &str) -> bool {
/// `9.1.1.4.15 CanDeclareGlobalVar ( N )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-candeclareglobalvar
pub fn can_declare_global_var(&self, name: &str, context: &mut Context) -> JsResult<bool> {
// 1. Let ObjRec be envRec.[[ObjectRecord]].
// 2. Let globalObject be ObjRec.[[BindingObject]].
let global_object = &self.object_record.bindings;
if global_object.has_field(name) {
true
} else {
global_object.is_extensible()
// 3. Let hasProperty be ? HasOwnProperty(globalObject, N).
let has_property = global_object.has_own_property(name, context)?;
// 4. If hasProperty is true, return true.
if has_property {
return Ok(true);
}
// 5. Return ? IsExtensible(globalObject).
global_object.is_extensible(context)
}
pub fn can_declare_global_function(&self, name: &str) -> bool {
/// `9.1.1.4.16 CanDeclareGlobalFunction ( N )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-candeclareglobalfunction
pub fn can_declare_global_function(&self, name: &str, context: &mut Context) -> JsResult<bool> {
// 1. Let ObjRec be envRec.[[ObjectRecord]].
// 2. Let globalObject be ObjRec.[[BindingObject]].
let global_object = &self.object_record.bindings;
let existing_prop = global_object.get_property(name);
match existing_prop {
Some(prop) => {
prop.expect_configurable()
|| prop
.enumerable()
.zip(prop.writable())
.map_or(false, |(a, b)| a && b)
// 3. Let existingProp be ? globalObject.[[GetOwnProperty]](N).
let existing_prop = global_object.__get_own_property__(&name.into(), context)?;
if let Some(existing_prop) = existing_prop {
// 5. If existingProp.[[Configurable]] is true, return true.
// 6. If IsDataDescriptor(existingProp) is true and existingProp has attribute values { [[Writable]]: true, [[Enumerable]]: true }, return true.
if existing_prop.expect_configurable()
|| matches!(
(
existing_prop.is_data_descriptor(),
existing_prop.writable(),
existing_prop.enumerable(),
),
(true, Some(true), Some(true))
)
{
Ok(true)
} else {
// 7. Return false.
Ok(false)
}
None => global_object.is_extensible(),
} else {
// 4. If existingProp is undefined, return ? IsExtensible(globalObject).
global_object.is_extensible(context)
}
}
/// `9.1.1.4.17 CreateGlobalVarBinding ( N, D )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-createglobalvarbinding
pub fn create_global_var_binding(
&mut self,
name: String,
name: &str,
deletion: bool,
context: &mut Context,
) -> JsResult<()> {
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();
// 1. Let ObjRec be envRec.[[ObjectRecord]].
// 2. Let globalObject be ObjRec.[[BindingObject]].
let global_object = &self.object_record.bindings;
// 3. Let hasProperty be ? HasOwnProperty(globalObject, N).
let has_property = global_object.has_own_property(name, context)?;
// 4. Let extensible be ? IsExtensible(globalObject).
let extensible = global_object.is_extensible(context)?;
// 5. If hasProperty is false and extensible is true, then
if !has_property && extensible {
obj_rec.create_mutable_binding(name.clone(), deletion, false, context)?;
obj_rec.initialize_binding(&name, JsValue::undefined(), context)?;
// a. Perform ? ObjRec.CreateMutableBinding(N, D).
self.object_record
.create_mutable_binding(name, deletion, false, context)?;
// b. Perform ? ObjRec.InitializeBinding(N, undefined).
self.object_record
.initialize_binding(name, JsValue::undefined(), context)?;
}
// 6. Let varDeclaredNames be envRec.[[VarNames]].
let mut var_declared_names = self.var_names.borrow_mut();
if !var_declared_names.contains(name.as_str()) {
var_declared_names.insert(name.into_boxed_str());
// 7. If varDeclaredNames does not contain N, then
if !var_declared_names.contains(name) {
// a. Append N to varDeclaredNames.
var_declared_names.insert(name.into());
}
// 8. Return NormalCompletion(empty).
Ok(())
}
pub fn create_global_function_binding(&mut self, name: &str, value: JsValue, deletion: bool) {
let global_object = &mut self.object_record.bindings;
let existing_prop = global_object.get_property(name);
// TODO: change to desc.is_undefined()
let desc = match existing_prop {
Some(desc) if desc.expect_configurable() => PropertyDescriptor::builder().value(value),
Some(_) => PropertyDescriptor::builder()
.value(value)
/// `9.1.1.4.18 CreateGlobalFunctionBinding ( N, V, D )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-createglobalfunctionbinding
pub fn create_global_function_binding(
&mut self,
name: &str,
value: JsValue,
deletion: bool,
context: &mut Context,
) -> JsResult<()> {
// 1. Let ObjRec be envRec.[[ObjectRecord]].
// 2. Let globalObject be ObjRec.[[BindingObject]].
let global_object = &self.object_record.bindings;
// 3. Let existingProp be ? globalObject.[[GetOwnProperty]](N).
let existing_prop = global_object.__get_own_property__(&name.into(), context)?;
// 4. If existingProp is undefined or existingProp.[[Configurable]] is true, then
let desc = if existing_prop
.map(|f| f.expect_configurable())
.unwrap_or(true)
{
// a. Let desc be the PropertyDescriptor { [[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: D }.
PropertyDescriptor::builder()
.value(value.clone())
.writable(true)
.enumerable(true)
.configurable(deletion),
None => PropertyDescriptor::builder().value(value),
.configurable(deletion)
.build()
// 5. Else,
} else {
// a. Let desc be the PropertyDescriptor { [[Value]]: V }.
PropertyDescriptor::builder().value(value.clone()).build()
};
// TODO: fix spec by adding Set and append name to varDeclaredNames
global_object
.as_object()
.expect("global object")
.insert(name, desc);
}
}
// 6. Perform ? DefinePropertyOrThrow(globalObject, N, desc).
global_object.define_property_or_throw(name, desc, context)?;
// 7. Perform ? Set(globalObject, N, V, false).
global_object.set(name, value, false, context)?;
impl EnvironmentRecordTrait for GlobalEnvironmentRecord {
fn has_binding(&self, name: &str) -> bool {
if self.declarative_record.has_binding(name) {
return true;
// 8. Let varDeclaredNames be envRec.[[VarNames]].
// 9. If varDeclaredNames does not contain N, then
if !self.var_names.borrow().contains(name) {
// a. Append N to varDeclaredNames.
self.var_names.borrow_mut().insert(name.into());
}
self.object_record.has_binding(name)
// 10. Return NormalCompletion(empty).
Ok(())
}
}
impl EnvironmentRecordTrait for GlobalEnvironmentRecord {
/// `9.1.1.4.1 HasBinding ( N )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-global-environment-records-hasbinding-n
fn has_binding(&self, name: &str, context: &mut Context) -> JsResult<bool> {
// 1. Let DclRec be envRec.[[DeclarativeRecord]].
// 2. If DclRec.HasBinding(N) is true, return true.
if self.declarative_record.has_binding(name, context)? {
return Ok(true);
}
// 3. Let ObjRec be envRec.[[ObjectRecord]].
// 4. Return ? ObjRec.HasBinding(N).
self.object_record.has_binding(name, context)
}
/// `9.1.1.4.2 CreateMutableBinding ( N, D )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-global-environment-records-createmutablebinding-n-d
fn create_mutable_binding(
&self,
name: String,
name: &str,
deletion: bool,
allow_name_reuse: bool,
context: &mut Context,
) -> JsResult<()> {
if !allow_name_reuse && self.declarative_record.has_binding(&name) {
// 1. Let DclRec be envRec.[[DeclarativeRecord]].
// 2. If DclRec.HasBinding(N) is true, throw a TypeError exception.
if !allow_name_reuse && self.declarative_record.has_binding(name, context)? {
return Err(
context.construct_type_error(format!("Binding already exists for {}", name))
);
}
// 3. Return DclRec.CreateMutableBinding(N, D).
self.declarative_record
.create_mutable_binding(name, deletion, allow_name_reuse, context)
}
/// `9.1.1.4.3 CreateImmutableBinding ( N, S )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-global-environment-records-createimmutablebinding-n-s
fn create_immutable_binding(
&self,
name: String,
name: &str,
strict: bool,
context: &mut Context,
) -> JsResult<()> {
if self.declarative_record.has_binding(&name) {
// 1. Let DclRec be envRec.[[DeclarativeRecord]].
// 2. If DclRec.HasBinding(N) is true, throw a TypeError exception.
if self.declarative_record.has_binding(name, context)? {
return Err(
context.construct_type_error(format!("Binding already exists for {}", name))
);
}
// 3. Return DclRec.CreateImmutableBinding(N, S).
self.declarative_record
.create_immutable_binding(name, strict, context)
}
/// `9.1.1.4.4 InitializeBinding ( N, V )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-global-environment-records-initializebinding-n-v
fn initialize_binding(
&self,
name: &str,
value: JsValue,
context: &mut Context,
) -> JsResult<()> {
if self.declarative_record.has_binding(name) {
// 1. Let DclRec be envRec.[[DeclarativeRecord]].
// 2. If DclRec.HasBinding(N) is true, then
if self.declarative_record.has_binding(name, context)? {
// a. Return DclRec.InitializeBinding(N, V).
return self
.declarative_record
.initialize_binding(name, value, context);
}
// 3. Assert: If the binding exists, it must be in the object Environment Record.
assert!(
self.object_record.has_binding(name),
self.object_record.has_binding(name, context)?,
"Binding must be in object_record"
);
// 4. Let ObjRec be envRec.[[ObjectRecord]].
// 5. Return ? ObjRec.InitializeBinding(N, V).
self.object_record.initialize_binding(name, value, context)
}
/// `9.1.1.4.5 SetMutableBinding ( N, V, S )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-global-environment-records-setmutablebinding-n-v-s
fn set_mutable_binding(
&self,
name: &str,
@ -210,64 +380,130 @@ impl EnvironmentRecordTrait for GlobalEnvironmentRecord {
strict: bool,
context: &mut Context,
) -> JsResult<()> {
if self.declarative_record.has_binding(name) {
// 1. Let DclRec be envRec.[[DeclarativeRecord]].
// 2. If DclRec.HasBinding(N) is true, then
if self.declarative_record.has_binding(name, context)? {
// a. Return DclRec.SetMutableBinding(N, V, S).
return self
.declarative_record
.set_mutable_binding(name, value, strict, context);
}
// 3. Let ObjRec be envRec.[[ObjectRecord]].
// 4. Return ? ObjRec.SetMutableBinding(N, V, S).
self.object_record
.set_mutable_binding(name, value, strict, context)
}
/// `9.1.1.4.6 GetBindingValue ( N, S )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-global-environment-records-getbindingvalue-n-s
fn get_binding_value(
&self,
name: &str,
strict: bool,
context: &mut Context,
) -> JsResult<JsValue> {
if self.declarative_record.has_binding(name) {
// 1. Let DclRec be envRec.[[DeclarativeRecord]].
// 2. If DclRec.HasBinding(N) is true, then
if self.declarative_record.has_binding(name, context)? {
// a. Return DclRec.GetBindingValue(N, S).
return self
.declarative_record
.get_binding_value(name, strict, context);
}
// 3. Let ObjRec be envRec.[[ObjectRecord]].
// 4. Return ? ObjRec.GetBindingValue(N, S).
self.object_record.get_binding_value(name, strict, context)
}
fn delete_binding(&self, name: &str) -> bool {
if self.declarative_record.has_binding(name) {
return self.declarative_record.delete_binding(name);
/// `9.1.1.4.7 DeleteBinding ( N )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-global-environment-records-deletebinding-n
fn delete_binding(&self, name: &str, context: &mut Context) -> JsResult<bool> {
// 1. Let DclRec be envRec.[[DeclarativeRecord]].
// 2. If DclRec.HasBinding(N) is true, then
if self.declarative_record.has_binding(name, context)? {
// a. Return DclRec.DeleteBinding(N).
return self.declarative_record.delete_binding(name, context);
}
let global: &JsValue = &self.object_record.bindings;
if global.has_field(name) {
let status = self.object_record.delete_binding(name);
// 3. Let ObjRec be envRec.[[ObjectRecord]].
// 4. Let globalObject be ObjRec.[[BindingObject]].
let global_object = &self.object_record.bindings;
// 5. Let existingProp be ? HasOwnProperty(globalObject, N).
// 6. If existingProp is true, then
if global_object.has_own_property(name, context)? {
// a. Let status be ? ObjRec.DeleteBinding(N).
let status = self.object_record.delete_binding(name, context)?;
// b. If status is true, then
if status {
let mut var_names = self.var_names.borrow_mut();
if var_names.contains(name) {
var_names.remove(name);
return status;
}
// i. Let varNames be envRec.[[VarNames]].
// ii. If N is an element of varNames, remove that element from the varNames.
self.var_names.borrow_mut().remove(name);
}
// c. Return status.
return Ok(status);
}
true
// 7. Return true.
Ok(true)
}
/// `9.1.1.4.8 HasThisBinding ( )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-global-environment-records-hasthisbinding
fn has_this_binding(&self) -> bool {
// 1. Return true.
true
}
fn get_this_binding(&self, _context: &mut Context) -> JsResult<JsValue> {
Ok(self.global_this_binding.clone().into())
}
/// `9.1.1.4.9 HasSuperBinding ( )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-global-environment-records-hassuperbinding
fn has_super_binding(&self) -> bool {
// 1. Return false.
false
}
/// `9.1.1.4.10 WithBaseObject ( )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-global-environment-records-withbaseobject
fn with_base_object(&self) -> Option<JsObject> {
// 1. Return undefined.
None
}
/// `9.1.1.4.11 GetThisBinding ( )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-global-environment-records-getthisbinding
fn get_this_binding(&self, _context: &mut Context) -> JsResult<JsValue> {
// 1. Return envRec.[[GlobalThisValue]].
Ok(self.global_this_binding.clone().into())
}
fn get_outer_environment(&self) -> Option<Environment> {
None
}
@ -287,7 +523,7 @@ impl EnvironmentRecordTrait for GlobalEnvironmentRecord {
fn recursive_create_mutable_binding(
&self,
name: String,
name: &str,
deletion: bool,
_scope: VariableScope,
context: &mut Context,
@ -297,7 +533,7 @@ impl EnvironmentRecordTrait for GlobalEnvironmentRecord {
fn recursive_create_immutable_binding(
&self,
name: String,
name: &str,
deletion: bool,
_scope: VariableScope,
context: &mut Context,

9
boa/src/environment/lexical_environment.rs

@ -95,7 +95,7 @@ impl Context {
pub(crate) fn create_mutable_binding(
&mut self,
name: String,
name: &str,
deletion: bool,
scope: VariableScope,
) -> JsResult<()> {
@ -105,7 +105,7 @@ impl Context {
pub(crate) fn create_immutable_binding(
&mut self,
name: String,
name: &str,
deletion: bool,
scope: VariableScope,
) -> JsResult<()> {
@ -139,8 +139,9 @@ impl Context {
.clone()
}
pub(crate) fn has_binding(&mut self, name: &str) -> bool {
self.get_current_environment().recursive_has_binding(name)
pub(crate) fn has_binding(&mut self, name: &str) -> JsResult<bool> {
self.get_current_environment()
.recursive_has_binding(name, self)
}
pub(crate) fn get_binding_value(&mut self, name: &str) -> JsResult<JsValue> {

196
boa/src/environment/object_environment_record.rs

@ -16,19 +16,19 @@ use crate::{
gc::{Finalize, Trace},
object::JsObject,
property::PropertyDescriptor,
symbol::WellKnownSymbols,
Context, JsResult, JsValue,
};
#[derive(Debug, Trace, Finalize, Clone)]
pub struct ObjectEnvironmentRecord {
// TODO: bindings should be an object.
pub bindings: JsValue,
pub bindings: JsObject,
pub with_environment: bool,
pub outer_env: Option<Environment>,
}
impl ObjectEnvironmentRecord {
pub fn new(object: JsValue, environment: Option<Environment>) -> ObjectEnvironmentRecord {
pub fn new(object: JsObject, environment: Option<Environment>) -> ObjectEnvironmentRecord {
ObjectEnvironmentRecord {
bindings: object,
outer_env: environment,
@ -43,124 +43,208 @@ impl ObjectEnvironmentRecord {
}
impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
fn has_binding(&self, name: &str) -> bool {
if self.bindings.has_field(name) {
if self.with_environment {
// TODO: implement unscopables
/// `9.1.1.2.1 HasBinding ( N )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-object-environment-records-hasbinding-n
fn has_binding(&self, name: &str, context: &mut Context) -> JsResult<bool> {
// 1. Let bindingObject be envRec.[[BindingObject]].
// 2. Let foundBinding be ? HasProperty(bindingObject, N).
// 3. If foundBinding is false, return false.
if !self.bindings.has_property(name, context)? {
return Ok(false);
}
// 4. If envRec.[[IsWithEnvironment]] is false, return true.
if !self.with_environment {
return Ok(true);
}
// 5. Let unscopables be ? Get(bindingObject, @@unscopables).
// 6. If Type(unscopables) is Object, then
if let Some(unscopables) = self
.bindings
.get(WellKnownSymbols::unscopables(), context)?
.as_object()
{
// a. Let blocked be ! ToBoolean(? Get(unscopables, N)).
// b. If blocked is true, return false.
if unscopables.get(name, context)?.to_boolean() {
return Ok(false);
}
true
} else {
false
}
// 7. Return true.
Ok(true)
}
/// `9.1.1.2.2 CreateMutableBinding ( N, D )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-object-environment-records-createmutablebinding-n-d
fn create_mutable_binding(
&self,
name: String,
name: &str,
deletion: bool,
_allow_name_reuse: bool,
_context: &mut Context,
context: &mut Context,
) -> JsResult<()> {
// 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 = &self.bindings;
let prop = PropertyDescriptor::builder()
// 1. Let bindingObject be envRec.[[BindingObject]].
// 2. Return ? DefinePropertyOrThrow(bindingObject, N, PropertyDescriptor { [[Value]]: undefined, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: D }).
self.bindings.define_property_or_throw(
name,
PropertyDescriptor::builder()
.value(JsValue::undefined())
.writable(true)
.enumerable(true)
.configurable(deletion);
bindings.set_property(name, prop);
.configurable(deletion),
context,
)?;
Ok(())
}
/// `9.1.1.2.3 CreateImmutableBinding ( N, S )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-object-environment-records-createimmutablebinding-n-s
fn create_immutable_binding(
&self,
_name: String,
_name: &str,
_strict: bool,
_context: &mut Context,
) -> JsResult<()> {
Ok(())
}
/// `9.1.1.2.4 InitializeBinding ( N, V )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-object-environment-records-initializebinding-n-v
fn initialize_binding(
&self,
name: &str,
value: JsValue,
context: &mut Context,
) -> JsResult<()> {
// We should never need to check if a binding has been created,
// As all calls to create_mutable_binding are followed by initialized binding
// The below is just a check.
debug_assert!(self.has_binding(name));
// 1. Return ? envRec.SetMutableBinding(N, V, false).
self.set_mutable_binding(name, value, false, context)
}
/// `9.1.1.2.5 SetMutableBinding ( N, V, S )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-object-environment-records-setmutablebinding-n-v-s
fn set_mutable_binding(
&self,
name: &str,
value: JsValue,
strict: bool,
_context: &mut Context,
context: &mut Context,
) -> JsResult<()> {
debug_assert!(value.is_object() || value.is_function());
let property = PropertyDescriptor::builder()
.value(value)
.enumerable(true)
.configurable(strict);
self.bindings
.as_object()
.expect("binding object")
.insert(name, property);
// 1. Let bindingObject be envRec.[[BindingObject]].
// 2. Let stillExists be ? HasProperty(bindingObject, N).
let still_exists = self.bindings.has_property(name, context)?;
// 3. If stillExists is false and S is true, throw a ReferenceError exception.
if !still_exists && strict {
return Err(context.construct_reference_error("Binding already exists"));
}
// 4. Return ? Set(bindingObject, N, V, S).
self.bindings.set(name, value, strict, context)?;
Ok(())
}
/// `9.1.1.2.6 GetBindingValue ( N, S )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-object-environment-records-getbindingvalue-n-s
fn get_binding_value(
&self,
name: &str,
strict: bool,
context: &mut Context,
) -> JsResult<JsValue> {
if self.bindings.has_field(name) {
Ok(self
.bindings
.get_property(name)
.as_ref()
.and_then(|prop| prop.value())
.cloned()
.unwrap_or_default())
} else if strict {
context.throw_reference_error(format!("{} has no binding", name))
// 1. Let bindingObject be envRec.[[BindingObject]].
// 2. Let value be ? HasProperty(bindingObject, N).
// 3. If value is false, then
if !self.bindings.__has_property__(&name.into(), context)? {
// a. If S is false, return the value undefined; otherwise throw a ReferenceError exception.
if !strict {
return Ok(JsValue::undefined());
} else {
Ok(JsValue::undefined())
return context.throw_reference_error(format!("{} has no binding", name));
}
}
fn delete_binding(&self, name: &str) -> bool {
self.bindings.remove_property(name);
true
// 4. Return ? Get(bindingObject, N).
self.bindings.get(name, context)
}
fn has_this_binding(&self) -> bool {
false
/// `9.1.1.2.7 DeleteBinding ( N )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-object-environment-records-deletebinding-n
fn delete_binding(&self, name: &str, context: &mut Context) -> JsResult<bool> {
// 1. Let bindingObject be envRec.[[BindingObject]].
// 2. Return ? bindingObject.[[Delete]](N).
self.bindings.__delete__(&name.into(), context)
}
fn get_this_binding(&self, _context: &mut Context) -> JsResult<JsValue> {
Ok(JsValue::undefined())
/// `9.1.1.2.8 HasThisBinding ( )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-object-environment-records-hasthisbinding
fn has_this_binding(&self) -> bool {
// 1. Return false.
false
}
/// `9.1.1.2.9 HasSuperBinding ( )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-object-environment-records-hassuperbinding
fn has_super_binding(&self) -> bool {
// 1. Return false.
false
}
/// `9.1.1.2.10 WithBaseObject ( )`
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-object-environment-records-hassuperbinding
fn with_base_object(&self) -> Option<JsObject> {
// Object Environment Records return undefined as their
// WithBaseObject unless their withEnvironment flag is true.
// 1. If envRec.[[IsWithEnvironment]] is true, return envRec.[[BindingObject]].
// 2. Otherwise, return undefined.
if self.with_environment {
return Some(self.bindings.as_object().unwrap());
Some(self.bindings.clone())
} else {
None
}
}
None
fn get_this_binding(&self, _context: &mut Context) -> JsResult<JsValue> {
Ok(JsValue::undefined())
}
fn get_outer_environment_ref(&self) -> Option<&Environment> {

13
boa/src/object/gcobject.rs

@ -199,7 +199,8 @@ impl JsObject {
BindingStatus::Uninitialized
},
JsValue::undefined(),
);
context,
)?;
let mut arguments_in_parameter_names = false;
@ -224,12 +225,7 @@ impl JsObject {
{
// Add arguments object
let arguments_obj = create_unmapped_arguments_object(args, context)?;
local_env.create_mutable_binding(
"arguments".to_string(),
false,
true,
context,
)?;
local_env.create_mutable_binding("arguments", false, true, context)?;
local_env.initialize_binding("arguments", arguments_obj, context)?;
}
@ -280,7 +276,8 @@ impl JsObject {
BindingStatus::Uninitialized
},
JsValue::undefined(),
);
context,
)?;
context.push_environment(second_env);
}

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

@ -95,14 +95,10 @@ impl Executable for FunctionDecl {
FunctionFlags::CONSTRUCTABLE,
)?;
if context.has_binding(self.name()) {
context.set_mutable_binding(self.name(), val, true)?;
if context.has_binding(self.name())? {
context.set_mutable_binding(self.name(), val, context.strict())?;
} else {
context.create_mutable_binding(
self.name().to_owned(),
false,
VariableScope::Function,
)?;
context.create_mutable_binding(self.name(), false, VariableScope::Function)?;
context.initialize_binding(self.name(), val)?;
}

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

@ -104,26 +104,26 @@ impl Executable for DeclarationList {
match &decl {
Declaration::Identifier { ident, init } => {
if self.is_var() && context.has_binding(ident.as_ref()) {
if self.is_var() && context.has_binding(ident.as_ref())? {
if init.is_some() {
context.set_mutable_binding(ident.as_ref(), val, true)?;
context.set_mutable_binding(ident.as_ref(), val, context.strict())?;
}
continue;
}
match &self {
Const(_) => context.create_immutable_binding(
ident.to_string(),
ident.as_ref(),
false,
VariableScope::Block,
)?,
Let(_) => context.create_mutable_binding(
ident.to_string(),
ident.as_ref(),
false,
VariableScope::Block,
)?,
Var(_) => context.create_mutable_binding(
ident.to_string(),
ident.as_ref(),
false,
VariableScope::Function,
)?,
@ -133,26 +133,30 @@ impl Executable for DeclarationList {
}
Declaration::Pattern(p) => {
for (ident, value) in p.run(None, context)? {
if self.is_var() && context.has_binding(ident.as_ref()) {
if self.is_var() && context.has_binding(ident.as_ref())? {
if !value.is_undefined() {
context.set_mutable_binding(ident.as_ref(), value, true)?;
context.set_mutable_binding(
ident.as_ref(),
value,
context.strict(),
)?;
}
continue;
}
match &self {
Const(_) => context.create_immutable_binding(
ident.to_string(),
ident.as_ref(),
false,
VariableScope::Block,
)?,
Let(_) => context.create_mutable_binding(
ident.to_string(),
ident.as_ref(),
false,
VariableScope::Block,
)?,
Var(_) => context.create_mutable_binding(
ident.to_string(),
ident.as_ref(),
false,
VariableScope::Function,
)?,

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

@ -112,12 +112,16 @@ impl Executable for ForInLoop {
match self.variable() {
Node::Identifier(ref name) => {
if context.has_binding(name.as_ref()) {
if context.has_binding(name.as_ref())? {
// Binding already exists
context.set_mutable_binding(name.as_ref(), next_result, true)?;
context.set_mutable_binding(
name.as_ref(),
next_result.clone(),
context.strict(),
)?;
} else {
context.create_mutable_binding(
name.as_ref().to_owned(),
name.as_ref(),
true,
VariableScope::Function,
)?;
@ -132,15 +136,15 @@ impl Executable for ForInLoop {
match &var {
Declaration::Identifier { ident, .. } => {
if context.has_binding(ident.as_ref()) {
if context.has_binding(ident.as_ref())? {
context.set_mutable_binding(
ident.as_ref(),
next_result,
true,
context.strict(),
)?;
} else {
context.create_mutable_binding(
ident.to_string(),
ident.as_ref(),
false,
VariableScope::Function,
)?;
@ -149,11 +153,15 @@ impl Executable for ForInLoop {
}
Declaration::Pattern(p) => {
for (ident, value) in p.run(Some(next_result), context)? {
if context.has_binding(ident.as_ref()) {
context.set_mutable_binding(ident.as_ref(), value, true)?;
if context.has_binding(ident.as_ref())? {
context.set_mutable_binding(
ident.as_ref(),
value,
context.strict(),
)?;
} else {
context.create_mutable_binding(
ident.to_string(),
ident.as_ref(),
false,
VariableScope::Function,
)?;
@ -178,7 +186,7 @@ impl Executable for ForInLoop {
match &var {
Declaration::Identifier { ident, .. } => {
context.create_mutable_binding(
ident.to_string(),
ident.as_ref(),
false,
VariableScope::Block,
)?;
@ -187,7 +195,7 @@ impl Executable for ForInLoop {
Declaration::Pattern(p) => {
for (ident, value) in p.run(Some(next_result), context)? {
context.create_mutable_binding(
ident.to_string(),
ident.as_ref(),
false,
VariableScope::Block,
)?;
@ -211,7 +219,7 @@ impl Executable for ForInLoop {
match &var {
Declaration::Identifier { ident, .. } => {
context.create_immutable_binding(
ident.to_string(),
ident.as_ref(),
false,
VariableScope::Block,
)?;
@ -220,7 +228,7 @@ impl Executable for ForInLoop {
Declaration::Pattern(p) => {
for (ident, value) in p.run(Some(next_result), context)? {
context.create_immutable_binding(
ident.to_string(),
ident.as_ref(),
false,
VariableScope::Block,
)?;

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

@ -100,12 +100,16 @@ impl Executable for ForOfLoop {
match self.variable() {
Node::Identifier(ref name) => {
if context.has_binding(name.as_ref()) {
if context.has_binding(name.as_ref())? {
// Binding already exists
context.set_mutable_binding(name.as_ref(), next_result, true)?;
context.set_mutable_binding(
name.as_ref(),
next_result.clone(),
context.strict(),
)?;
} else {
context.create_mutable_binding(
name.as_ref().to_owned(),
name.as_ref(),
true,
VariableScope::Function,
)?;
@ -120,15 +124,15 @@ impl Executable for ForOfLoop {
match &var {
Declaration::Identifier { ident, .. } => {
if context.has_binding(ident.as_ref()) {
if context.has_binding(ident.as_ref())? {
context.set_mutable_binding(
ident.as_ref(),
next_result,
true,
context.strict(),
)?;
} else {
context.create_mutable_binding(
ident.to_string(),
ident.as_ref(),
false,
VariableScope::Function,
)?;
@ -137,11 +141,15 @@ impl Executable for ForOfLoop {
}
Declaration::Pattern(p) => {
for (ident, value) in p.run(Some(next_result), context)? {
if context.has_binding(ident.as_ref()) {
context.set_mutable_binding(ident.as_ref(), value, true)?;
if context.has_binding(ident.as_ref())? {
context.set_mutable_binding(
ident.as_ref(),
value,
context.strict(),
)?;
} else {
context.create_mutable_binding(
ident.to_string(),
ident.as_ref(),
false,
VariableScope::Function,
)?;
@ -166,7 +174,7 @@ impl Executable for ForOfLoop {
match &var {
Declaration::Identifier { ident, .. } => {
context.create_mutable_binding(
ident.to_string(),
ident.as_ref(),
false,
VariableScope::Block,
)?;
@ -175,7 +183,7 @@ impl Executable for ForOfLoop {
Declaration::Pattern(p) => {
for (ident, value) in p.run(Some(next_result), context)? {
context.create_mutable_binding(
ident.to_string(),
ident.as_ref(),
false,
VariableScope::Block,
)?;
@ -199,7 +207,7 @@ impl Executable for ForOfLoop {
match &var {
Declaration::Identifier { ident, .. } => {
context.create_immutable_binding(
ident.to_string(),
ident.as_ref(),
false,
VariableScope::Block,
)?;
@ -208,7 +216,7 @@ impl Executable for ForOfLoop {
Declaration::Pattern(p) => {
for (ident, value) in p.run(Some(next_result), context)? {
context.create_immutable_binding(
ident.to_string(),
ident.as_ref(),
false,
VariableScope::Block,
)?;

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

@ -58,15 +58,11 @@ impl Executable for Assign {
let val = self.rhs().run(context)?;
match self.lhs() {
Node::Identifier(ref name) => {
if context.has_binding(name.as_ref()) {
if context.has_binding(name.as_ref())? {
// Binding already exists
context.set_mutable_binding(name.as_ref(), val.clone(), true)?;
context.set_mutable_binding(name.as_ref(), val.clone(), context.strict())?;
} else {
context.create_mutable_binding(
name.as_ref().to_owned(),
true,
VariableScope::Function,
)?;
context.create_mutable_binding(name.as_ref(), true, VariableScope::Function)?;
context.initialize_binding(name.as_ref(), val.clone())?;
}
}

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

@ -203,7 +203,7 @@ impl Executable for BinOp {
let v_a = context.get_binding_value(name.as_ref())?;
let value = Self::run_assign(op, v_a, self.rhs(), context)?;
context.set_mutable_binding(name.as_ref(), value.clone(), true)?;
context.set_mutable_binding(name.as_ref(), value.clone(), context.strict())?;
Ok(value)
}
Node::GetConstField(ref get_const_field) => {

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

@ -105,11 +105,7 @@ impl Executable for Try {
context.push_environment(DeclarativeEnvironmentRecord::new(Some(env)));
if let Some(param) = catch.parameter() {
context.create_mutable_binding(
param.to_owned(),
false,
VariableScope::Block,
)?;
context.create_mutable_binding(param, false, VariableScope::Block)?;
context.initialize_binding(param, err)?;
}
}

26
boa/src/value/mod.rs

@ -154,19 +154,6 @@ impl JsValue {
}
}
/// This will tell us if we can exten an object or not, not properly implemented yet
///
/// For now always returns true.
///
/// For scalar types it should be false, for objects check the private field for extensibilaty.
/// By default true.
///
/// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal> would turn `extensible` to `false`
/// <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/freeze> would also turn `extensible` to `false`
pub(crate) fn is_extensible(&self) -> bool {
true
}
/// Returns true if the value is an object
#[inline]
pub fn is_object(&self) -> bool {
@ -365,19 +352,6 @@ impl JsValue {
}
}
/// Check to see if the Value has the field, mainly used by environment records.
#[inline]
pub(crate) fn has_field<K>(&self, key: K) -> bool
where
K: Into<PropertyKey>,
{
let _timer = BoaProfiler::global().start_event("Value::has_field", "value");
// todo: call `__has_property__` instead of directly getting from object
self.as_object()
.map(|object| object.borrow().properties().contains_key(&key.into()))
.unwrap_or(false)
}
/// Set the field in the value
///
/// Similar to `7.3.4 Set ( O, P, V, Throw )`, but returns the value instead of a boolean.

28
boa/src/vm/mod.rs

@ -259,28 +259,22 @@ impl<'a> Vm<'a> {
let index = self.read::<u32>();
let name = &self.code.names[index as usize];
self.context.create_mutable_binding(
name.to_string(),
false,
VariableScope::Function,
)?;
self.context
.create_mutable_binding(name, false, VariableScope::Function)?;
}
Opcode::DefLet => {
let index = self.read::<u32>();
let name = &self.code.names[index as usize];
self.context.create_mutable_binding(
name.to_string(),
false,
VariableScope::Block,
)?;
self.context
.create_mutable_binding(name, false, VariableScope::Block)?;
}
Opcode::DefConst => {
let index = self.read::<u32>();
let name = &self.code.names[index as usize];
self.context.create_immutable_binding(
name.to_string(),
name.as_ref(),
false,
VariableScope::Block,
)?;
@ -304,15 +298,13 @@ impl<'a> Vm<'a> {
let value = self.pop();
let name = &self.code.names[index as usize];
if self.context.has_binding(name) {
if self.context.has_binding(name)? {
// Binding already exists
self.context.set_mutable_binding(name, value, true)?;
self.context
.set_mutable_binding(name, value, self.context.strict())?;
} else {
self.context.create_mutable_binding(
name.to_string(),
true,
VariableScope::Function,
)?;
self.context
.create_mutable_binding(name, true, VariableScope::Function)?;
self.context.initialize_binding(name, value)?;
}
}

Loading…
Cancel
Save