Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix environment record panics #1285

Merged
merged 10 commits into from
Jun 2, 2021
68 changes: 38 additions & 30 deletions boa/src/environment/declarative_environment_record.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,44 +34,51 @@ pub struct DeclarativeEnvironmentRecordBinding {
/// declarations contained within its scope.
#[derive(Debug, Trace, Finalize, Clone)]
pub struct DeclarativeEnvironmentRecord {
pub env_rec: FxHashMap<Box<str>, DeclarativeEnvironmentRecordBinding>,
pub env_rec: GcCell<FxHashMap<Box<str>, DeclarativeEnvironmentRecordBinding>>,
pub outer_env: Option<Environment>,
}

impl DeclarativeEnvironmentRecord {
pub fn new_declarative_environment_record(
env: Option<Environment>,
) -> DeclarativeEnvironmentRecord {
DeclarativeEnvironmentRecord {
env_rec: GcCell::new(FxHashMap::default()),
outer_env: env,
}
}

#[allow(clippy::new_ret_no_self)]
pub fn new(env: Option<Environment>) -> Environment {
0x7D2B marked this conversation as resolved.
Show resolved Hide resolved
let _timer = BoaProfiler::global().start_event("new_declarative_environment", "env");
let boxed_env = Box::new(DeclarativeEnvironmentRecord {
env_rec: FxHashMap::default(),
outer_env: env,
});

Gc::new(GcCell::new(boxed_env))
Gc::new(GcCell::new(Box::new(
Self::new_declarative_environment_record(env),
)))
}
}

impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
fn has_binding(&self, name: &str) -> bool {
self.env_rec.contains_key(name)
self.env_rec.borrow().contains_key(name)
}

fn create_mutable_binding(
&mut self,
&self,
name: String,
deletion: bool,
allow_name_reuse: bool,
_context: &mut Context,
) -> Result<()> {
if !allow_name_reuse {
assert!(
!self.env_rec.contains_key(name.as_str()),
!self.env_rec.borrow().contains_key(name.as_str()),
"Identifier {} has already been declared",
name
);
}

self.env_rec.insert(
self.env_rec.borrow_mut().insert(
name.into_boxed_str(),
DeclarativeEnvironmentRecordBinding {
value: None,
Expand All @@ -84,18 +91,18 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
}

fn create_immutable_binding(
&mut self,
&self,
name: String,
strict: bool,
_context: &mut Context,
) -> Result<()> {
assert!(
!self.env_rec.contains_key(name.as_str()),
!self.env_rec.borrow().contains_key(name.as_str()),
"Identifier {} has already been declared",
name
);

self.env_rec.insert(
self.env_rec.borrow_mut().insert(
name.into_boxed_str(),
DeclarativeEnvironmentRecordBinding {
value: None,
Expand All @@ -107,13 +114,8 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
Ok(())
}

fn initialize_binding(
&mut self,
name: &str,
value: Value,
_context: &mut Context,
) -> Result<()> {
if let Some(ref mut record) = self.env_rec.get_mut(name) {
fn initialize_binding(&self, name: &str, value: Value, _context: &mut Context) -> Result<()> {
if let Some(ref mut record) = self.env_rec.borrow_mut().get_mut(name) {
if record.value.is_none() {
record.value = Some(value);
return Ok(());
Expand All @@ -124,13 +126,13 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {

#[allow(clippy::else_if_without_else)]
fn set_mutable_binding(
&mut self,
&self,
name: &str,
value: Value,
mut strict: bool,
context: &mut Context,
) -> Result<()> {
if self.env_rec.get(name).is_none() {
if self.env_rec.borrow().get(name).is_none() {
if strict {
return Err(context.construct_reference_error(format!("{} not found", name)));
}
Expand All @@ -140,16 +142,22 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
return Ok(());
}

let record: &mut DeclarativeEnvironmentRecordBinding = self.env_rec.get_mut(name).unwrap();
if record.strict {
let (record_strict, record_has_no_value, record_mutable) = {
let env_rec = self.env_rec.borrow();
let record = env_rec.get(name).unwrap();
(record.strict, record.value.is_none(), record.mutable)
};
if record_strict {
strict = true
}
if record.value.is_none() {
if record_has_no_value {
return Err(
context.construct_reference_error(format!("{} has not been initialized", name))
);
}
if record.mutable {
if record_mutable {
let mut env_rec = self.env_rec.borrow_mut();
let record = env_rec.get_mut(name).unwrap();
record.value = Some(value);
} else if strict {
return Err(context.construct_reference_error(format!(
Expand All @@ -162,7 +170,7 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
}

fn get_binding_value(&self, name: &str, _strict: bool, context: &mut Context) -> Result<Value> {
if let Some(binding) = self.env_rec.get(name) {
if let Some(binding) = self.env_rec.borrow().get(name) {
if let Some(ref val) = binding.value {
Ok(val.clone())
} else {
Expand All @@ -173,11 +181,11 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
}
}

fn delete_binding(&mut self, name: &str) -> bool {
match self.env_rec.get(name) {
fn delete_binding(&self, name: &str) -> bool {
match self.env_rec.borrow().get(name) {
Some(binding) => {
if binding.can_delete {
self.env_rec.remove(name);
self.env_rec.borrow_mut().remove(name);
true
} else {
false
Expand Down
27 changes: 13 additions & 14 deletions boa/src/environment/environment_record_trait.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize {
/// Most variable names cannot be reused, but functions in JavaScript are allowed to have multiple
/// paraments with the same name.
fn create_mutable_binding(
&mut self,
&self,
name: String,
deletion: bool,
allow_name_reuse: bool,
Expand All @@ -46,7 +46,7 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize {
/// 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,
&self,
name: String,
strict: bool,
context: &mut Context,
Expand All @@ -55,15 +55,14 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize {
/// 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, context: &mut Context)
-> Result<()>;
fn initialize_binding(&self, name: &str, value: Value, context: &mut Context) -> Result<()>;

/// Set the value of an already existing mutable binding in an Environment Record.
/// The String value `name` is the text of the bound name.
/// 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,
&self,
name: &str,
value: Value,
strict: bool,
Expand All @@ -80,7 +79,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(&mut self, name: &str) -> bool;
fn delete_binding(&self, name: &str) -> 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 @@ -123,7 +122,7 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize {

/// Create mutable binding while handling outer environments
fn recursive_create_mutable_binding(
&mut self,
&self,
name: String,
deletion: bool,
scope: VariableScope,
Expand All @@ -134,14 +133,14 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize {
VariableScope::Function => self
.get_outer_environment_ref()
.expect("No function or global environment")
.borrow_mut()
.borrow()
.recursive_create_mutable_binding(name, deletion, scope, context),
}
}

/// Create immutable binding while handling outer environments
fn recursive_create_immutable_binding(
&mut self,
&self,
name: String,
deletion: bool,
scope: VariableScope,
Expand All @@ -152,14 +151,14 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize {
VariableScope::Function => self
.get_outer_environment_ref()
.expect("No function or global environment")
.borrow_mut()
.borrow()
.recursive_create_immutable_binding(name, deletion, scope, context),
}
}

/// Set mutable binding while handling outer environments
fn recursive_set_mutable_binding(
&mut self,
&self,
name: &str,
value: Value,
strict: bool,
Expand All @@ -170,14 +169,14 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize {
} else {
self.get_outer_environment_ref()
.expect("Environment stack underflow")
.borrow_mut()
.borrow()
.recursive_set_mutable_binding(name, value, strict, context)
}
}

/// Initialize binding while handling outer environments
fn recursive_initialize_binding(
&mut self,
&self,
name: &str,
value: Value,
context: &mut Context,
Expand All @@ -187,7 +186,7 @@ pub trait EnvironmentRecordTrait: Debug + Trace + Finalize {
} else {
self.get_outer_environment_ref()
.expect("Environment stack underflow")
.borrow_mut()
.borrow()
.recursive_initialize_binding(name, value, context)
}
}
Expand Down
27 changes: 10 additions & 17 deletions boa/src/environment/function_environment_record.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
//! More info: <https://tc39.es/ecma262/#sec-function-environment-records>

use gc::{Gc, GcCell};
use rustc_hash::FxHashMap;

use crate::{
environment::{
Expand Down Expand Up @@ -68,10 +67,9 @@ impl FunctionEnvironmentRecord {
new_target: Value,
) -> Environment {
let mut func_env = FunctionEnvironmentRecord {
declarative_record: DeclarativeEnvironmentRecord {
env_rec: FxHashMap::default(),
outer_env: outer, // this will come from Environment set as a private property of F - https://tc39.es/ecma262/#sec-ecmascript-function-objects
},
declarative_record: DeclarativeEnvironmentRecord::new_declarative_environment_record(
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,
this_binding_status: binding_status,
home_object: Value::undefined(),
Expand Down Expand Up @@ -123,7 +121,7 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
}

fn create_mutable_binding(
&mut self,
&self,
name: String,
deletion: bool,
allow_name_reuse: bool,
Expand All @@ -134,7 +132,7 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
}

fn create_immutable_binding(
&mut self,
&self,
name: String,
strict: bool,
context: &mut Context,
Expand All @@ -143,18 +141,13 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
.create_immutable_binding(name, strict, context)
}

fn initialize_binding(
&mut self,
name: &str,
value: Value,
context: &mut Context,
) -> Result<()> {
fn initialize_binding(&self, name: &str, value: Value, context: &mut Context) -> Result<()> {
self.declarative_record
.initialize_binding(name, value, context)
}

fn set_mutable_binding(
&mut self,
&self,
name: &str,
value: Value,
strict: bool,
Expand All @@ -169,7 +162,7 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
.get_binding_value(name, strict, context)
}

fn delete_binding(&mut self, name: &str) -> bool {
fn delete_binding(&self, name: &str) -> bool {
self.declarative_record.delete_binding(name)
}

Expand Down Expand Up @@ -214,7 +207,7 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
}

fn recursive_create_mutable_binding(
&mut self,
&self,
name: String,
deletion: bool,
_scope: VariableScope,
Expand All @@ -224,7 +217,7 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
}

fn recursive_create_immutable_binding(
&mut self,
&self,
name: String,
deletion: bool,
_scope: VariableScope,
Expand Down
Loading