Browse Source

Implement `new.target` expression (#2299)

This Pull Request changes the following:

- Implement `new.target` expression
pull/2305/head
raskad 2 years ago
parent
commit
c58a8997ca
  1. 5
      boa_engine/src/bytecompiler/mod.rs
  2. 6
      boa_engine/src/syntax/ast/node/mod.rs
  3. 16
      boa_engine/src/syntax/parser/expression/left_hand_side/member.rs
  4. 31
      boa_engine/src/syntax/parser/mod.rs
  5. 1
      boa_engine/src/vm/code_block.rs
  6. 16
      boa_engine/src/vm/mod.rs
  7. 9
      boa_engine/src/vm/opcode.rs
  8. 4
      boa_interner/src/sym.rs

5
boa_engine/src/bytecompiler/mod.rs

@ -1301,6 +1301,11 @@ impl<'b> ByteCompiler<'b> {
self.emit_opcode(Opcode::Pop);
}
}
Node::NewTarget => {
if use_expr {
self.emit_opcode(Opcode::PushNewTarget);
}
}
_ => unreachable!(),
}
Ok(())

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

@ -250,6 +250,9 @@ pub enum Node {
/// A call of the super constructor. [More information](./super_call/struct.SuperCall.html).
SuperCall(SuperCall),
/// The `new.target` pseudo-property expression.
NewTarget,
/// A FormalParameterList.
///
/// This is only used in the parser itself.
@ -364,6 +367,7 @@ impl Node {
Self::ClassDecl(ref decl) => decl.to_indented_string(interner, indentation),
Self::ClassExpr(ref expr) => expr.to_indented_string(interner, indentation),
Self::SuperCall(ref super_call) => super_call.to_interned_string(interner),
Self::NewTarget => "new.target".to_owned(),
Self::FormalParameterList(_) => unreachable!(),
}
}
@ -1248,6 +1252,7 @@ impl Node {
}
}
Node::Yield(_) if symbol == ContainsSymbol::YieldExpression => return true,
Node::NewTarget if symbol == ContainsSymbol::NewTarget => return true,
_ => {}
}
false
@ -1261,6 +1266,7 @@ pub(crate) enum ContainsSymbol {
SuperCall,
YieldExpression,
AwaitExpression,
NewTarget,
}
impl ToInternedString for Node {

16
boa_engine/src/syntax/parser/expression/left_hand_side/member.rs

@ -73,7 +73,21 @@ where
));
}
TokenKind::Keyword((Keyword::New, false)) => {
let _next = cursor.next(interner).expect("new keyword disappeared");
cursor.next(interner).expect("token disappeared");
if cursor.next_if(Punctuator::Dot, interner)?.is_some() {
let token = cursor.next(interner)?.ok_or(ParseError::AbruptEnd)?;
match token.kind() {
TokenKind::Identifier(Sym::TARGET) => return Ok(Node::NewTarget),
_ => {
return Err(ParseError::general(
"unexpected private identifier",
token.span().start(),
));
}
}
}
let lhs = self.parse(cursor, interner)?;
let args = match cursor.peek(0, interner)? {
Some(next) if next.kind() == &TokenKind::Punctuator(Punctuator::OpenParen) => {

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

@ -146,7 +146,7 @@ impl<R> Parser<R> {
where
R: Read,
{
let (in_method, in_derived_constructor) = if let Some(function_env) = context
let (in_function, in_method, in_derived_constructor) = if let Some(function_env) = context
.realm
.environments
.get_this_environment()
@ -156,6 +156,7 @@ impl<R> Parser<R> {
let has_super_binding = function_env_borrow.has_super_binding();
let function_object = function_env_borrow.function_object().borrow();
(
true,
has_super_binding,
function_object
.as_function()
@ -163,13 +164,14 @@ impl<R> Parser<R> {
.is_derived_constructor(),
)
} else {
(false, false)
(false, false, false)
};
let statement_list = Script::new(direct).parse(&mut self.cursor, context)?;
let mut contains_super_property = false;
let mut contains_super_call = false;
let mut contains_new_target = false;
if direct {
for node in statement_list.items() {
if !contains_super_property && node.contains(ContainsSymbol::SuperProperty) {
@ -179,9 +181,19 @@ impl<R> Parser<R> {
if !contains_super_call && node.contains(ContainsSymbol::SuperCall) {
contains_super_call = true;
}
if !contains_new_target && node.contains(ContainsSymbol::NewTarget) {
contains_new_target = true;
}
}
}
if !in_function && contains_new_target {
return Err(ParseError::general(
"invalid new.target usage",
Position::new(1, 1),
));
}
if !in_method && contains_super_property {
return Err(ParseError::general(
"invalid super usage",
@ -365,10 +377,11 @@ where
let body = self::statement::StatementList::new(false, false, false, &[])
.parse(cursor, interner)?;
// It is a Syntax Error if StatementList Contains super unless the source text containing super is eval code that is being processed by a direct eval.
// Additional early error rules for super within direct eval are defined in 19.2.1.1.
if !self.direct_eval {
for node in body.items() {
// It is a Syntax Error if StatementList Contains super unless the source text containing super is eval
// code that is being processed by a direct eval.
// Additional early error rules for super within direct eval are defined in 19.2.1.1.
if node.contains(ContainsSymbol::SuperCall)
|| node.contains(ContainsSymbol::SuperProperty)
{
@ -377,6 +390,16 @@ where
Position::new(1, 1),
));
}
// It is a Syntax Error if StatementList Contains NewTarget unless the source text containing NewTarget
// is eval code that is being processed by a direct eval.
// Additional early error rules for NewTarget in direct eval are defined in 19.2.1.1.
if node.contains(ContainsSymbol::NewTarget) {
return Err(ParseError::general(
"invalid new.target usage",
Position::new(1, 1),
));
}
}
}

1
boa_engine/src/vm/code_block.rs

@ -371,6 +371,7 @@ impl CodeBlock {
| Opcode::PushClassField
| Opcode::SuperCallDerived
| Opcode::Await
| Opcode::PushNewTarget
| Opcode::CallEvalSpread
| Opcode::CallSpread
| Opcode::NewSpread

16
boa_engine/src/vm/mod.rs

@ -2501,6 +2501,22 @@ impl Context {
self.vm.push(JsValue::undefined());
return Ok(ShouldExit::Await);
}
Opcode::PushNewTarget => {
if let Some(env) = self
.realm
.environments
.get_this_environment()
.as_function_slots()
{
if let Some(new_target) = env.borrow().new_target() {
self.vm.push(new_target.clone());
} else {
self.vm.push(JsValue::undefined());
}
} else {
self.vm.push(JsValue::undefined());
}
}
}
Ok(ShouldExit::False)

9
boa_engine/src/vm/opcode.rs

@ -1183,6 +1183,13 @@ pub enum Opcode {
/// Stack: promise **=>**
Await,
/// Push the current new target to the stack.
///
/// Operands:
///
/// Stack: **=>** new_target
PushNewTarget,
/// No-operation instruction, does nothing.
///
/// Operands:
@ -1364,6 +1371,7 @@ impl Opcode {
Self::GeneratorNext => "GeneratorNext",
Self::AsyncGeneratorNext => "AsyncGeneratorNext",
Self::Await => "Await",
Self::PushNewTarget => "PushNewTarget",
Self::GeneratorNextDelegate => "GeneratorNextDelegate",
Self::Nop => "Nop",
}
@ -1509,6 +1517,7 @@ impl Opcode {
Self::Yield => "INST - Yield",
Self::GeneratorNext => "INST - GeneratorNext",
Self::AsyncGeneratorNext => "INST - AsyncGeneratorNext",
Self::PushNewTarget => "INST - PushNewTarget",
Self::Await => "INST - Await",
Self::GeneratorNextDelegate => "INST - GeneratorNextDelegate",
Self::Nop => "INST - Nop",

4
boa_interner/src/sym.rs

@ -97,6 +97,9 @@ impl Sym {
/// Symbol for the `"of"` string.
pub const OF: Self = unsafe { Self::new_unchecked(27) };
/// Symbol for the `"target"` string.
pub const TARGET: Self = unsafe { Self::new_unchecked(28) };
/// Creates a new [`Sym`] from the provided `value`, or returns `None` if `index` is zero.
#[inline]
pub(super) fn new(value: usize) -> Option<Self> {
@ -161,6 +164,7 @@ pub(super) static COMMON_STRINGS: phf::OrderedSet<&'static str> = {
"false",
"async",
"of",
"target",
};
// A `COMMON_STRINGS` of size `usize::MAX` would cause an overflow on our `Interner`
sa::const_assert!(COMMON_STRINGS.len() < usize::MAX);

Loading…
Cancel
Save