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

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

@ -146,7 +146,7 @@ impl<R> Parser<R> {
where where
R: Read, 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 .realm
.environments .environments
.get_this_environment() .get_this_environment()
@ -156,6 +156,7 @@ impl<R> Parser<R> {
let has_super_binding = function_env_borrow.has_super_binding(); let has_super_binding = function_env_borrow.has_super_binding();
let function_object = function_env_borrow.function_object().borrow(); let function_object = function_env_borrow.function_object().borrow();
( (
true,
has_super_binding, has_super_binding,
function_object function_object
.as_function() .as_function()
@ -163,13 +164,14 @@ impl<R> Parser<R> {
.is_derived_constructor(), .is_derived_constructor(),
) )
} else { } else {
(false, false) (false, false, false)
}; };
let statement_list = Script::new(direct).parse(&mut self.cursor, context)?; let statement_list = Script::new(direct).parse(&mut self.cursor, context)?;
let mut contains_super_property = false; let mut contains_super_property = false;
let mut contains_super_call = false; let mut contains_super_call = false;
let mut contains_new_target = false;
if direct { if direct {
for node in statement_list.items() { for node in statement_list.items() {
if !contains_super_property && node.contains(ContainsSymbol::SuperProperty) { if !contains_super_property && node.contains(ContainsSymbol::SuperProperty) {
@ -179,9 +181,19 @@ impl<R> Parser<R> {
if !contains_super_call && node.contains(ContainsSymbol::SuperCall) { if !contains_super_call && node.contains(ContainsSymbol::SuperCall) {
contains_super_call = true; 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 { if !in_method && contains_super_property {
return Err(ParseError::general( return Err(ParseError::general(
"invalid super usage", "invalid super usage",
@ -365,10 +377,11 @@ where
let body = self::statement::StatementList::new(false, false, false, &[]) let body = self::statement::StatementList::new(false, false, false, &[])
.parse(cursor, interner)?; .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 { if !self.direct_eval {
for node in body.items() { 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) if node.contains(ContainsSymbol::SuperCall)
|| node.contains(ContainsSymbol::SuperProperty) || node.contains(ContainsSymbol::SuperProperty)
{ {
@ -377,6 +390,16 @@ where
Position::new(1, 1), 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::PushClassField
| Opcode::SuperCallDerived | Opcode::SuperCallDerived
| Opcode::Await | Opcode::Await
| Opcode::PushNewTarget
| Opcode::CallEvalSpread | Opcode::CallEvalSpread
| Opcode::CallSpread | Opcode::CallSpread
| Opcode::NewSpread | Opcode::NewSpread

16
boa_engine/src/vm/mod.rs

@ -2501,6 +2501,22 @@ impl Context {
self.vm.push(JsValue::undefined()); self.vm.push(JsValue::undefined());
return Ok(ShouldExit::Await); 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) Ok(ShouldExit::False)

9
boa_engine/src/vm/opcode.rs

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

4
boa_interner/src/sym.rs

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

Loading…
Cancel
Save