Skip to content

Commit

Permalink
Remove strict flag from Context (#2069)
Browse files Browse the repository at this point in the history
The `Context` currently contains a `strict` flag that indicates is global strict mode is active. This is redundant to the strict flag that is set on every function and causes some non spec compliant situations. This pull request removes the strict flag from `Context` and fixes some resulting errors.

Detailed changes:

- Remove strict flag from `Context`
- Make 262 tester compliant with the strict section in [test262/INTERPRETING.md](https://github.com/tc39/test262/blob/2e7cdfbe18eae4309677033673bb4b5ac6b1de40/INTERPRETING.md#strict-mode)
- Make 262 tester compliant with the `raw` flag in [test262/INTERPRETING.md](https://github.com/tc39/test262/blob/2e7cdfbe18eae4309677033673bb4b5ac6b1de40/INTERPRETING.md#flags)
- Allow function declarations in strict mode
- Fix parser flag propagation for classes
- Move some early errors from the lexer to the parser
- Add / fix some early errors for 'arguments' and 'eval' identifier usage in strict mode
- Refactor `ArrayLiteral` parser for readability and correct early errors
  • Loading branch information
raskad committed May 21, 2022
1 parent ace28e5 commit 45dd2d4
Show file tree
Hide file tree
Showing 29 changed files with 839 additions and 293 deletions.
2 changes: 1 addition & 1 deletion boa_cli/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ where
use boa_engine::syntax::parser::Parser;

let src_bytes = src.as_ref();
Parser::new(src_bytes, false)
Parser::new(src_bytes)
.parse_all(context)
.map_err(|e| format!("ParsingError: {e}"))
}
Expand Down
7 changes: 6 additions & 1 deletion boa_engine/src/builtins/eval/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,12 @@ impl Eval {

// Parse the script body (11.a - 11.d)
// TODO: Implement errors for 11.e - 11.h
let body = match context.parse(x.as_bytes()).map_err(|e| e.to_string()) {
let parse_result = if strict {
context.parse_strict(x.as_bytes())
} else {
context.parse(x.as_bytes())
};
let body = match parse_result.map_err(|e| e.to_string()) {
Ok(body) => body,
Err(e) => return context.throw_syntax_error(e),
};
Expand Down
32 changes: 14 additions & 18 deletions boa_engine/src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,9 +82,6 @@ pub struct Context {
/// Intrinsic objects
intrinsics: Intrinsics,

/// Whether or not global strict mode is active.
strict: bool,

pub(crate) vm: Vm,
}

Expand All @@ -96,7 +93,6 @@ impl Default for Context {
#[cfg(feature = "console")]
console: Console::default(),
intrinsics: Intrinsics::default(),
strict: false,
vm: Vm {
frame: None,
stack: Vec::with_capacity(1024),
Expand Down Expand Up @@ -149,18 +145,6 @@ impl Context {
&mut self.console
}

/// Returns if strict mode is currently active.
#[inline]
pub fn strict(&self) -> bool {
self.strict
}

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

/// Sets up the default global objects within Global
#[inline]
fn create_intrinsics(&mut self) {
Expand All @@ -178,11 +162,23 @@ impl Context {
)
}

/// Parse the given source text.
pub fn parse<S>(&mut self, src: S) -> Result<StatementList, ParseError>
where
S: AsRef<[u8]>,
{
Parser::new(src.as_ref(), self.strict).parse_all(self)
let mut parser = Parser::new(src.as_ref());
parser.parse_all(self)
}

/// Parse the given source text in strict mode.
pub(crate) fn parse_strict<S>(&mut self, src: S) -> Result<StatementList, ParseError>
where
S: AsRef<[u8]>,
{
let mut parser = Parser::new(src.as_ref());
parser.set_strict();
parser.parse_all(self)
}

/// <https://tc39.es/ecma262/#sec-call>
Expand Down Expand Up @@ -641,7 +637,7 @@ impl Context {
{
let main_timer = Profiler::global().start_event("Evaluation", "Main");

let parsing_result = Parser::new(src.as_ref(), false)
let parsing_result = Parser::new(src.as_ref())
.parse_all(self)
.map_err(|e| e.to_string());

Expand Down
7 changes: 7 additions & 0 deletions boa_engine/src/syntax/ast/node/await_expr/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ pub struct AwaitExpr {
expr: Box<Node>,
}

impl AwaitExpr {
/// Return the expression that should be awaited.
pub(crate) fn expr(&self) -> &Node {
&self.expr
}
}

impl<T> From<T> for AwaitExpr
where
T: Into<Box<Node>>,
Expand Down
108 changes: 108 additions & 0 deletions boa_engine/src/syntax/ast/node/declaration/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,114 @@ impl DeclarationPattern {
DeclarationPattern::Array(pattern) => pattern.init(),
}
}

/// Returns true if the node contains a identifier reference named 'arguments'.
///
/// More information:
/// - [ECMAScript specification][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-static-semantics-containsarguments
#[inline]
pub(crate) fn contains_arguments(&self) -> bool {
match self {
DeclarationPattern::Object(pattern) => {
if let Some(init) = pattern.init() {
if init.contains_arguments() {
return true;
}
}
for binding in pattern.bindings() {
match binding {
BindingPatternTypeObject::SingleName {
property_name,
default_init,
..
} => {
if let PropertyName::Computed(node) = property_name {
if node.contains_arguments() {
return true;
}
}
if let Some(init) = default_init {
if init.contains_arguments() {
return true;
}
}
}
BindingPatternTypeObject::RestGetConstField {
get_const_field, ..
} => {
if get_const_field.obj().contains_arguments() {
return true;
}
}
BindingPatternTypeObject::BindingPattern {
ident,
pattern,
default_init,
} => {
if let PropertyName::Computed(node) = ident {
if node.contains_arguments() {
return true;
}
}
if pattern.contains_arguments() {
return true;
}
if let Some(init) = default_init {
if init.contains_arguments() {
return true;
}
}
}
_ => {}
}
}
}
DeclarationPattern::Array(pattern) => {
if let Some(init) = pattern.init() {
if init.contains_arguments() {
return true;
}
}
for binding in pattern.bindings() {
match binding {
BindingPatternTypeArray::SingleName {
default_init: Some(init),
..
} => {
if init.contains_arguments() {
return true;
}
}
BindingPatternTypeArray::GetField { get_field }
| BindingPatternTypeArray::GetFieldRest { get_field } => {
if get_field.obj().contains_arguments() {
return true;
}
if get_field.field().contains_arguments() {
return true;
}
}
BindingPatternTypeArray::GetConstField { get_const_field }
| BindingPatternTypeArray::GetConstFieldRest { get_const_field } => {
if get_const_field.obj().contains_arguments() {
return true;
}
}
BindingPatternTypeArray::BindingPattern { pattern }
| BindingPatternTypeArray::BindingPatternRest { pattern } => {
if pattern.contains_arguments() {
return true;
}
}
_ => {}
}
}
}
}
false
}
}

/// `DeclarationPatternObject` represents an object binding pattern.
Expand Down
23 changes: 22 additions & 1 deletion boa_engine/src/syntax/ast/node/identifier/mod.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
//! Local identifier node.
use crate::syntax::ast::node::Node;
use crate::syntax::{
ast::{node::Node, Position},
parser::ParseError,
};
use boa_gc::{unsafe_empty_trace, Finalize, Trace};
use boa_interner::{Interner, Sym, ToInternedString};

Expand Down Expand Up @@ -39,6 +42,24 @@ impl Identifier {
pub fn sym(self) -> Sym {
self.ident
}

/// Returns an error if `arguments` or `eval` are used as identifier in strict mode.
pub(crate) fn check_strict_arguments_or_eval(
self,
position: Position,
) -> Result<(), ParseError> {
match self.ident {
Sym::ARGUMENTS => Err(ParseError::general(
"unexpected identifier 'arguments' in strict mode",
position,
)),
Sym::EVAL => Err(ParseError::general(
"unexpected identifier 'eval' in strict mode",
position,
)),
_ => Ok(()),
}
}
}

impl ToInternedString for Identifier {
Expand Down
Loading

0 comments on commit 45dd2d4

Please sign in to comment.