Skip to content

Commit

Permalink
Add strict mode flag to Context (#1550)
Browse files Browse the repository at this point in the history
* Add strict mode flag to `Context`

* Add strict mode handling of `delete`

* Add strict mode test

* Handle non-strict functions in strict functions

* Enable strict mode for functions defined in a strict context
  • Loading branch information
raskad authored Sep 9, 2021
1 parent 59659a6 commit f1b5358
Show file tree
Hide file tree
Showing 10 changed files with 265 additions and 34 deletions.
61 changes: 56 additions & 5 deletions boa/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,14 @@ impl StandardObjects {
}
}

/// Internal representation of the strict mode types.
#[derive(Debug, Copy, Clone)]
pub(crate) enum StrictType {
Off,
Global,
Function,
}

/// Javascript context. It is the primary way to interact with the runtime.
///
/// `Context`s constructed in a thread share the same runtime, therefore it
Expand Down Expand Up @@ -273,6 +281,9 @@ pub struct Context {

/// Whether or not to show trace of instructions being ran
pub trace: bool,

/// Whether or not strict mode is active.
strict: StrictType,
}

impl Default for Context {
Expand All @@ -287,6 +298,7 @@ impl Default for Context {
iterator_prototypes: IteratorPrototypes::default(),
standard_objects: Default::default(),
trace: false,
strict: StrictType::Off,
};

// Add new builtIns to Context Realm
Expand Down Expand Up @@ -323,6 +335,36 @@ impl Context {
&mut self.console
}

/// Returns if strict mode is currently active.
#[inline]
pub fn strict(&self) -> bool {
matches!(self.strict, StrictType::Global | StrictType::Function)
}

/// Returns the strict mode type.
#[inline]
pub(crate) fn strict_type(&self) -> StrictType {
self.strict
}

/// Set strict type.
#[inline]
pub(crate) fn set_strict(&mut self, strict: StrictType) {
self.strict = strict;
}

/// Disable the strict mode.
#[inline]
pub fn set_strict_mode_off(&mut self) {
self.strict = StrictType::Off;
}

/// Enable the global strict mode.
#[inline]
pub fn set_strict_mode_global(&mut self) {
self.strict = StrictType::Global;
}

/// Sets up the default global objects within Global
#[inline]
fn create_intrinsics(&mut self) {
Expand Down Expand Up @@ -519,17 +561,16 @@ impl Context {
}

/// Utility to create a function Value for Function Declarations, Arrow Functions or Function Expressions
pub(crate) fn create_function<N, P, B>(
pub(crate) fn create_function<N, P>(
&mut self,
name: N,
params: P,
body: B,
mut body: StatementList,
flags: FunctionFlags,
) -> JsResult<JsValue>
where
N: Into<JsString>,
P: Into<Box<[FormalParameter]>>,
B: Into<StatementList>,
{
let name = name.into();
let function_prototype: JsValue =
Expand All @@ -538,11 +579,16 @@ impl Context {
// Every new function has a prototype property pre-made
let prototype = self.construct_object();

// If a function is defined within a strict context, it is strict.
if self.strict() {
body.set_strict(true);
}

let params = params.into();
let params_len = params.len();
let func = Function::Ordinary {
flags,
body: RcStatementList::from(body.into()),
body: RcStatementList::from(body),
params,
environment: self.get_current_environment().clone(),
};
Expand Down Expand Up @@ -790,7 +836,12 @@ impl Context {
.map_err(|e| e.to_string());

let execution_result = match parsing_result {
Ok(statement_list) => statement_list.run(self),
Ok(statement_list) => {
if statement_list.strict() {
self.set_strict_mode_global();
}
statement_list.run(self)
}
Err(e) => self.throw_syntax_error(e),
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ impl ArrowFunctionDecl {
}

/// Gets the body of the arrow function.
pub(crate) fn body(&self) -> &[Node] {
self.body.items()
pub(crate) fn body(&self) -> &StatementList {
&self.body
}

/// Implements the display formatting with indentation.
Expand All @@ -61,7 +61,7 @@ impl ArrowFunctionDecl {
) -> fmt::Result {
write!(f, "(")?;
join_nodes(f, &self.params)?;
if self.body().is_empty() {
if self.body().items().is_empty() {
f.write_str(") => {}")
} else {
f.write_str(") => {\n")?;
Expand All @@ -76,7 +76,7 @@ impl Executable for ArrowFunctionDecl {
context.create_function(
"",
self.params().to_vec(),
self.body().to_vec(),
self.body().clone(),
FunctionFlags::LEXICAL_THIS_MODE,
)
}
Expand Down
8 changes: 4 additions & 4 deletions boa/src/syntax/ast/node/declaration/function_decl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ impl FunctionDecl {
}

/// Gets the body of the function declaration.
pub fn body(&self) -> &[Node] {
self.body.items()
pub fn body(&self) -> &StatementList {
&self.body
}

/// Implements the display formatting with indentation.
Expand All @@ -75,7 +75,7 @@ impl FunctionDecl {
) -> fmt::Result {
write!(f, "function {}(", self.name)?;
join_nodes(f, &self.parameters)?;
if self.body().is_empty() {
if self.body().items().is_empty() {
f.write_str(") {}")
} else {
f.write_str(") {\n")?;
Expand All @@ -91,7 +91,7 @@ impl Executable for FunctionDecl {
let val = context.create_function(
self.name(),
self.parameters().to_vec(),
self.body().to_vec(),
self.body().clone(),
FunctionFlags::CONSTRUCTABLE,
)?;

Expand Down
8 changes: 4 additions & 4 deletions boa/src/syntax/ast/node/declaration/function_expr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ impl FunctionExpr {
}

/// Gets the body of the function declaration.
pub fn body(&self) -> &[Node] {
self.body.items()
pub fn body(&self) -> &StatementList {
&self.body
}

/// Implements the display formatting with indentation.
Expand All @@ -87,7 +87,7 @@ impl FunctionExpr {
f: &mut fmt::Formatter<'_>,
indentation: usize,
) -> fmt::Result {
if self.body().is_empty() {
if self.body().items().is_empty() {
f.write_str("{}")
} else {
f.write_str("{\n")?;
Expand All @@ -102,7 +102,7 @@ impl Executable for FunctionExpr {
let val = context.create_function(
self.name().unwrap_or(""),
self.parameters().to_vec(),
self.body().to_vec(),
self.body().clone(),
FunctionFlags::CONSTRUCTABLE,
)?;

Expand Down
14 changes: 10 additions & 4 deletions boa/src/syntax/ast/node/operator/unary_op/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,13 +90,19 @@ impl Executable for UnaryOp {
JsValue::undefined()
}
op::UnaryOp::Delete => match *self.target() {
Node::GetConstField(ref get_const_field) => JsValue::new(
get_const_field
Node::GetConstField(ref get_const_field) => {
let delete_status = get_const_field
.obj()
.run(context)?
.to_object(context)?
.__delete__(&get_const_field.field().into(), context)?,
),
.__delete__(&get_const_field.field().into(), context)?;

if !delete_status && context.strict() {
return context.throw_type_error("Cannot delete property");
} else {
JsValue::new(delete_status)
}
}
Node::GetField(ref get_field) => {
let obj = get_field.obj().run(context)?;
let field = &get_field.field().run(context)?;
Expand Down
42 changes: 40 additions & 2 deletions boa/src/syntax/ast/node/statement_list/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
//! Statement list node.

use crate::{
context::StrictType,
exec::{Executable, InterpreterState},
gc::{empty_trace, Finalize, Trace},
syntax::ast::node::{Declaration, Node},
Expand All @@ -11,6 +12,9 @@ use std::{collections::HashSet, fmt, ops::Deref, rc::Rc};
#[cfg(feature = "deser")]
use serde::{Deserialize, Serialize};

#[cfg(test)]
mod tests;

/// List of statements.
///
/// Similar to `Node::Block` but without the braces.
Expand All @@ -24,14 +28,28 @@ use serde::{Deserialize, Serialize};
pub struct StatementList {
#[cfg_attr(feature = "deser", serde(flatten))]
items: Box<[Node]>,
strict: bool,
}

impl StatementList {
/// Gets the list of items.
#[inline]
pub fn items(&self) -> &[Node] {
&self.items
}

/// Get the strict mode.
#[inline]
pub fn strict(&self) -> bool {
self.strict
}

/// Set the strict mode.
#[inline]
pub fn set_strict(&mut self, strict: bool) {
self.strict = strict;
}

/// Implements the display formatting with indentation.
pub(in crate::syntax::ast::node) fn display(
&self,
Expand Down Expand Up @@ -121,8 +139,23 @@ impl Executable for StatementList {
context
.executor()
.set_current_state(InterpreterState::Executing);

let strict_before = context.strict_type();

match context.strict_type() {
StrictType::Off if self.strict => context.set_strict(StrictType::Function),
StrictType::Function if !self.strict => context.set_strict_mode_off(),
_ => {}
}

for (i, item) in self.items().iter().enumerate() {
let val = item.run(context)?;
let val = match item.run(context) {
Ok(val) => val,
Err(e) => {
context.set_strict(strict_before);
return Err(e);
}
};
match context.executor().get_current_state() {
InterpreterState::Return => {
// Early return.
Expand All @@ -145,6 +178,8 @@ impl Executable for StatementList {
}
}

context.set_strict(strict_before);

Ok(obj)
}
}
Expand All @@ -154,7 +189,10 @@ where
T: Into<Box<[Node]>>,
{
fn from(stm: T) -> Self {
Self { items: stm.into() }
Self {
items: stm.into(),
strict: false,
}
}
}

Expand Down
Loading

0 comments on commit f1b5358

Please sign in to comment.