Browse Source

Bugfix/new.target is not understood by the parser as an expression #2793 (#2878)

<!---
Thank you for contributing to Boa! Please fill out the template below, and remove or add any
information as you feel necessary.
--->

This Pull Request fixes/closes #2793.

It changes the following:

- Added a condition to the boa_parser/src/parser/expression/left_hand_side/member.rs parse function match operation for the new token that allows for the operation to continue evaluating more tokens when the TARGET keyword follows it.
- Added a test to validate the fix. (Could not figure out the structure of the test suite so it's commented for now). All other tests pass.

Please let me know if there's anything else I can do to improve the fix.


Co-authored-by: jedel1043 <jedel0124@gmail.com>
pull/2882/head
Juan 1 year ago
parent
commit
d49656d6c5
  1. 17
      boa_parser/src/parser/expression/left_hand_side/member.rs
  2. 82
      boa_parser/src/parser/statement/declaration/hoistable/class_decl/tests.rs

17
boa_parser/src/parser/expression/left_hand_side/member.rs

@ -82,7 +82,7 @@ where
TokenKind::Keyword((Keyword::New, false)) => { TokenKind::Keyword((Keyword::New, false)) => {
cursor.advance(interner); cursor.advance(interner);
if cursor.next_if(Punctuator::Dot, interner)?.is_some() { let lhs_new_target = if cursor.next_if(Punctuator::Dot, interner)?.is_some() {
let token = cursor.next(interner).or_abrupt()?; let token = cursor.next(interner).or_abrupt()?;
match token.kind() { match token.kind() {
TokenKind::IdentifierName((Sym::TARGET, ContainsEscapeSequence(true))) => { TokenKind::IdentifierName((Sym::TARGET, ContainsEscapeSequence(true))) => {
@ -92,7 +92,7 @@ where
)); ));
} }
TokenKind::IdentifierName((Sym::TARGET, ContainsEscapeSequence(false))) => { TokenKind::IdentifierName((Sym::TARGET, ContainsEscapeSequence(false))) => {
return Ok(ast::Expression::NewTarget) ast::Expression::NewTarget
} }
_ => { _ => {
return Err(Error::general( return Err(Error::general(
@ -101,19 +101,22 @@ where
)); ));
} }
} }
} } else {
let lhs_inner = self.parse(cursor, interner)?;
let lhs = self.parse(cursor, interner)?;
let args = match cursor.peek(0, interner)? { let args = match cursor.peek(0, interner)? {
Some(next) if next.kind() == &TokenKind::Punctuator(Punctuator::OpenParen) => { Some(next)
if next.kind() == &TokenKind::Punctuator(Punctuator::OpenParen) =>
{
Arguments::new(self.allow_yield, self.allow_await) Arguments::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)? .parse(cursor, interner)?
} }
_ => Box::new([]), _ => Box::new([]),
}; };
let call_node = Call::new(lhs, args); let call_node = Call::new(lhs_inner, args);
ast::Expression::from(New::from(call_node)) ast::Expression::from(New::from(call_node))
};
lhs_new_target
} }
TokenKind::Keyword((Keyword::Super, _)) => { TokenKind::Keyword((Keyword::Super, _)) => {
cursor.advance(interner); cursor.advance(interner);

82
boa_parser/src/parser/statement/declaration/hoistable/class_decl/tests.rs

@ -1,9 +1,14 @@
use crate::parser::tests::check_script_parser; use crate::parser::tests::check_script_parser;
use boa_ast::{ use boa_ast::{
expression::literal::Literal, declaration::{LexicalDeclaration, Variable, VariableList},
expression::{
access::{PropertyAccess, SimplePropertyAccess},
literal::Literal,
Call, Identifier,
},
function::{Class, ClassElement, FormalParameterList, Function}, function::{Class, ClassElement, FormalParameterList, Function},
property::{MethodDefinition, PropertyName}, property::{MethodDefinition, PropertyName},
Declaration, StatementList, Declaration, Expression, Statement, StatementList, StatementListItem,
}; };
use boa_interner::Interner; use boa_interner::Interner;
use boa_macros::utf16; use boa_macros::utf16;
@ -90,3 +95,76 @@ fn check_async_field() {
interner, interner,
); );
} }
#[test]
fn check_new_target_with_property_access() {
let interner = &mut Interner::default();
let new_target = Expression::PropertyAccess(
SimplePropertyAccess::new(
Expression::NewTarget,
interner.get_or_intern_static("name", utf16!("name")),
)
.into(),
);
let console = Expression::Call(Call::new(
PropertyAccess::Simple(SimplePropertyAccess::new(
Identifier::from(interner.get_or_intern_static("console", utf16!("console"))).into(),
interner.get_or_intern_static("log", utf16!("log")),
))
.into(),
[new_target].into(),
));
let constructor = Function::new(
Some(interner.get_or_intern_static("A", utf16!("A")).into()),
FormalParameterList::default(),
StatementList::new([Statement::Expression(console).into()], false),
);
let class = Class::new(
Some(interner.get("A").unwrap().into()),
None,
Some(constructor),
Box::default(),
true,
);
let instantiation = Expression::New(
Call::new(
Identifier::from(interner.get("A").unwrap()).into(),
Box::default(),
)
.into(),
);
let const_decl = LexicalDeclaration::Const(
VariableList::new(
[Variable::from_identifier(
interner.get_or_intern_static("a", utf16!("a")).into(),
Some(instantiation),
)]
.into(),
)
.unwrap(),
);
let script = [
StatementListItem::Declaration(class.into()),
StatementListItem::Declaration(const_decl.into()),
];
check_script_parser(
r#"
class A {
constructor() {
console.log(new.target.name);
}
}
const a = new A();
"#,
script,
interner,
);
}

Loading…
Cancel
Save