Browse Source

Remove `strict` flag from `Context` (#2069)

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](2e7cdfbe18/INTERPRETING.md (strict-mode))
- Make 262 tester compliant with the `raw` flag in [test262/INTERPRETING.md](2e7cdfbe18/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
pull/2084/head
raskad 3 years ago
parent
commit
45dd2d416c
  1. 2
      boa_cli/src/main.rs
  2. 7
      boa_engine/src/builtins/eval/mod.rs
  3. 32
      boa_engine/src/context/mod.rs
  4. 7
      boa_engine/src/syntax/ast/node/await_expr/mod.rs
  5. 108
      boa_engine/src/syntax/ast/node/declaration/mod.rs
  6. 23
      boa_engine/src/syntax/ast/node/identifier/mod.rs
  7. 435
      boa_engine/src/syntax/ast/node/mod.rs
  8. 29
      boa_engine/src/syntax/ast/node/operator/assign/mod.rs
  9. 32
      boa_engine/src/syntax/lexer/identifier.rs
  10. 2
      boa_engine/src/syntax/parser/error.rs
  11. 18
      boa_engine/src/syntax/parser/expression/assignment/mod.rs
  12. 2
      boa_engine/src/syntax/parser/expression/identifiers.rs
  13. 1
      boa_engine/src/syntax/parser/expression/mod.rs
  14. 85
      boa_engine/src/syntax/parser/expression/primary/array_initializer/mod.rs
  15. 3
      boa_engine/src/syntax/parser/expression/primary/mod.rs
  16. 56
      boa_engine/src/syntax/parser/expression/update.rs
  17. 1
      boa_engine/src/syntax/parser/function/mod.rs
  18. 2
      boa_engine/src/syntax/parser/function/tests.rs
  19. 20
      boa_engine/src/syntax/parser/mod.rs
  20. 1
      boa_engine/src/syntax/parser/statement/block/mod.rs
  21. 32
      boa_engine/src/syntax/parser/statement/declaration/hoistable/class_decl/mod.rs
  22. 2
      boa_engine/src/syntax/parser/statement/declaration/hoistable/mod.rs
  23. 32
      boa_engine/src/syntax/parser/statement/mod.rs
  24. 2
      boa_engine/src/syntax/parser/statement/switch/mod.rs
  25. 6
      boa_engine/src/syntax/parser/tests.rs
  26. 18
      boa_engine/src/tests.rs
  27. 85
      boa_engine/src/vm/code_block.rs
  28. 29
      boa_engine/src/vm/mod.rs
  29. 60
      boa_tester/src/exec/mod.rs

2
boa_cli/src/main.rs

@ -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}"))
}

7
boa_engine/src/builtins/eval/mod.rs

@ -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),
};

32
boa_engine/src/context/mod.rs

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

7
boa_engine/src/syntax/ast/node/await_expr/mod.rs

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

108
boa_engine/src/syntax/ast/node/declaration/mod.rs

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

23
boa_engine/src/syntax/ast/node/identifier/mod.rs

@ -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};
@ -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 {

435
boa_engine/src/syntax/ast/node/mod.rs

@ -23,7 +23,6 @@ pub mod throw;
pub mod try_node;
pub mod r#yield;
use self::field::get_private_field::GetPrivateField;
pub use self::{
array::ArrayDecl,
await_expr::AwaitExpr,
@ -36,7 +35,7 @@ pub use self::{
ArrowFunctionDecl, AsyncFunctionDecl, AsyncFunctionExpr, Declaration, DeclarationList,
DeclarationPattern, FunctionDecl, FunctionExpr,
},
field::{GetConstField, GetField},
field::{get_private_field::GetPrivateField, GetConstField, GetField},
identifier::Identifier,
iteration::{Break, Continue, DoWhileLoop, ForInLoop, ForLoop, ForOfLoop, WhileLoop},
new::New,
@ -52,6 +51,11 @@ pub use self::{
throw::Throw,
try_node::{Catch, Finally, Try},
};
use self::{
declaration::class_decl::ClassElement,
iteration::IterableLoopInitializer,
object::{MethodDefinition, PropertyDefinition},
};
pub(crate) use self::parameters::FormalParameterListFlags;
@ -397,7 +401,7 @@ impl Node {
for_loop.body().var_declared_names(vars);
}
Node::ForInLoop(for_in_loop) => {
if let iteration::IterableLoopInitializer::Var(declaration) = for_in_loop.init() {
if let IterableLoopInitializer::Var(declaration) = for_in_loop.init() {
match declaration {
Declaration::Identifier { ident, .. } => {
vars.insert(ident.sym());
@ -412,7 +416,7 @@ impl Node {
for_in_loop.body().var_declared_names(vars);
}
Node::ForOfLoop(for_of_loop) => {
if let iteration::IterableLoopInitializer::Var(declaration) = for_of_loop.init() {
if let IterableLoopInitializer::Var(declaration) = for_of_loop.init() {
match declaration {
Declaration::Identifier { ident, .. } => {
vars.insert(ident.sym());
@ -456,6 +460,427 @@ impl Node {
_ => {}
}
}
/// 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
pub(crate) fn contains_arguments(&self) -> bool {
match self {
Node::Identifier(ident) if ident.sym() == Sym::ARGUMENTS => return true,
Node::ArrayDecl(array) => {
for node in array.as_ref() {
if node.contains_arguments() {
return true;
}
}
}
Node::ArrowFunctionDecl(decl) => {
for node in decl.body().items() {
if node.contains_arguments() {
return true;
}
}
}
Node::Assign(assign) => {
if assign.rhs().contains_arguments() {
return true;
}
}
Node::AwaitExpr(r#await) => {
if r#await.expr().contains_arguments() {
return true;
}
}
Node::BinOp(bin_op) => {
if bin_op.lhs().contains_arguments() || bin_op.rhs().contains_arguments() {
return true;
}
}
Node::Block(block) => {
for node in block.items() {
if node.contains_arguments() {
return true;
}
}
}
Node::Call(call) => {
if call.expr().contains_arguments() {
return true;
}
for node in call.args() {
if node.contains_arguments() {
return true;
}
}
}
Node::ConditionalOp(conditional) => {
if conditional.cond().contains_arguments() {
return true;
}
if conditional.if_true().contains_arguments() {
return true;
}
if conditional.if_false().contains_arguments() {
return true;
}
}
Node::DoWhileLoop(do_while_loop) => {
if do_while_loop.body().contains_arguments() {
return true;
}
if do_while_loop.cond().contains_arguments() {
return true;
}
}
Node::GetConstField(get_const_field) => {
if get_const_field.obj().contains_arguments() {
return true;
}
}
Node::GetPrivateField(get_private_field) => {
if get_private_field.obj().contains_arguments() {
return true;
}
}
Node::GetField(get_field) => {
if get_field.obj().contains_arguments() {
return true;
}
if get_field.field().contains_arguments() {
return true;
}
}
Node::ForLoop(for_loop) => {
if let Some(node) = for_loop.init() {
if node.contains_arguments() {
return true;
}
}
if let Some(node) = for_loop.condition() {
if node.contains_arguments() {
return true;
}
}
if let Some(node) = for_loop.final_expr() {
if node.contains_arguments() {
return true;
}
}
if for_loop.body().contains_arguments() {
return true;
}
}
Node::ForInLoop(for_in_loop) => {
match for_in_loop.init() {
IterableLoopInitializer::Var(declaration)
| IterableLoopInitializer::Let(declaration)
| IterableLoopInitializer::Const(declaration) => match declaration {
Declaration::Identifier { init, .. } => {
if let Some(init) = init {
{
if init.contains_arguments() {
return true;
}
}
}
}
Declaration::Pattern(pattern) => {
if pattern.contains_arguments() {
return true;
}
}
},
IterableLoopInitializer::DeclarationPattern(pattern) => {
if pattern.contains_arguments() {
return true;
}
}
IterableLoopInitializer::Identifier(_) => {}
}
if for_in_loop.expr().contains_arguments() {
return true;
}
if for_in_loop.body().contains_arguments() {
return true;
}
}
Node::ForOfLoop(for_of_loop) => {
match for_of_loop.init() {
IterableLoopInitializer::Var(declaration)
| IterableLoopInitializer::Let(declaration)
| IterableLoopInitializer::Const(declaration) => match declaration {
Declaration::Identifier { init, .. } => {
if let Some(init) = init {
{
if init.contains_arguments() {
return true;
}
}
}
}
Declaration::Pattern(pattern) => {
if pattern.contains_arguments() {
return true;
}
}
},
IterableLoopInitializer::DeclarationPattern(pattern) => {
if pattern.contains_arguments() {
return true;
}
}
IterableLoopInitializer::Identifier(_) => {}
}
if for_of_loop.iterable().contains_arguments() {
return true;
}
if for_of_loop.body().contains_arguments() {
return true;
}
}
Node::If(r#if) => {
if r#if.cond().contains_arguments() {
return true;
}
if r#if.body().contains_arguments() {
return true;
}
if let Some(node) = r#if.else_node() {
if node.contains_arguments() {
return true;
}
}
}
Node::VarDeclList(decl_list)
| Node::ConstDeclList(decl_list)
| Node::LetDeclList(decl_list) => match decl_list {
DeclarationList::Const(declarations)
| DeclarationList::Let(declarations)
| DeclarationList::Var(declarations) => {
for declaration in declarations.iter() {
match declaration {
Declaration::Identifier { init, .. } => {
if let Some(init) = init {
{
if init.contains_arguments() {
return true;
}
}
}
}
Declaration::Pattern(pattern) => {
if pattern.contains_arguments() {
return true;
}
}
}
}
}
},
Node::New(new) => {
if new.expr().contains_arguments() {
return true;
}
for node in new.args() {
if node.contains_arguments() {
return true;
}
}
}
Node::Object(object) => {
for property in object.properties() {
match property {
PropertyDefinition::IdentifierReference(ident) => {
if *ident == Sym::ARGUMENTS {
return true;
}
}
PropertyDefinition::Property(_, node)
| PropertyDefinition::SpreadObject(node) => {
if node.contains_arguments() {
return true;
}
}
PropertyDefinition::MethodDefinition(method, _) => match method {
MethodDefinition::Get(function)
| MethodDefinition::Set(function)
| MethodDefinition::Ordinary(function) => {
if let Some(Sym::ARGUMENTS) = function.name() {
return true;
}
}
MethodDefinition::Generator(generator) => {
if let Some(Sym::ARGUMENTS) = generator.name() {
return true;
}
}
MethodDefinition::AsyncGenerator(async_generator) => {
if let Some(Sym::ARGUMENTS) = async_generator.name() {
return true;
}
}
MethodDefinition::Async(function) => {
if let Some(Sym::ARGUMENTS) = function.name() {
return true;
}
}
},
}
}
}
Node::Return(r#return) => {
if let Some(node) = r#return.expr() {
if node.contains_arguments() {
return true;
}
}
}
Node::Switch(r#switch) => {
if r#switch.val().contains_arguments() {
return true;
}
for case in r#switch.cases() {
if case.condition().contains_arguments() {
return true;
}
for node in case.body().items() {
if node.contains_arguments() {
return true;
}
}
}
}
Node::Spread(spread) => {
if spread.val().contains_arguments() {
return true;
}
}
Node::TaggedTemplate(tagged_template) => {
if tagged_template.tag().contains_arguments() {
return true;
}
for node in tagged_template.exprs() {
if node.contains_arguments() {
return true;
}
}
}
Node::TemplateLit(template_lit) => {
for element in template_lit.elements() {
if let template::TemplateElement::Expr(node) = element {
if node.contains_arguments() {
return false;
}
}
}
}
Node::Throw(throw) => {
if throw.expr().contains_arguments() {
return true;
}
}
Node::Try(r#try) => {
for node in r#try.block().items() {
if node.contains_arguments() {
return true;
}
}
if let Some(catch) = r#try.catch() {
for node in catch.block().items() {
if node.contains_arguments() {
return true;
}
}
}
if let Some(finally) = r#try.finally() {
for node in finally.items() {
if node.contains_arguments() {
return true;
}
}
}
}
Node::UnaryOp(unary_op) => {
if unary_op.target().contains_arguments() {
return true;
}
}
Node::WhileLoop(while_loop) => {
if while_loop.cond().contains_arguments() {
return true;
}
if while_loop.body().contains_arguments() {
return true;
}
}
Node::Yield(r#yield) => {
if let Some(node) = r#yield.expr() {
if node.contains_arguments() {
return true;
}
}
}
Node::ClassExpr(class) | Node::ClassDecl(class) => {
if let Some(node) = class.super_ref() {
if node.contains_arguments() {
return true;
}
for element in class.elements() {
match element {
ClassElement::MethodDefinition(_, method)
| ClassElement::StaticMethodDefinition(_, method) => match method {
MethodDefinition::Get(function)
| MethodDefinition::Set(function)
| MethodDefinition::Ordinary(function) => {
if let Some(Sym::ARGUMENTS) = function.name() {
return true;
}
}
MethodDefinition::Generator(generator) => {
if let Some(Sym::ARGUMENTS) = generator.name() {
return true;
}
}
MethodDefinition::AsyncGenerator(async_generator) => {
if let Some(Sym::ARGUMENTS) = async_generator.name() {
return true;
}
}
MethodDefinition::Async(function) => {
if let Some(Sym::ARGUMENTS) = function.name() {
return true;
}
}
},
ClassElement::FieldDefinition(_, node)
| ClassElement::StaticFieldDefinition(_, node)
| ClassElement::PrivateFieldDefinition(_, node)
| ClassElement::PrivateStaticFieldDefinition(_, node) => {
if let Some(node) = node {
if node.contains_arguments() {
return true;
}
}
}
ClassElement::StaticBlock(statement_list) => {
for node in statement_list.items() {
if node.contains_arguments() {
return true;
}
}
}
_ => {}
}
}
}
}
_ => {}
}
false
}
}
impl ToInternedString for Node {
@ -509,7 +934,7 @@ fn test_formatting(source: &'static str) {
.collect::<Vec<&'static str>>()
.join("\n");
let mut context = Context::default();
let result = Parser::new(scenario.as_bytes(), false)
let result = Parser::new(scenario.as_bytes())
.parse_all(&mut context)
.expect("parsing failed")
.to_interned_string(context.interner());

29
boa_engine/src/syntax/ast/node/operator/assign/mod.rs

@ -1,11 +1,14 @@
use crate::syntax::ast::node::{
declaration::{
BindingPatternTypeArray, BindingPatternTypeObject, DeclarationPatternArray,
DeclarationPatternObject,
use crate::syntax::{
ast::node::{
declaration::{
BindingPatternTypeArray, BindingPatternTypeObject, DeclarationPatternArray,
DeclarationPatternObject,
},
field::get_private_field::GetPrivateField,
object::{PropertyDefinition, PropertyName},
ArrayDecl, DeclarationPattern, GetConstField, GetField, Identifier, Node, Object,
},
field::get_private_field::GetPrivateField,
object::{PropertyDefinition, PropertyName},
ArrayDecl, DeclarationPattern, GetConstField, GetField, Identifier, Node, Object,
parser::RESERVED_IDENTIFIERS_STRICT,
};
use boa_gc::{Finalize, Trace};
use boa_interner::{Interner, Sym, ToInternedString};
@ -152,6 +155,10 @@ pub(crate) fn object_decl_to_declaration_pattern(
return None
}
PropertyDefinition::IdentifierReference(ident) => {
if strict && RESERVED_IDENTIFIERS_STRICT.contains(ident) {
return None;
}
excluded_keys.push(*ident);
bindings.push(BindingPatternTypeObject::SingleName {
ident: *ident,
@ -164,6 +171,10 @@ pub(crate) fn object_decl_to_declaration_pattern(
if strict && *name == Sym::EVAL {
return None;
}
if strict && RESERVED_IDENTIFIERS_STRICT.contains(name) {
return None;
}
excluded_keys.push(*name);
bindings.push(BindingPatternTypeObject::SingleName {
ident: *name,
@ -217,6 +228,10 @@ pub(crate) fn array_decl_to_declaration_pattern(
for (i, node) in array.as_ref().iter().enumerate() {
match node {
Node::Identifier(ident) => {
if strict && ident.sym() == Sym::ARGUMENTS {
return None;
}
bindings.push(BindingPatternTypeArray::SingleName {
ident: ident.sym(),
default_init: None,

32
boa_engine/src/syntax/lexer/identifier.rs

@ -8,19 +8,7 @@ use crate::syntax::{
use boa_interner::Interner;
use boa_profiler::Profiler;
use boa_unicode::UnicodeProperties;
use std::{io::Read, str};
const STRICT_FORBIDDEN_IDENTIFIERS: [&str; 9] = [
"implements",
"interface",
"let",
"package",
"private",
"protected",
"public",
"static",
"yield",
];
use std::io::Read;
/// Identifier lexing.
///
@ -90,13 +78,6 @@ impl<R> Tokenizer<R> for Identifier {
Self::take_identifier_name(cursor, start_pos, self.init)?;
let token_kind = if let Ok(keyword) = identifier_name.parse() {
if cursor.strict_mode() && keyword == Keyword::With {
return Err(Error::Syntax(
"using 'with' statement not allowed in strict mode".into(),
start_pos,
));
}
match keyword {
Keyword::True => TokenKind::BooleanLiteral(true),
Keyword::False => TokenKind::BooleanLiteral(false),
@ -104,17 +85,6 @@ impl<R> Tokenizer<R> for Identifier {
_ => TokenKind::Keyword((keyword, contains_escaped_chars)),
}
} else {
if cursor.strict_mode()
&& STRICT_FORBIDDEN_IDENTIFIERS.contains(&identifier_name.as_str())
{
return Err(Error::Syntax(
format!(
"using future reserved keyword '{identifier_name}' not allowed in strict mode",
)
.into(),
start_pos,
));
}
TokenKind::identifier(interner.get_or_intern(identifier_name))
};

2
boa_engine/src/syntax/parser/error.rs

@ -98,7 +98,7 @@ impl ParseError {
}
/// Creates a "general" parsing error.
pub(super) fn general(message: &'static str, position: Position) -> Self {
pub(crate) fn general(message: &'static str, position: Position) -> Self {
Self::General { message, position }
}

18
boa_engine/src/syntax/parser/expression/assignment/mod.rs

@ -214,17 +214,7 @@ where
TokenKind::Punctuator(Punctuator::Assign) => {
if cursor.strict_mode() {
if let Node::Identifier(ident) = lhs {
if ident.sym() == Sym::ARGUMENTS {
return Err(ParseError::lex(LexError::Syntax(
"unexpected identifier 'arguments' in strict mode".into(),
position,
)));
} else if ident.sym() == Sym::EVAL {
return Err(ParseError::lex(LexError::Syntax(
"unexpected identifier 'eval' in strict mode".into(),
position,
)));
}
ident.check_strict_arguments_or_eval(position)?;
}
}
@ -243,6 +233,12 @@ where
}
}
TokenKind::Punctuator(p) if p.as_binop().is_some() && p != &Punctuator::Comma => {
if cursor.strict_mode() {
if let Node::Identifier(ident) = lhs {
ident.check_strict_arguments_or_eval(position)?;
}
}
cursor.next(interner)?.expect("token vanished");
if is_assignable(&lhs) {
let binop = p.as_binop().expect("binop disappeared");

2
boa_engine/src/syntax/parser/expression/identifiers.rs

@ -15,7 +15,7 @@ use boa_interner::{Interner, Sym};
use boa_profiler::Profiler;
use std::io::Read;
const RESERVED_IDENTIFIERS_STRICT: [Sym; 9] = [
pub(in crate::syntax) const RESERVED_IDENTIFIERS_STRICT: [Sym; 9] = [
Sym::IMPLEMENTS,
Sym::INTERFACE,
Sym::LET,

1
boa_engine/src/syntax/parser/expression/mod.rs

@ -36,6 +36,7 @@ use boa_profiler::Profiler;
use std::io::Read;
pub(super) use self::{assignment::AssignmentExpression, primary::Initializer};
pub(in crate::syntax) use identifiers::RESERVED_IDENTIFIERS_STRICT;
pub(in crate::syntax::parser) use {
identifiers::{BindingIdentifier, LabelIdentifier},
left_hand_side::LeftHandSideExpression,

85
boa_engine/src/syntax/parser/expression/primary/array_initializer/mod.rs

@ -66,40 +66,69 @@ where
let _timer = Profiler::global().start_event("ArrayLiteral", "Parsing");
let mut elements = Vec::new();
let mut has_trailing_comma_spread = false;
loop {
// TODO: Support all features.
while cursor.next_if(Punctuator::Comma, interner)?.is_some() {
elements.push(Node::Empty);
}
if cursor
.next_if(Punctuator::CloseBracket, interner)?
.is_some()
{
break;
}
let mut next_comma = false;
let mut last_spread = false;
let _next = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd); // Check that there are more tokens to read.
loop {
let token = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?;
match token.kind() {
TokenKind::Punctuator(Punctuator::CloseBracket) => {
cursor.next(interner).expect("token disappeared");
break;
}
TokenKind::Punctuator(Punctuator::Comma) if next_comma => {
cursor.next(interner).expect("token disappeared");
if cursor.next_if(Punctuator::Spread, interner)?.is_some() {
let node =
AssignmentExpression::new(None, true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
elements.push(Spread::new(node).into());
// If the last element in the array is followed by a comma, push an elision.
if cursor.next_if(Punctuator::Comma, interner)?.is_some() {
if let Some(t) = cursor.peek(0, interner)? {
if *t.kind() == TokenKind::Punctuator(Punctuator::CloseBracket) {
if last_spread {
let token = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?;
if token.kind() == &TokenKind::Punctuator(Punctuator::CloseBracket) {
has_trailing_comma_spread = true;
}
}
next_comma = false;
}
TokenKind::Punctuator(Punctuator::Comma) => {
cursor.next(interner).expect("token disappeared");
elements.push(Node::Empty);
}
} else {
elements.push(
AssignmentExpression::new(None, true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?,
);
cursor.next_if(Punctuator::Comma, interner)?;
TokenKind::Punctuator(Punctuator::Spread) if next_comma => {
return Err(ParseError::unexpected(
token.to_string(interner),
token.span(),
"expected comma or end of array",
));
}
TokenKind::Punctuator(Punctuator::Spread) => {
cursor.next(interner).expect("token disappeared");
let node =
AssignmentExpression::new(None, true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
elements.push(Spread::new(node).into());
next_comma = true;
last_spread = true;
}
_ if next_comma => {
return Err(ParseError::unexpected(
token.to_string(interner),
token.span(),
"expected comma or end of array",
));
}
_ => {
let node =
AssignmentExpression::new(None, true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
elements.push(node);
next_comma = true;
last_spread = false;
}
}
}
if last_spread {
if let Some(Node::Empty) = elements.last() {
has_trailing_comma_spread = true;
}
}

3
boa_engine/src/syntax/parser/expression/primary/mod.rs

@ -113,7 +113,8 @@ where
}
TokenKind::Keyword((Keyword::Class, _)) => {
cursor.next(interner).expect("token disappeared");
ClassExpression::new(self.name, false, false).parse(cursor, interner)
ClassExpression::new(self.name, self.allow_yield, self.allow_await)
.parse(cursor, interner)
}
TokenKind::Keyword((Keyword::Async, false)) => {
cursor.next(interner).expect("token disappeared");

56
boa_engine/src/syntax/parser/expression/update.rs

@ -57,56 +57,46 @@ where
let _timer = Profiler::global().start_event("UpdateExpression", "Parsing");
let tok = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?;
let position = tok.span().start();
match tok.kind() {
TokenKind::Punctuator(Punctuator::Inc) => {
cursor
.next(interner)?
.expect("Punctuator::Inc token disappeared");
return Ok(node::UnaryOp::new(
UnaryOp::IncrementPre,
UnaryExpression::new(self.name, self.allow_yield, self.allow_await)
.parse(cursor, interner)?,
)
.into());
let target = UnaryExpression::new(self.name, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
if cursor.strict_mode() {
if let Node::Identifier(ident) = target {
ident.check_strict_arguments_or_eval(position)?;
}
}
return Ok(node::UnaryOp::new(UnaryOp::IncrementPre, target).into());
}
TokenKind::Punctuator(Punctuator::Dec) => {
cursor
.next(interner)?
.expect("Punctuator::Dec token disappeared");
return Ok(node::UnaryOp::new(
UnaryOp::DecrementPre,
UnaryExpression::new(self.name, self.allow_yield, self.allow_await)
.parse(cursor, interner)?,
)
.into());
let target = UnaryExpression::new(self.name, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
if cursor.strict_mode() {
if let Node::Identifier(ident) = target {
ident.check_strict_arguments_or_eval(position)?;
}
}
return Ok(node::UnaryOp::new(UnaryOp::DecrementPre, target).into());
}
_ => {}
}
let position = cursor
.peek(0, interner)?
.ok_or(ParseError::AbruptEnd)?
.span()
.start();
let lhs = LeftHandSideExpression::new(self.name, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
if cursor.strict_mode() {
if let Node::Identifier(ident) = lhs {
if ident.sym() == Sym::ARGUMENTS {
return Err(ParseError::lex(LexError::Syntax(
"unexpected identifier 'arguments' in strict mode".into(),
position,
)));
} else if ident.sym() == Sym::EVAL {
return Err(ParseError::lex(LexError::Syntax(
"unexpected identifier 'eval' in strict mode".into(),
position,
)));
}
}
}
let strict = cursor.strict_mode();
if let Some(tok) = cursor.peek(0, interner)? {
let token_start = tok.span().start();

1
boa_engine/src/syntax/parser/function/mod.rs

@ -541,7 +541,6 @@ where
self.allow_yield,
self.allow_await,
true,
true,
&FUNCTION_BREAK_TOKENS,
)
.parse(cursor, interner);

2
boa_engine/src/syntax/parser/function/tests.rs

@ -70,7 +70,7 @@ fn check_duplicates_strict_on() {
let js = "'use strict'; function foo(a, a) {}";
let mut context = Context::default();
let res = Parser::new(js.as_bytes(), false).parse_all(&mut context);
let res = Parser::new(js.as_bytes()).parse_all(&mut context);
dbg!(&res);
assert!(res.is_err());
}

20
boa_engine/src/syntax/parser/mod.rs

@ -24,6 +24,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
use std::io::Read;
pub use self::error::{ParseError, ParseResult};
pub(in crate::syntax) use expression::RESERVED_IDENTIFIERS_STRICT;
/// Trait implemented by parsers.
///
@ -103,14 +104,21 @@ pub struct Parser<R> {
impl<R> Parser<R> {
/// Create a new `Parser` with a reader as the input to parse.
pub fn new(reader: R, strict_mode: bool) -> Self
pub fn new(reader: R) -> Self
where
R: Read,
{
let mut cursor = Cursor::new(reader);
cursor.set_strict_mode(strict_mode);
Self {
cursor: Cursor::new(reader),
}
}
Self { cursor }
/// Set the parser strict mode to true.
pub(crate) fn set_strict(&mut self)
where
R: Read,
{
self.cursor.set_strict_mode(true);
}
/// Parse the full input as a [ECMAScript Script][spec] into the boa AST representation.
@ -204,9 +212,9 @@ where
cursor: &mut Cursor<R>,
interner: &mut Interner,
) -> Result<Self::Output, ParseError> {
let mut strict = cursor.strict_mode();
match cursor.peek(0, interner)? {
Some(tok) => {
let mut strict = false;
match tok.kind() {
// Set the strict mode
TokenKind::StringLiteral(string)
@ -246,6 +254,6 @@ where
cursor: &mut Cursor<R>,
interner: &mut Interner,
) -> Result<Self::Output, ParseError> {
self::statement::StatementList::new(false, false, false, false, &[]).parse(cursor, interner)
self::statement::StatementList::new(false, false, false, &[]).parse(cursor, interner)
}
}

1
boa_engine/src/syntax/parser/statement/block/mod.rs

@ -91,7 +91,6 @@ where
self.allow_yield,
self.allow_await,
self.allow_return,
true,
&BLOCK_BREAK_TOKENS,
)
.parse(cursor, interner)

32
boa_engine/src/syntax/parser/statement/declaration/hoistable/class_decl/mod.rs

@ -515,6 +515,7 @@ where
};
let token = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?;
let position = token.span().start();
let element = match token.kind() {
TokenKind::Identifier(Sym::CONSTRUCTOR) if !r#static => {
cursor.next(interner).expect("token disappeared");
@ -561,7 +562,7 @@ where
.span()
.start();
let statement_list =
StatementList::new(false, true, false, true, &FUNCTION_BREAK_TOKENS)
StatementList::new(false, true, false, &FUNCTION_BREAK_TOKENS)
.parse(cursor, interner)?;
let lexically_declared_names = statement_list.lexically_declared_names();
@ -1243,6 +1244,35 @@ where
}
};
match &element {
// FieldDefinition : ClassElementName Initializer [opt]
// It is a Syntax Error if Initializer is present and ContainsArguments of Initializer is true.
ClassElementNode::FieldDefinition(_, Some(node))
| ClassElementNode::StaticFieldDefinition(_, Some(node))
| ClassElementNode::PrivateFieldDefinition(_, Some(node))
| ClassElementNode::PrivateStaticFieldDefinition(_, Some(node)) => {
if node.contains_arguments() {
return Err(ParseError::general(
"'arguments' not allowed in class field definition",
position,
));
}
}
// ClassStaticBlockBody : ClassStaticBlockStatementList
// It is a Syntax Error if ContainsArguments of ClassStaticBlockStatementList is true.
ClassElementNode::StaticBlock(block) => {
for node in block.items() {
if node.contains_arguments() {
return Err(ParseError::general(
"'arguments' not allowed in class static block",
position,
));
}
}
}
_ => {}
}
Ok((None, Some(element)))
}
}

2
boa_engine/src/syntax/parser/statement/declaration/hoistable/mod.rs

@ -111,7 +111,7 @@ where
}
}
TokenKind::Keyword((Keyword::Class, false)) => {
ClassDeclaration::new(false, false, self.is_default)
ClassDeclaration::new(self.allow_yield, self.allow_await, false)
.parse(cursor, interner)
.map(Node::from)
}

32
boa_engine/src/syntax/parser/statement/mod.rs

@ -240,7 +240,6 @@ pub(super) struct StatementList {
allow_yield: AllowYield,
allow_await: AllowAwait,
allow_return: AllowReturn,
in_block: bool,
break_nodes: &'static [TokenKind],
}
@ -250,7 +249,6 @@ impl StatementList {
allow_yield: Y,
allow_await: A,
allow_return: R,
in_block: bool,
break_nodes: &'static [TokenKind],
) -> Self
where
@ -262,7 +260,6 @@ impl StatementList {
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
allow_return: allow_return.into(),
in_block,
break_nodes,
}
}
@ -299,13 +296,9 @@ where
_ => {}
}
let item = StatementListItem::new(
self.allow_yield,
self.allow_await,
self.allow_return,
self.in_block,
)
.parse(cursor, interner)?;
let item =
StatementListItem::new(self.allow_yield, self.allow_await, self.allow_return)
.parse(cursor, interner)?;
items.push(item);
// move the cursor forward for any consecutive semicolon.
@ -333,12 +326,11 @@ struct StatementListItem {
allow_yield: AllowYield,
allow_await: AllowAwait,
allow_return: AllowReturn,
in_block: bool,
}
impl StatementListItem {
/// Creates a new `StatementListItem` parser.
fn new<Y, A, R>(allow_yield: Y, allow_await: A, allow_return: R, in_block: bool) -> Self
fn new<Y, A, R>(allow_yield: Y, allow_await: A, allow_return: R) -> Self
where
Y: Into<AllowYield>,
A: Into<AllowAwait>,
@ -348,7 +340,6 @@ impl StatementListItem {
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
allow_return: allow_return.into(),
in_block,
}
}
}
@ -365,20 +356,13 @@ where
interner: &mut Interner,
) -> Result<Self::Output, ParseError> {
let _timer = Profiler::global().start_event("StatementListItem", "Parsing");
let strict_mode = cursor.strict_mode();
let tok = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?;
match *tok.kind() {
TokenKind::Keyword((Keyword::Function | Keyword::Async | Keyword::Class, _)) => {
if strict_mode && self.in_block {
return Err(ParseError::lex(LexError::Syntax(
"Function declaration in blocks not allowed in strict mode".into(),
tok.span().start(),
)));
}
Declaration::new(self.allow_yield, self.allow_await, true).parse(cursor, interner)
}
TokenKind::Keyword((Keyword::Const | Keyword::Let, _)) => {
TokenKind::Keyword((
Keyword::Function | Keyword::Async | Keyword::Class | Keyword::Const | Keyword::Let,
_,
)) => {
Declaration::new(self.allow_yield, self.allow_await, true).parse(cursor, interner)
}
_ => Statement::new(self.allow_yield, self.allow_await, self.allow_return)

2
boa_engine/src/syntax/parser/statement/switch/mod.rs

@ -150,7 +150,6 @@ where
self.allow_yield,
self.allow_await,
self.allow_return,
false,
&CASE_BREAK_TOKENS,
)
.parse(cursor, interner)?;
@ -173,7 +172,6 @@ where
self.allow_yield,
self.allow_await,
self.allow_return,
false,
&CASE_BREAK_TOKENS,
)
.parse(cursor, interner)?;

6
boa_engine/src/syntax/parser/tests.rs

@ -25,7 +25,7 @@ where
{
let mut context = Context::new(interner);
assert_eq!(
Parser::new(js.as_bytes(), false)
Parser::new(js.as_bytes())
.parse_all(&mut context)
.expect("failed to parse"),
StatementList::from(expr)
@ -36,9 +36,7 @@ where
#[track_caller]
pub(super) fn check_invalid(js: &str) {
let mut context = Context::default();
assert!(Parser::new(js.as_bytes(), false)
.parse_all(&mut context)
.is_err());
assert!(Parser::new(js.as_bytes()).parse_all(&mut context).is_err());
}
/// Should be parsed as `new Class().method()` instead of `new (Class().method())`

18
boa_engine/src/tests.rs

@ -1607,24 +1607,6 @@ fn test_strict_mode_reserved_name() {
}
}
#[test]
fn test_strict_mode_func_decl_in_block() {
// Checks that a function declaration in a block is an error in
// strict mode code as per https://tc39.es/ecma262/#early-error.
let scenario = r#"
'use strict';
let a = 4;
let b = 5;
if (a < b) { function f() {} }
"#;
check_output(&[TestAction::TestStartsWith(
scenario,
"Uncaught \"SyntaxError\": ",
)]);
}
#[test]
fn test_strict_mode_dup_func_parameters() {
// Checks that a function cannot contain duplicate parameter

85
boa_engine/src/vm/code_block.rs

@ -634,10 +634,14 @@ impl JsObject {
} else {
context.global_object().clone().into()
}
} else if (!code.strict && !context.strict()) && this.is_null_or_undefined() {
} else if code.strict {
this.clone()
} else if this.is_null_or_undefined() {
context.global_object().clone().into()
} else {
this.clone()
this.to_object(context)
.expect("conversion cannot fail")
.into()
};
if code.params.has_expressions() {
@ -655,19 +659,18 @@ impl JsObject {
}
if let Some(binding) = code.arguments_binding {
let arguments_obj =
if context.strict() || code.strict || !code.params.is_simple() {
Arguments::create_unmapped_arguments_object(args, context)
} else {
let env = context.realm.environments.current();
Arguments::create_mapped_arguments_object(
&this_function_object,
&code.params,
args,
&env,
context,
)
};
let arguments_obj = if code.strict || !code.params.is_simple() {
Arguments::create_unmapped_arguments_object(args, context)
} else {
let env = context.realm.environments.current();
Arguments::create_mapped_arguments_object(
&this_function_object,
&code.params,
args,
&env,
context,
)
};
context.realm.environments.put_value(
binding.environment_index(),
binding.binding_index(),
@ -742,7 +745,7 @@ impl JsObject {
} else {
context.global_object().clone().into()
}
} else if (!code.strict && !context.strict()) && this.is_null_or_undefined() {
} else if !code.strict && this.is_null_or_undefined() {
context.global_object().clone().into()
} else {
this.clone()
@ -763,19 +766,18 @@ impl JsObject {
}
if let Some(binding) = code.arguments_binding {
let arguments_obj =
if context.strict() || code.strict || !code.params.is_simple() {
Arguments::create_unmapped_arguments_object(args, context)
} else {
let env = context.realm.environments.current();
Arguments::create_mapped_arguments_object(
&this_function_object,
&code.params,
args,
&env,
context,
)
};
let arguments_obj = if code.strict || !code.params.is_simple() {
Arguments::create_unmapped_arguments_object(args, context)
} else {
let env = context.realm.environments.current();
Arguments::create_mapped_arguments_object(
&this_function_object,
&code.params,
args,
&env,
context,
)
};
context.realm.environments.put_value(
binding.environment_index(),
binding.binding_index(),
@ -957,19 +959,18 @@ impl JsObject {
}
if let Some(binding) = code.arguments_binding {
let arguments_obj =
if context.strict() || code.strict || !code.params.is_simple() {
Arguments::create_unmapped_arguments_object(args, context)
} else {
let env = context.realm.environments.current();
Arguments::create_mapped_arguments_object(
&this_function_object,
&code.params,
args,
&env,
context,
)
};
let arguments_obj = if code.strict || !code.params.is_simple() {
Arguments::create_unmapped_arguments_object(args, context)
} else {
let env = context.realm.environments.current();
Arguments::create_mapped_arguments_object(
&this_function_object,
&code.params,
args,
&env,
context,
)
};
context.realm.environments.put_value(
binding.environment_index(),
binding.binding_index(),

29
boa_engine/src/vm/mod.rs

@ -9,7 +9,7 @@ use crate::{
value::Numeric,
vm::{
call_frame::CatchAddresses,
code_block::{create_function_object, create_generator_function_object, Readable},
code_block::{create_generator_function_object, Readable},
},
Context, JsBigInt, JsResult, JsString, JsValue,
};
@ -25,6 +25,7 @@ pub use {call_frame::CallFrame, code_block::CodeBlock, opcode::Opcode};
pub(crate) use {
call_frame::{FinallyReturn, GeneratorResumeKind, TryStackEntry},
code_block::create_function_object,
opcode::BindingOpcode,
};
@ -553,7 +554,7 @@ impl Context {
.into();
let exists = self.global_bindings_mut().contains_key(&key);
if !exists && (self.strict() || self.vm.frame().code.strict) {
if !exists && self.vm.frame().code.strict {
return self.throw_reference_error(format!(
"assignment to undeclared variable {key}"
));
@ -566,7 +567,7 @@ impl Context {
self,
)?;
if !success && (self.strict() || self.vm.frame().code.strict) {
if !success && self.vm.frame().code.strict {
return self.throw_type_error(format!(
"cannot set non-writable property: {key}",
));
@ -674,12 +675,7 @@ impl Context {
let name = self.vm.frame().code.names[index as usize];
let name: PropertyKey = self.interner().resolve_expect(name).into();
object.set(
name,
value,
self.strict() || self.vm.frame().code.strict,
self,
)?;
object.set(name, value, self.vm.frame().code.strict, self)?;
}
Opcode::DefineOwnPropertyByName => {
let index = self.vm.read::<u32>();
@ -736,12 +732,7 @@ impl Context {
};
let key = key.to_property_key(self)?;
object.set(
key,
value,
self.strict() || self.vm.frame().code.strict,
self,
)?;
object.set(key, value, self.vm.frame().code.strict, self)?;
}
Opcode::DefineOwnPropertyByValue => {
let value = self.vm.pop();
@ -1068,7 +1059,7 @@ impl Context {
let key = self.interner().resolve_expect(key).into();
let object = self.vm.pop();
let result = object.to_object(self)?.__delete__(&key, self)?;
if !result && self.strict() || self.vm.frame().code.strict {
if !result && self.vm.frame().code.strict {
return Err(self.construct_type_error("Cannot delete property"));
}
self.vm.push(result);
@ -1079,7 +1070,7 @@ impl Context {
let result = object
.to_object(self)?
.__delete__(&key.to_property_key(self)?, self)?;
if !result && self.strict() || self.vm.frame().code.strict {
if !result && self.vm.frame().code.strict {
return Err(self.construct_type_error("Cannot delete property"));
}
self.vm.push(result);
@ -1256,7 +1247,7 @@ impl Context {
// A native function with the name "eval" implies, that is this the built-in eval function.
let eval = matches!(object.borrow().as_function(), Some(Function::Native { .. }));
let strict = self.strict() || self.vm.frame().code.strict;
let strict = self.vm.frame().code.strict;
if eval {
if let Some(x) = arguments.get(0) {
@ -1304,7 +1295,7 @@ impl Context {
// A native function with the name "eval" implies, that is this the built-in eval function.
let eval = matches!(object.borrow().as_function(), Some(Function::Native { .. }));
let strict = self.strict() || self.vm.frame().code.strict;
let strict = self.vm.frame().code.strict;
if eval {
if let Some(x) = arguments.get(0) {

60
boa_tester/src/exec/mod.rs

@ -111,7 +111,7 @@ impl Test {
/// Runs the test.
pub(crate) fn run(&self, harness: &Harness, verbose: u8) -> Vec<TestResult> {
let mut results = Vec::new();
if self.flags.contains(TestFlags::STRICT) {
if self.flags.contains(TestFlags::STRICT) && !self.flags.contains(TestFlags::RAW) {
results.push(self.run_once(harness, true, verbose));
}
@ -132,6 +132,12 @@ impl Test {
);
}
let test_content = if strict {
format!("\"use strict\";\n{}", self.content)
} else {
self.content.to_string()
};
let (result, result_text) = if !IGNORED.contains_any_flag(self.flags)
&& !IGNORED.contains_test(&self.name)
&& !IGNORED.contains_any_feature(&self.features)
@ -162,10 +168,9 @@ impl Test {
// TODO: implement async and add `harness/doneprintHandle.js` to the includes.
let mut context = Context::default();
match self.set_up_env(harness, strict, &mut context) {
match self.set_up_env(harness, &mut context) {
Ok(_) => {
context.set_strict_mode(strict);
let res = context.eval(&self.content.as_ref());
let res = context.eval(&test_content);
let passed = res.is_ok();
let text = match res {
@ -190,8 +195,7 @@ impl Test {
);
let mut context = Context::default();
context.set_strict_mode(strict);
match context.parse(self.content.as_bytes()) {
match context.parse(&test_content) {
Ok(statement_list) => match context.compile(&statement_list) {
Ok(_) => (false, "StatementList compilation should fail".to_owned()),
Err(e) => (true, format!("Uncaught {e:?}")),
@ -208,27 +212,22 @@ impl Test {
ref error_type,
} => {
let mut context = Context::default();
if let Err(e) =
Parser::new(self.content.as_bytes(), strict).parse_all(&mut context)
{
if let Err(e) = Parser::new(test_content.as_bytes()).parse_all(&mut context) {
(false, format!("Uncaught {e}"))
} else {
match self.set_up_env(harness, strict, &mut context) {
Ok(_) => {
context.set_strict_mode(strict);
match context.eval(&self.content.as_ref()) {
Ok(res) => (false, res.display().to_string()),
Err(e) => {
let passed = e
.display()
.internals(true)
.to_string()
.contains(error_type.as_ref());
(passed, format!("Uncaught {}", e.display()))
}
match self.set_up_env(harness, &mut context) {
Ok(_) => match context.eval(&test_content) {
Ok(res) => (false, res.display().to_string()),
Err(e) => {
let passed = e
.display()
.internals(true)
.to_string()
.contains(error_type.as_ref());
(passed, format!("Uncaught {}", e.display()))
}
}
},
Err(e) => (false, e),
}
}
@ -307,22 +306,15 @@ impl Test {
}
/// Sets the environment up to run the test.
fn set_up_env(
&self,
harness: &Harness,
strict: bool,
context: &mut Context,
) -> Result<(), String> {
fn set_up_env(&self, harness: &Harness, context: &mut Context) -> Result<(), String> {
// Register the print() function.
context.register_global_function("print", 1, test262_print);
// add the $262 object.
let _js262 = js262::init(context);
if strict {
context
.eval(r#""use strict";"#)
.map_err(|e| format!("could not set strict mode:\n{}", e.display()))?;
if self.flags.contains(TestFlags::RAW) {
return Ok(());
}
context

Loading…
Cancel
Save