use crate::engine::api::error::Error;
use crate::engine::api::error::ErrorKind;
use crate::engine::code::bytecode::Instruction;
use crate::engine::compiler::lexer::Identifier;
use crate::engine::compiler::lexer::Keyword;
use crate::engine::compiler::lexer::LexicalGoal;
use crate::engine::compiler::lexer::NumberKind;
use crate::engine::compiler::lexer::Punctuator;
use crate::engine::compiler::lexer::Span;
use crate::engine::compiler::lexer::TemplatePartKind;
use crate::engine::compiler::lexer::Token;
use crate::engine::compiler::lexer::TokenKind;
use crate::engine::compiler::model::ir::IdentifierAccess;
use crate::engine::compiler::model::ir::IdentifierReferenceAccess;
use crate::engine::compiler::model::ir::IrConstant;
use crate::engine::compiler::model::ir::PrivateFieldAccess;
use crate::engine::compiler::names::NameId;
use crate::engine::compiler::parser::context::ForAssignmentDeclaration;
use crate::engine::compiler::parser::context::ForAssignmentTargetInfo;
use crate::engine::compiler::parser::context::ForHeadDelimiter;
use crate::engine::compiler::parser::context::ForIterationKind;
use crate::engine::compiler::parser::context::IdentifierReference;
use crate::engine::compiler::parser::context::MemberReference;
use crate::engine::compiler::parser::context::Parser;
use crate::engine::compiler::parser::diagnostics::IdentifierContext;
use crate::engine::compiler::parser::diagnostics::source_offset;
use crate::engine::compiler::parser::literals::parse_number;
use crate::engine::compiler::parser::tokens::for_head_regexp_allowed_after;
use crate::engine::value::JsString;
use crate::engine::value::PrimitiveValue as Value;
use crate::source::SourceOffset;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum BindingSite {
Declaration,
Iteration(ForIterationKind),
Catch,
Parameter,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
enum BindingPatternKind {
Array,
Object,
}
enum ObjectBindingPropertyKey<'source> {
Fixed {
key: JsString,
token: Token<'source>,
shorthand: Option<Identifier<'source>>,
},
Computed {
span: Span,
},
}
#[derive(Clone, Copy)]
pub(super) struct BindingPatternScan<'source> {
following: Token<'source>,
has_object_rest: bool,
has_assignment: bool,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(super) struct ParenthesizedParameterScan {
pub(super) has_assignment: bool,
pub(super) bound_name_count: Option<usize>,
}
struct FormalParameterBoundNameCounter<I: Iterator> {
tokens: std::iter::Peekable<I>,
}
impl<'source, I: Iterator<Item = Result<Token<'source>, ()>>> FormalParameterBoundNameCounter<I> {
fn new(tokens: I) -> Self {
Self {
tokens: tokens.peekable(),
}
}
fn count(mut self) -> Option<usize> {
let mut count = 0usize;
while self.tokens.peek().is_some() {
let is_rest = self.consume_punctuator(Punctuator::Ellipsis);
count = count.checked_add(self.count_binding_target()?)?;
if self.consume_punctuator(Punctuator::Equal) {
if is_rest {
return None;
}
self.skip_expression_until(Punctuator::RightParen)?;
}
if self.tokens.peek().is_none() {
break;
}
if !self.consume_punctuator(Punctuator::Comma) {
return None;
}
if is_rest || self.tokens.peek().is_none() {
break;
}
}
(self.tokens.peek().is_none()).then_some(count)
}
fn count_binding_target(&mut self) -> Option<usize> {
match self.current_kind()? {
TokenKind::Identifier(_) => {
self.tokens.next();
Some(1)
}
TokenKind::Punctuator(Punctuator::LeftBracket) => self.count_array_pattern(),
TokenKind::Punctuator(Punctuator::LeftBrace) => self.count_object_pattern(),
_ => None,
}
}
fn count_array_pattern(&mut self) -> Option<usize> {
self.expect_punctuator(Punctuator::LeftBracket)?;
let mut count = 0usize;
while !self.consume_punctuator(Punctuator::RightBracket) {
if self.consume_punctuator(Punctuator::Comma) {
continue;
}
let is_rest = self.consume_punctuator(Punctuator::Ellipsis);
count = count.checked_add(self.count_binding_target()?)?;
if self.consume_punctuator(Punctuator::Equal) {
if is_rest {
return None;
}
self.skip_expression_until(Punctuator::RightBracket)?;
}
if self.consume_punctuator(Punctuator::RightBracket) {
break;
}
if !self.consume_punctuator(Punctuator::Comma) || is_rest {
return None;
}
}
Some(count)
}
fn count_object_pattern(&mut self) -> Option<usize> {
self.expect_punctuator(Punctuator::LeftBrace)?;
let mut count = 0usize;
while !self.consume_punctuator(Punctuator::RightBrace) {
if self.consume_punctuator(Punctuator::Ellipsis) {
count = count.checked_add(self.count_binding_target()?)?;
if !self.consume_punctuator(Punctuator::RightBrace) {
return None;
}
break;
}
let shorthand = match self.current_kind()? {
TokenKind::Identifier(_) => {
self.tokens.next();
true
}
TokenKind::Keyword(_) | TokenKind::String(_) | TokenKind::Number(_) => {
self.tokens.next();
false
}
TokenKind::Punctuator(Punctuator::LeftBracket) => {
self.skip_balanced(Punctuator::LeftBracket)?;
false
}
_ => return None,
};
if self.consume_punctuator(Punctuator::Colon) {
count = count.checked_add(self.count_binding_target()?)?;
} else if shorthand {
count = count.checked_add(1)?;
} else {
return None;
}
if self.consume_punctuator(Punctuator::Equal) {
self.skip_expression_until(Punctuator::RightBrace)?;
}
if self.consume_punctuator(Punctuator::RightBrace) {
break;
}
if !self.consume_punctuator(Punctuator::Comma) {
return None;
}
}
Some(count)
}
fn skip_expression_until(&mut self, closing: Punctuator) -> Option<()> {
let mut delimiters = Vec::new();
while let Some(kind) = self.current_kind() {
if delimiters.is_empty() && matches!(kind, TokenKind::Punctuator(Punctuator::Comma)) {
return Some(());
}
if delimiters.is_empty()
&& matches!(kind, TokenKind::Punctuator(punctuator) if *punctuator == closing)
{
return Some(());
}
Self::update_delimiters(kind, &mut delimiters)?;
self.tokens.next();
}
(closing == Punctuator::RightParen && delimiters.is_empty()).then_some(())
}
fn skip_balanced(&mut self, opening: Punctuator) -> Option<()> {
if !matches!(
self.current_kind(),
Some(TokenKind::Punctuator(current)) if *current == opening
) {
return None;
}
let mut delimiters = Vec::new();
while let Some(kind) = self.current_kind() {
Self::update_delimiters(kind, &mut delimiters)?;
self.tokens.next();
if delimiters.is_empty() {
return Some(());
}
}
None
}
fn update_delimiters(
kind: &TokenKind<'source>,
delimiters: &mut Vec<ForHeadDelimiter>,
) -> Option<()> {
match kind {
TokenKind::Punctuator(Punctuator::LeftParen) => {
delimiters.push(ForHeadDelimiter::Parenthesis)
}
TokenKind::Punctuator(Punctuator::LeftBracket) => {
delimiters.push(ForHeadDelimiter::Bracket)
}
TokenKind::Punctuator(Punctuator::LeftBrace) => {
delimiters.push(ForHeadDelimiter::Brace)
}
TokenKind::Punctuator(Punctuator::RightParen) => {
(delimiters.pop() == Some(ForHeadDelimiter::Parenthesis)).then_some(())?;
}
TokenKind::Punctuator(Punctuator::RightBracket) => {
(delimiters.pop() == Some(ForHeadDelimiter::Bracket)).then_some(())?;
}
TokenKind::Punctuator(Punctuator::RightBrace) => {
if delimiters.last() != Some(&ForHeadDelimiter::Template) {
(delimiters.pop() == Some(ForHeadDelimiter::Brace)).then_some(())?;
}
}
TokenKind::Template(part) => match part.kind {
TemplatePartKind::Head => delimiters.push(ForHeadDelimiter::Template),
TemplatePartKind::Middle => {
(delimiters.last() == Some(&ForHeadDelimiter::Template)).then_some(())?;
}
TemplatePartKind::Tail => {
(delimiters.pop() == Some(ForHeadDelimiter::Template)).then_some(())?;
}
TemplatePartKind::NoSubstitution => {}
},
_ => {}
}
Some(())
}
fn current_kind(&mut self) -> Option<&TokenKind<'source>> {
self.tokens.peek()?.as_ref().ok().map(|token| &token.kind)
}
fn consume_punctuator(&mut self, punctuator: Punctuator) -> bool {
if matches!(self.current_kind(), Some(TokenKind::Punctuator(current)) if *current == punctuator)
{
self.tokens.next();
true
} else {
false
}
}
fn expect_punctuator(&mut self, punctuator: Punctuator) -> Option<()> {
self.consume_punctuator(punctuator).then_some(())
}
}
enum DestructuringAssignmentReference {
Identifier(IdentifierReference),
Member(MemberReference),
}
impl DestructuringAssignmentReference {
fn depth(&self) -> u8 {
match self {
Self::Identifier(reference) => u8::from(reference.object_environment),
Self::Member(MemberReference::Field { .. }) => 1,
Self::Member(MemberReference::Computed { .. }) => 2,
Self::Member(MemberReference::Super { .. }) => 3,
Self::Member(MemberReference::Private { .. }) => 1,
}
}
fn inferred_name(&self) -> Option<NameId> {
match self {
Self::Identifier(reference) => Some(reference.name),
Self::Member(_) => None,
}
}
fn site(&self) -> Result<SourceOffset, Error> {
match self {
Self::Identifier(reference) => source_offset(reference.span),
Self::Member(
MemberReference::Field { site, .. }
| MemberReference::Computed { site }
| MemberReference::Super { site }
| MemberReference::Private { site, .. },
) => Ok(*site),
}
}
}
impl<'source> Parser<'source> {
pub(super) fn parse_array_assignment_expression(&mut self) -> Result<(), Error> {
self.parse_assignment_pattern_expression(BindingPatternKind::Array)
}
pub(super) fn parse_object_assignment_expression(&mut self) -> Result<(), Error> {
self.parse_assignment_pattern_expression(BindingPatternKind::Object)
}
fn parse_assignment_pattern_expression(
&mut self,
pattern: BindingPatternKind,
) -> Result<(), Error> {
let entry_depth = self.current_ir().context.stack_depth;
let initializer_jump = self.emit_instruction(Instruction::Goto(u32::MAX))?;
let assignment_target = self.current_ir().ops.len();
self.current_ir_mut().context.stack_depth = entry_depth
.checked_add(2)
.ok_or_else(|| Error::new(ErrorKind::JsInternal, "stack overflow"))?;
self.parse_assignment_pattern(pattern)?;
self.require_stack_depth(entry_depth + 1, "assignment pattern")?;
let done_jump = self.emit_instruction(Instruction::Goto(u32::MAX))?;
let initializer_target = self.current_ir().ops.len();
self.patch_jump(initializer_jump, initializer_target)?;
self.current_ir_mut().context.stack_depth = entry_depth;
self.expect_punctuator(Punctuator::Equal)?;
self.parse_assignment_allow_in()?;
self.anonymous_function_definition = None;
self.emit_instruction(Instruction::Dup)?;
self.require_stack_depth(entry_depth + 2, "assignment pattern initializer")?;
self.emit_instruction(Instruction::Goto(
u32::try_from(assignment_target)
.map_err(|_| Error::new(ErrorKind::JsInternal, "out of memory"))?,
))?;
let done_target = self.current_ir().ops.len();
self.patch_jump(done_jump, done_target)?;
self.current_ir_mut().context.stack_depth = entry_depth + 1;
self.current_ir_mut().context.last_member_reference = None;
self.current_ir_mut().context.last_identifier_reference = None;
self.current_ir_mut().context.last_optional_chain = None;
Ok(())
}
pub(super) fn parse_for_array_assignment_pattern(
&mut self,
_iteration_kind: ForIterationKind,
) -> Result<ForAssignmentTargetInfo, Error> {
self.parse_for_assignment_pattern(BindingPatternKind::Array)
}
pub(super) fn parse_for_object_assignment_pattern(
&mut self,
_iteration_kind: ForIterationKind,
) -> Result<ForAssignmentTargetInfo, Error> {
self.parse_for_assignment_pattern(BindingPatternKind::Object)
}
fn parse_for_assignment_pattern(
&mut self,
pattern: BindingPatternKind,
) -> Result<ForAssignmentTargetInfo, Error> {
self.parse_assignment_pattern(pattern)?;
Ok(ForAssignmentTargetInfo {
declaration: ForAssignmentDeclaration::Assignment,
var_initializer: None,
is_destructuring: true,
})
}
pub(super) fn parse_array_binding_declaration(
&mut self,
declaration: ForAssignmentDeclaration,
is_const: bool,
) -> Result<(), Error> {
self.parse_binding_declaration(declaration, is_const, BindingPatternKind::Array)
}
pub(super) fn parse_object_binding_declaration(
&mut self,
declaration: ForAssignmentDeclaration,
is_const: bool,
) -> Result<(), Error> {
self.parse_binding_declaration(declaration, is_const, BindingPatternKind::Object)
}
pub(super) fn parse_catch_array_binding_pattern(&mut self) -> Result<(), Error> {
self.parse_catch_binding_pattern(BindingPatternKind::Array)
}
pub(super) fn parse_catch_object_binding_pattern(&mut self) -> Result<(), Error> {
self.parse_catch_binding_pattern(BindingPatternKind::Object)
}
fn parse_catch_binding_pattern(&mut self, pattern: BindingPatternKind) -> Result<(), Error> {
let entry_depth = self.current_ir().context.stack_depth;
let expected_depth = entry_depth
.checked_sub(1)
.ok_or_else(|| Error::internal("catch binding pattern has no exception value"))?;
self.parse_nested_binding_element(
ForAssignmentDeclaration::Lexical,
false,
BindingSite::Catch,
false,
pattern,
)?;
self.require_stack_depth(expected_depth, "catch binding pattern")
}
fn parse_binding_declaration(
&mut self,
declaration: ForAssignmentDeclaration,
is_const: bool,
pattern: BindingPatternKind,
) -> Result<(), Error> {
if !matches!(
declaration,
ForAssignmentDeclaration::Var | ForAssignmentDeclaration::Lexical
) {
return Err(Error::internal(
"binding pattern received a non-declaration target",
));
}
let pattern_span = self.current().span;
if !self.binding_initializer_ahead(pattern) {
return Err(Error::syntax("variable name expected", pattern_span));
}
let entry_depth = self.current_ir().context.stack_depth;
let initializer_jump = self.emit_instruction(Instruction::Goto(u32::MAX))?;
let assignment_target = self.current_ir().ops.len();
self.current_ir_mut().context.stack_depth = entry_depth
.checked_add(1)
.ok_or_else(|| Error::new(ErrorKind::JsInternal, "stack overflow"))?;
match pattern {
BindingPatternKind::Array => {
self.parse_array_binding_pattern(declaration, is_const, BindingSite::Declaration)?;
}
BindingPatternKind::Object => {
self.parse_object_binding_pattern(declaration, is_const, BindingSite::Declaration)?;
}
}
self.require_stack_depth(entry_depth, "binding pattern assignment")?;
let done_jump = self.emit_instruction(Instruction::Goto(u32::MAX))?;
let initializer_target = self.current_ir().ops.len();
self.patch_jump(initializer_jump, initializer_target)?;
self.current_ir_mut().context.stack_depth = entry_depth;
if !self.consume_punctuator(Punctuator::Equal)? {
if let TokenKind::Punctuator(punctuator) = self.current().kind {
return Err(self.syntax_here(format!(
"unexpected token in expression: '{}'",
punctuator.as_str()
)));
}
return Err(Error::syntax("variable name expected", pattern_span));
}
self.parse_assignment_allow_in()?;
self.anonymous_function_definition = None;
self.require_stack_depth(entry_depth + 1, "binding pattern initializer")?;
self.emit_instruction(Instruction::Goto(
u32::try_from(assignment_target)
.map_err(|_| Error::new(ErrorKind::JsInternal, "out of memory"))?,
))?;
let done_target = self.current_ir().ops.len();
self.patch_jump(done_jump, done_target)?;
self.current_ir_mut().context.stack_depth = entry_depth;
Ok(())
}
fn binding_initializer_ahead(&self, pattern: BindingPatternKind) -> bool {
match pattern {
BindingPatternKind::Array => self.array_binding_following_token(),
BindingPatternKind::Object => self.object_binding_following_token(),
}
.is_some_and(|token| matches!(token.kind, TokenKind::Punctuator(Punctuator::Equal)))
}
pub(super) fn array_assignment_pattern_ahead(&self) -> bool {
self.is_punctuator(Punctuator::LeftBracket)
&& self
.array_binding_following_token()
.is_some_and(|token| matches!(token.kind, TokenKind::Punctuator(Punctuator::Equal)))
}
pub(super) fn object_assignment_pattern_ahead(&self) -> bool {
self.is_punctuator(Punctuator::LeftBrace)
&& self
.object_binding_following_token()
.is_some_and(|token| matches!(token.kind, TokenKind::Punctuator(Punctuator::Equal)))
}
pub(super) fn for_array_assignment_pattern_ahead(
&self,
iteration_kind: ForIterationKind,
) -> bool {
self.array_binding_following_token()
.is_some_and(|token| Self::is_iteration_delimiter(&token, iteration_kind))
}
pub(super) fn for_object_assignment_pattern_ahead(
&self,
iteration_kind: ForIterationKind,
) -> bool {
self.object_binding_following_token()
.is_some_and(|token| Self::is_iteration_delimiter(&token, iteration_kind))
}
fn is_iteration_delimiter(token: &Token<'source>, iteration_kind: ForIterationKind) -> bool {
match iteration_kind {
ForIterationKind::In => matches!(token.kind, TokenKind::Keyword(Keyword::In)),
ForIterationKind::Of => matches!(
&token.kind,
TokenKind::Identifier(identifier)
if identifier.raw == "of" && !identifier.has_escape
),
}
}
fn array_binding_following_token(&self) -> Option<Token<'source>> {
self.binding_pattern_following_token(Punctuator::LeftBracket)
}
fn object_binding_following_token(&self) -> Option<Token<'source>> {
self.binding_pattern_following_token(Punctuator::LeftBrace)
}
fn object_binding_has_rest(&self) -> bool {
self.binding_pattern_scan(Punctuator::LeftBrace)
.is_some_and(|scan| scan.has_object_rest)
}
#[cfg(test)]
pub(super) fn parenthesized_parameter_has_assignment(&self) -> Option<bool> {
self.parenthesized_parameter_scan()
.map(|scan| scan.has_assignment)
}
pub(super) fn parenthesized_parameter_scan(&self) -> Option<ParenthesizedParameterScan> {
let start = self.current().span.start.byte_offset;
let context = self.lexer.context();
if let Some((offset, lex_context, result)) = self.lookahead.borrow().parameter_scan
&& (offset, lex_context) == (start, context)
{
return result;
}
let result = self.scan_parenthesized_parameters();
self.lookahead.borrow_mut().parameter_scan = Some((start, context, result));
result
}
fn scan_parenthesized_parameters(&self) -> Option<ParenthesizedParameterScan> {
let mut assignment_seen = false;
let scan = self
.binding_pattern_scan_recording_assignment(Punctuator::LeftParen, &mut assignment_seen);
let has_assignment = scan
.as_ref()
.map(|scan| scan.has_assignment)
.or_else(|| assignment_seen.then_some(true))?;
let bound_name_count = has_assignment
.then(|| self.parenthesized_parameter_tokens())
.flatten()
.and_then(|tokens| FormalParameterBoundNameCounter::new(tokens).count());
Some(ParenthesizedParameterScan {
has_assignment,
bound_name_count,
})
}
fn parenthesized_parameter_tokens(
&self,
) -> Option<impl Iterator<Item = Result<Token<'source>, ()>> + '_> {
if !self.is_punctuator(Punctuator::LeftParen) {
return None;
}
let mut lexer = self.lexer.clone();
lexer.seek(self.current().span.start);
if !matches!(
self.probe_token(&mut lexer, LexicalGoal::Div).ok()?.kind,
TokenKind::Punctuator(Punctuator::LeftParen)
) {
return None;
}
let mut delimiters = self.delimiter_stack();
delimiters.push(ForHeadDelimiter::Parenthesis);
let mut goal = LexicalGoal::Div;
let mut regexp_allowed = true;
let mut finished = false;
Some(std::iter::from_fn(move || {
if finished {
return None;
}
let result = (|| -> Result<Option<Token<'source>>, ()> {
let requested_goal = goal;
goal = LexicalGoal::Div;
let mut token = self
.probe_token(&mut lexer, requested_goal)
.map_err(|_| ())?;
if requested_goal == LexicalGoal::Div
&& regexp_allowed
&& matches!(
token.kind,
TokenKind::Punctuator(Punctuator::Divide | Punctuator::DivideAssign)
)
{
lexer.seek(token.span.start);
token = self
.probe_token(&mut lexer, LexicalGoal::RegExp)
.map_err(|_| ())?;
}
let root_close =
matches!(token.kind, TokenKind::Punctuator(Punctuator::RightParen))
&& delimiters.as_slice() == [ForHeadDelimiter::Parenthesis];
if root_close {
return Ok(None);
}
match &token.kind {
TokenKind::Punctuator(Punctuator::LeftParen) => {
delimiters.push(ForHeadDelimiter::Parenthesis);
}
TokenKind::Punctuator(Punctuator::LeftBracket) => {
delimiters.push(ForHeadDelimiter::Bracket);
}
TokenKind::Punctuator(Punctuator::LeftBrace) => {
delimiters.push(ForHeadDelimiter::Brace);
}
TokenKind::Punctuator(Punctuator::RightParen) => {
if delimiters.pop() != Some(ForHeadDelimiter::Parenthesis) {
return Err(());
}
}
TokenKind::Punctuator(Punctuator::RightBracket) => {
if delimiters.pop() != Some(ForHeadDelimiter::Bracket) {
return Err(());
}
}
TokenKind::Punctuator(Punctuator::RightBrace) => {
if delimiters.last() == Some(&ForHeadDelimiter::Template) {
goal = LexicalGoal::TemplateContinuation;
regexp_allowed = true;
return Ok(Some(token));
}
if delimiters.pop() != Some(ForHeadDelimiter::Brace) {
return Err(());
}
}
TokenKind::Template(part) => match part.kind {
TemplatePartKind::Head => {
delimiters.push(ForHeadDelimiter::Template);
}
TemplatePartKind::Middle => {
if delimiters.last() != Some(&ForHeadDelimiter::Template) {
return Err(());
}
}
TemplatePartKind::Tail => {
if delimiters.pop() != Some(ForHeadDelimiter::Template) {
return Err(());
}
}
TemplatePartKind::NoSubstitution => {}
},
TokenKind::Eof => return Err(()),
_ => {}
}
regexp_allowed = for_head_regexp_allowed_after(&token.kind);
Ok(Some(token))
})();
match result {
Ok(Some(token)) => Some(Ok(token)),
Ok(None) => {
finished = true;
None
}
Err(()) => {
finished = true;
Some(Err(()))
}
}
}))
}
fn binding_pattern_following_token(&self, opening: Punctuator) -> Option<Token<'source>> {
self.binding_pattern_scan(opening)
.map(|scan| scan.following)
}
fn binding_pattern_scan(&self, opening: Punctuator) -> Option<BindingPatternScan<'source>> {
let mut assignment_seen = false;
self.binding_pattern_scan_recording_assignment(opening, &mut assignment_seen)
}
fn binding_pattern_scan_recording_assignment(
&self,
opening: Punctuator,
assignment_seen: &mut bool,
) -> Option<BindingPatternScan<'source>> {
use crate::engine::compiler::parser::lookahead::BindingScanMemo;
if !self.is_punctuator(opening) {
return None;
}
let start = self.current().span.start.byte_offset;
let context = self.lexer.context();
if let Some(memo) = self.lookahead.borrow().binding_scan
&& (memo.start, memo.context, memo.opening) == (start, context, opening)
{
*assignment_seen |= memo.assignment_seen;
return memo.scan;
}
let mut seen = false;
let scan = self.scan_binding_pattern(opening, &mut seen);
self.lookahead.borrow_mut().binding_scan = Some(BindingScanMemo {
start,
context,
opening,
scan,
assignment_seen: seen,
});
*assignment_seen |= seen;
scan
}
fn scan_binding_pattern(
&self,
opening: Punctuator,
assignment_seen: &mut bool,
) -> Option<BindingPatternScan<'source>> {
if !self.is_punctuator(opening) {
return None;
}
let root = match opening {
Punctuator::LeftParen => ForHeadDelimiter::Parenthesis,
Punctuator::LeftBracket => ForHeadDelimiter::Bracket,
Punctuator::LeftBrace => ForHeadDelimiter::Brace,
_ => return None,
};
let mut lexer = self.lexer.clone();
lexer.seek(self.current().span.start);
let mut delimiters = self.delimiter_stack();
let mut goal = LexicalGoal::Div;
let mut regexp_allowed = true;
let mut has_object_rest = false;
let mut has_assignment = false;
loop {
let requested_goal = goal;
goal = LexicalGoal::Div;
let Ok(mut token) = self.probe_token(&mut lexer, requested_goal) else {
return None;
};
if requested_goal == LexicalGoal::Div
&& regexp_allowed
&& matches!(
token.kind,
TokenKind::Punctuator(Punctuator::Divide | Punctuator::DivideAssign)
)
{
lexer.seek(token.span.start);
let Ok(regexp) = self.probe_token(&mut lexer, LexicalGoal::RegExp) else {
return None;
};
token = regexp;
}
if root == ForHeadDelimiter::Brace
&& delimiters.as_slice() == [ForHeadDelimiter::Brace]
&& matches!(token.kind, TokenKind::Punctuator(Punctuator::Ellipsis))
{
has_object_rest = true;
}
if matches!(token.kind, TokenKind::Punctuator(Punctuator::Equal)) {
has_assignment = true;
*assignment_seen = true;
}
match &token.kind {
TokenKind::Punctuator(Punctuator::LeftParen) => {
if delimiters.len() >= 255 {
return None;
}
delimiters.push(ForHeadDelimiter::Parenthesis);
}
TokenKind::Punctuator(Punctuator::LeftBracket) => {
if delimiters.len() >= 255 {
return None;
}
delimiters.push(ForHeadDelimiter::Bracket);
}
TokenKind::Punctuator(Punctuator::LeftBrace) => {
if delimiters.len() >= 255 {
return None;
}
delimiters.push(ForHeadDelimiter::Brace);
}
TokenKind::Punctuator(Punctuator::RightParen) => {
if delimiters.pop() != Some(ForHeadDelimiter::Parenthesis) {
return None;
}
if root == ForHeadDelimiter::Parenthesis && delimiters.is_empty() {
return Some(BindingPatternScan {
following: self.probe_token(&mut lexer, LexicalGoal::Div).ok()?,
has_object_rest,
has_assignment,
});
}
}
TokenKind::Punctuator(Punctuator::RightBracket) => {
if delimiters.pop() != Some(ForHeadDelimiter::Bracket) {
return None;
}
if root == ForHeadDelimiter::Bracket && delimiters.is_empty() {
return Some(BindingPatternScan {
following: self.probe_token(&mut lexer, LexicalGoal::Div).ok()?,
has_object_rest,
has_assignment,
});
}
}
TokenKind::Punctuator(Punctuator::RightBrace) => {
if delimiters.last() == Some(&ForHeadDelimiter::Template) {
goal = LexicalGoal::TemplateContinuation;
regexp_allowed = true;
continue;
}
if delimiters.pop() != Some(ForHeadDelimiter::Brace) {
return None;
}
if root == ForHeadDelimiter::Brace && delimiters.is_empty() {
return Some(BindingPatternScan {
following: self.probe_token(&mut lexer, LexicalGoal::Div).ok()?,
has_object_rest,
has_assignment,
});
}
}
TokenKind::Template(part) => match part.kind {
TemplatePartKind::Head => {
if delimiters.len() >= 255 {
return None;
}
delimiters.push(ForHeadDelimiter::Template);
}
TemplatePartKind::Middle => {
if delimiters.last() != Some(&ForHeadDelimiter::Template) {
return None;
}
}
TemplatePartKind::Tail => {
if delimiters.pop() != Some(ForHeadDelimiter::Template) {
return None;
}
}
TemplatePartKind::NoSubstitution => {}
},
TokenKind::Eof => return None,
_ => {}
}
regexp_allowed = for_head_regexp_allowed_after(&token.kind);
}
}
pub(super) fn parse_for_array_binding_pattern(
&mut self,
iteration_kind: ForIterationKind,
declaration: ForAssignmentDeclaration,
is_const: bool,
) -> Result<ForAssignmentTargetInfo, Error> {
self.parse_for_binding_pattern(
iteration_kind,
declaration,
is_const,
BindingPatternKind::Array,
)
}
pub(super) fn parse_for_object_binding_pattern(
&mut self,
iteration_kind: ForIterationKind,
declaration: ForAssignmentDeclaration,
is_const: bool,
) -> Result<ForAssignmentTargetInfo, Error> {
self.parse_for_binding_pattern(
iteration_kind,
declaration,
is_const,
BindingPatternKind::Object,
)
}
fn parse_for_binding_pattern(
&mut self,
iteration_kind: ForIterationKind,
declaration: ForAssignmentDeclaration,
is_const: bool,
pattern: BindingPatternKind,
) -> Result<ForAssignmentTargetInfo, Error> {
if !matches!(
declaration,
ForAssignmentDeclaration::Var | ForAssignmentDeclaration::Lexical
) {
return Err(Error::internal(
"binding pattern received a non-declaration target",
));
}
match pattern {
BindingPatternKind::Array => self.parse_array_binding_pattern(
declaration,
is_const,
BindingSite::Iteration(iteration_kind),
)?,
BindingPatternKind::Object => self.parse_object_binding_pattern(
declaration,
is_const,
BindingSite::Iteration(iteration_kind),
)?,
}
Ok(ForAssignmentTargetInfo {
declaration,
var_initializer: None,
is_destructuring: true,
})
}
pub(super) fn parse_array_parameter_binding_pattern(
&mut self,
argument: u16,
) -> Result<bool, Error> {
self.parse_parameter_binding_pattern(argument, BindingPatternKind::Array)
}
pub(super) fn parse_object_parameter_binding_pattern(
&mut self,
argument: u16,
) -> Result<bool, Error> {
self.parse_parameter_binding_pattern(argument, BindingPatternKind::Object)
}
pub(super) fn parse_array_rest_parameter_binding_pattern(
&mut self,
start: u16,
) -> Result<bool, Error> {
self.parse_rest_parameter_binding_pattern(start, BindingPatternKind::Array)
}
pub(super) fn parse_object_rest_parameter_binding_pattern(
&mut self,
start: u16,
) -> Result<bool, Error> {
self.parse_rest_parameter_binding_pattern(start, BindingPatternKind::Object)
}
fn parse_rest_parameter_binding_pattern(
&mut self,
start: u16,
pattern: BindingPatternKind,
) -> Result<bool, Error> {
self.emit_instruction(Instruction::Rest(start))?;
self.parse_parameter_binding_pattern_value(pattern)
}
fn parse_parameter_binding_pattern(
&mut self,
argument: u16,
pattern: BindingPatternKind,
) -> Result<bool, Error> {
self.emit_instruction(Instruction::GetArg(argument))?;
self.parse_parameter_binding_pattern_value(pattern)
}
fn parse_parameter_binding_pattern_value(
&mut self,
pattern: BindingPatternKind,
) -> Result<bool, Error> {
let opening = match pattern {
BindingPatternKind::Array => Punctuator::LeftBracket,
BindingPatternKind::Object => Punctuator::LeftBrace,
};
let has_initializer = self
.binding_pattern_following_token(opening)
.is_some_and(|token| matches!(token.kind, TokenKind::Punctuator(Punctuator::Equal)));
if !has_initializer {
self.parse_binding_pattern(
pattern,
ForAssignmentDeclaration::Var,
false,
BindingSite::Parameter,
)?;
return Ok(false);
}
let value_depth = self.current_ir().context.stack_depth;
self.emit_instruction(Instruction::Dup)?;
self.emit_instruction(Instruction::Undefined)?;
self.emit_instruction(Instruction::StrictEq)?;
let use_initializer = self.emit_instruction(Instruction::IfTrue(u32::MAX))?;
let assignment_target = self.current_ir().ops.len();
self.parse_binding_pattern(
pattern,
ForAssignmentDeclaration::Var,
false,
BindingSite::Parameter,
)?;
self.require_stack_depth(value_depth - 1, "parameter BindingPattern assignment")?;
self.expect_punctuator(Punctuator::Equal)?;
let done_jump = self.emit_instruction(Instruction::Goto(u32::MAX))?;
let initializer_target = self.current_ir().ops.len();
self.patch_jump(use_initializer, initializer_target)?;
self.current_ir_mut().context.stack_depth = value_depth;
self.emit_instruction(Instruction::Drop)?;
self.parse_assignment_allow_in()?;
self.anonymous_function_definition = None;
self.require_stack_depth(value_depth, "parameter BindingPattern initializer")?;
self.emit_instruction(Instruction::Goto(
u32::try_from(assignment_target)
.map_err(|_| Error::new(ErrorKind::JsInternal, "out of memory"))?,
))?;
let done_target = self.current_ir().ops.len();
self.patch_jump(done_jump, done_target)?;
self.current_ir_mut().context.stack_depth = value_depth - 1;
Ok(true)
}
fn parse_assignment_pattern(&mut self, pattern: BindingPatternKind) -> Result<(), Error> {
match pattern {
BindingPatternKind::Array => self.parse_array_assignment_pattern(),
BindingPatternKind::Object => self.parse_object_assignment_pattern(),
}
}
fn parse_array_assignment_pattern(&mut self) -> Result<(), Error> {
self.expect_punctuator(Punctuator::LeftBracket)?;
self.emit_instruction(Instruction::ForOfStart)?;
self.push_destructuring_iterator_control()?;
while !self.is_punctuator(Punctuator::RightBracket) {
let is_rest = self.consume_punctuator(Punctuator::Ellipsis)?;
let element_span = self.current().span;
if is_rest
&& matches!(
self.current().kind,
TokenKind::Punctuator(Punctuator::Comma | Punctuator::RightBracket)
)
{
return Err(Error::syntax("missing binding pattern...", element_span));
}
let nested_object_follow = self
.is_punctuator(Punctuator::LeftBrace)
.then(|| self.object_binding_following_token())
.flatten();
let nested_array_follow = self
.is_punctuator(Punctuator::LeftBracket)
.then(|| self.array_binding_following_token())
.flatten();
let nested_pattern = self.nested_binding_pattern_kind(Punctuator::RightBracket);
if let Some(pattern) = nested_pattern {
let nested_follow = match pattern {
BindingPatternKind::Array => nested_array_follow.as_ref(),
BindingPatternKind::Object => nested_object_follow.as_ref(),
};
if is_rest
&& nested_follow.is_some_and(|token| {
matches!(token.kind, TokenKind::Punctuator(Punctuator::Equal))
})
{
return Err(Error::syntax(
"rest element cannot have a default value",
element_span,
));
}
if is_rest {
self.emit_array_rest(0, None)?;
} else {
self.emit_instruction(Instruction::ForOfNext(0))?;
self.emit_instruction(Instruction::Drop)?;
}
self.parse_nested_assignment_element(pattern, is_rest)?;
} else if !is_rest && self.consume_punctuator(Punctuator::Comma)? {
self.emit_instruction(Instruction::ForOfNext(0))?;
self.emit_instruction(Instruction::Drop)?;
self.emit_instruction(Instruction::Drop)?;
continue;
} else {
self.parse_array_assignment_leaf(is_rest)?;
}
if self.is_punctuator(Punctuator::RightBracket) {
break;
}
if is_rest {
return Err(self.syntax_here("rest element must be the last one"));
}
self.expect_punctuator(Punctuator::Comma)?;
}
self.expect_punctuator(Punctuator::RightBracket)?;
self.emit_instruction(Instruction::IteratorClose)?;
self.pop_destructuring_iterator_control()?;
self.anonymous_function_definition = None;
Ok(())
}
fn parse_array_assignment_leaf(&mut self, is_rest: bool) -> Result<(), Error> {
let target = self.parse_destructuring_assignment_reference()?;
let reference_depth = target.depth();
let inferred_name = target.inferred_name();
let target_site = target.site()?;
if is_rest {
self.emit_array_assignment_rest(reference_depth, target_site)?;
} else {
self.emit_instruction_at(Instruction::ForOfNext(reference_depth), target_site)?;
self.emit_instruction(Instruction::Drop)?;
if self.consume_punctuator(Punctuator::Equal)? {
self.emit_instruction(Instruction::Dup)?;
self.emit_instruction(Instruction::Undefined)?;
self.emit_instruction(Instruction::StrictEq)?;
let has_value = self.emit_instruction(Instruction::IfFalse(u32::MAX))?;
self.emit_instruction(Instruction::Drop)?;
self.parse_assignment_allow_in()?;
let anonymous_default = self.take_anonymous_function_definition();
if let (Some(definition), Some(name)) = (anonymous_default, inferred_name) {
let js_name = JsString::try_from_utf8(self.names.name(name))?;
let name_constant =
self.add_constant(IrConstant::Primitive(Value::String(js_name)))?;
self.emit_anonymous_set_name(definition, Instruction::SetName(name_constant))?;
}
let has_value_target = self.current_ir().ops.len();
self.patch_jump(has_value, has_value_target)?;
}
}
self.emit_destructuring_assignment_put(target)
}
fn parse_destructuring_assignment_reference(
&mut self,
) -> Result<DestructuringAssignmentReference, Error> {
self.parse_left_hand_side_expression()?;
if self.current_ir().context.last_optional_chain.is_some() {
return Err(self.syntax_here("invalid destructuring target"));
}
if let Some(target) = self.take_tail_identifier_reference()? {
self.validate_identifier_assignment_target(&target)?;
return Ok(DestructuringAssignmentReference::Identifier(target));
}
if let Some(target) = self.take_tail_member_reference()? {
return Ok(DestructuringAssignmentReference::Member(target));
}
Err(self.syntax_here("invalid destructuring target"))
}
fn emit_destructuring_assignment_put(
&mut self,
target: DestructuringAssignmentReference,
) -> Result<(), Error> {
match target {
DestructuringAssignmentReference::Identifier(target) => {
if target.object_environment {
self.emit_identifier_reference_inherited(
target.name,
target.span,
target.scope,
IdentifierReferenceAccess::Set,
)?;
self.emit_instruction(Instruction::Drop)?;
} else {
self.emit_identifier_inherited(
target.name,
target.span,
target.scope,
IdentifierAccess::Put,
)?;
}
}
DestructuringAssignmentReference::Member(MemberReference::Field { key, site }) => {
self.emit_instruction_at(Instruction::PutField(key), site)?;
}
DestructuringAssignmentReference::Member(MemberReference::Computed { site }) => {
self.emit_instruction_at(Instruction::PutArrayEl, site)?;
}
DestructuringAssignmentReference::Member(MemberReference::Super { site }) => {
self.emit_instruction_at(Instruction::PutSuperValue, site)?;
}
DestructuringAssignmentReference::Member(MemberReference::Private {
name,
span,
scope,
site,
}) => {
self.emit_private_field_operation(
name,
span,
scope,
PrivateFieldAccess::Put,
site,
)?;
}
}
self.anonymous_function_definition = None;
Ok(())
}
fn parse_nested_assignment_element(
&mut self,
pattern: BindingPatternKind,
is_rest: bool,
) -> Result<(), Error> {
let value_depth = self.current_ir().context.stack_depth;
if value_depth == 0 {
return Err(Error::internal(
"nested assignment pattern has no value to consume",
));
}
if is_rest {
self.parse_assignment_pattern(pattern)?;
if self.is_punctuator(Punctuator::Equal) {
return Err(self.syntax_here("rest element cannot have a default value"));
}
return Ok(());
}
self.emit_instruction(Instruction::Dup)?;
self.emit_instruction(Instruction::Undefined)?;
self.emit_instruction(Instruction::StrictEq)?;
let use_initializer = self.emit_instruction(Instruction::IfTrue(u32::MAX))?;
let assignment_target = self.current_ir().ops.len();
self.parse_assignment_pattern(pattern)?;
self.require_stack_depth(value_depth - 1, "nested assignment pattern")?;
if self.consume_punctuator(Punctuator::Equal)? {
let done_jump = self.emit_instruction(Instruction::Goto(u32::MAX))?;
let initializer_target = self.current_ir().ops.len();
self.patch_jump(use_initializer, initializer_target)?;
self.current_ir_mut().context.stack_depth = value_depth;
self.emit_instruction(Instruction::Drop)?;
self.parse_assignment_allow_in()?;
self.anonymous_function_definition = None;
self.require_stack_depth(value_depth, "nested assignment pattern initializer")?;
self.emit_instruction(Instruction::Goto(
u32::try_from(assignment_target)
.map_err(|_| Error::new(ErrorKind::JsInternal, "out of memory"))?,
))?;
let done_target = self.current_ir().ops.len();
self.patch_jump(done_jump, done_target)?;
self.current_ir_mut().context.stack_depth = value_depth - 1;
} else {
self.patch_jump(use_initializer, assignment_target)?;
}
Ok(())
}
fn parse_object_assignment_pattern(&mut self) -> Result<(), Error> {
let pattern_span = self.current().span;
let has_rest = self.object_binding_has_rest();
self.expect_punctuator(Punctuator::LeftBrace)?;
self.emit_instruction_at(Instruction::ToObject, source_offset(pattern_span)?)?;
if has_rest {
self.emit_instruction(Instruction::Object)?;
self.emit_instruction(Instruction::Insert2)?;
self.emit_instruction(Instruction::Drop)?;
}
while !self.is_punctuator(Punctuator::RightBrace) {
if self.is_punctuator(Punctuator::Ellipsis) {
self.parse_object_assignment_rest(has_rest)?;
break;
}
let property = self.parse_object_binding_property_name()?;
let shorthand = matches!(
&property,
ObjectBindingPropertyKey::Fixed {
shorthand: Some(_),
..
}
);
if !shorthand {
self.advance()?;
}
let computed_key_is_canonical =
self.emit_object_binding_exclusion(&property, has_rest)?;
let nested_pattern = if shorthand {
None
} else {
self.nested_binding_pattern_kind(Punctuator::RightBrace)
};
if let Some(pattern) = nested_pattern {
self.emit_nested_object_property_value(&property)?;
self.parse_nested_assignment_element(pattern, false)?;
} else {
self.parse_object_assignment_leaf(property, computed_key_is_canonical)?;
}
if self.is_punctuator(Punctuator::RightBrace) {
break;
}
self.expect_punctuator(Punctuator::Comma)?;
}
self.expect_punctuator(Punctuator::RightBrace)?;
self.emit_instruction(Instruction::Drop)?;
if has_rest {
self.emit_instruction(Instruction::Drop)?;
}
self.anonymous_function_definition = None;
Ok(())
}
fn parse_object_assignment_leaf(
&mut self,
property: ObjectBindingPropertyKey<'source>,
computed_key_is_canonical: bool,
) -> Result<(), Error> {
let (fixed_key, property_span, shorthand) = match property {
ObjectBindingPropertyKey::Fixed {
key,
token,
shorthand,
} => (
Some(key),
token.span,
shorthand.map(|identifier| (token, identifier)),
),
ObjectBindingPropertyKey::Computed { span } => (None, span, None),
};
if fixed_key.is_some() {
self.emit_instruction(Instruction::Dup)?;
} else {
if !computed_key_is_canonical {
self.emit_instruction_at(Instruction::ToPropKey, source_offset(property_span)?)?;
}
self.emit_instruction(Instruction::Dup1)?;
}
let target = if let Some((token, identifier)) = shorthand {
self.prepare_shorthand_assignment_reference(token, identifier)?
} else {
self.parse_destructuring_assignment_reference()?
};
let reference_depth = target.depth();
let inferred_name = target.inferred_name();
let target_site = target.site()?;
self.emit_object_assignment_read_reorder(fixed_key.is_none(), reference_depth)?;
if let Some(key) = fixed_key {
let key = self.add_constant(IrConstant::Primitive(Value::String(key)))?;
self.emit_instruction_at(Instruction::GetField(key), target_site)?;
} else {
self.emit_instruction_at(Instruction::GetArrayEl, target_site)?;
}
if self.consume_punctuator(Punctuator::Equal)? {
self.emit_instruction(Instruction::Dup)?;
self.emit_instruction(Instruction::Undefined)?;
self.emit_instruction(Instruction::StrictEq)?;
let has_value = self.emit_instruction(Instruction::IfFalse(u32::MAX))?;
self.emit_instruction(Instruction::Drop)?;
self.parse_assignment_allow_in()?;
let anonymous_default = self.take_anonymous_function_definition();
if let (Some(definition), Some(name)) = (anonymous_default, inferred_name) {
let name_constant = self.add_constant(IrConstant::Primitive(Value::String(
JsString::try_from_utf8(self.names.name(name))?,
)))?;
self.emit_anonymous_set_name(definition, Instruction::SetName(name_constant))?;
}
let has_value_target = self.current_ir().ops.len();
self.patch_jump(has_value, has_value_target)?;
}
self.emit_destructuring_assignment_put(target)
}
fn prepare_shorthand_assignment_reference(
&mut self,
token: Token<'source>,
identifier: Identifier<'source>,
) -> Result<DestructuringAssignmentReference, Error> {
self.validate_identifier(
&identifier,
token.span,
self.current_ir().strict,
IdentifierContext::Reference,
)?;
if self.current_ir().strict
&& matches!(
self.identifier_text(&identifier).as_ref(),
"eval" | "arguments"
)
{
return Err(self.syntax_here("invalid destructuring target"));
}
let scope = self.current_ir().context.current_scope;
let object_environment =
self.parser_scope_has_authored_with(self.current_function, scope)?;
let reference = IdentifierReference {
name: self.intern_identifier(&identifier),
span: token.span,
scope,
object_environment,
};
if object_environment {
self.emit_identifier_reference_inherited(
reference.name,
reference.span,
reference.scope,
IdentifierReferenceAccess::Prepare,
)?;
}
Ok(DestructuringAssignmentReference::Identifier(reference))
}
fn emit_object_assignment_read_reorder(
&mut self,
computed: bool,
reference_depth: u8,
) -> Result<(), Error> {
match (computed, reference_depth) {
(_, 0) => {}
(false, 1) => {
self.emit_instruction(Instruction::Insert2)?;
self.emit_instruction(Instruction::Drop)?;
}
(false, 2) => {
self.emit_instruction(Instruction::Perm3)?;
self.emit_instruction(Instruction::Insert2)?;
self.emit_instruction(Instruction::Drop)?;
}
(false, 3) => {
self.emit_instruction(Instruction::Rot4Left)?;
}
(true, 1) => {
self.emit_instruction(Instruction::Insert2)?;
self.emit_instruction(Instruction::Drop)?;
self.emit_instruction(Instruction::Perm3)?;
}
(true, 2) => {
self.emit_instruction(Instruction::Rot4Left)?;
self.emit_instruction(Instruction::Rot4Left)?;
}
(true, 3) => {
self.emit_instruction(Instruction::Rot4Left)?;
self.emit_instruction(Instruction::Perm5)?;
self.emit_instruction(Instruction::Perm5)?;
self.emit_instruction(Instruction::Perm5)?;
}
_ => return Err(Error::internal("unsupported destructuring Reference depth")),
}
Ok(())
}
fn parse_object_assignment_rest(&mut self, has_rest: bool) -> Result<(), Error> {
self.advance()?;
let target = self.parse_destructuring_assignment_reference()?;
if !self.is_punctuator(Punctuator::RightBrace) {
return Err(self.syntax_here("assignment rest property must be last"));
}
if !has_rest {
return Err(Error::internal(
"object rest assignment was absent from its pattern skip-scan",
));
}
let reference_depth = target.depth();
let target_site = target.site()?;
self.emit_instruction(Instruction::Object)?;
self.emit_instruction_at(
Instruction::CopyDataPropertiesExcluded {
target_depth: 0,
source_depth: reference_depth
.checked_add(1)
.ok_or_else(|| Error::new(ErrorKind::JsInternal, "stack overflow"))?,
excluded_depth: reference_depth
.checked_add(2)
.ok_or_else(|| Error::new(ErrorKind::JsInternal, "stack overflow"))?,
},
target_site,
)?;
self.emit_destructuring_assignment_put(target)
}
fn parse_array_binding_pattern(
&mut self,
declaration: ForAssignmentDeclaration,
is_const: bool,
site: BindingSite,
) -> Result<(), Error> {
self.expect_punctuator(Punctuator::LeftBracket)?;
self.emit_instruction(Instruction::ForOfStart)?;
self.push_destructuring_iterator_control()?;
while !self.is_punctuator(Punctuator::RightBracket) {
let is_rest = self.consume_punctuator(Punctuator::Ellipsis)?;
let object_binding_span = self.current().span;
let object_binding_follow = self
.is_punctuator(Punctuator::LeftBrace)
.then(|| self.object_binding_following_token())
.flatten();
let object_binding = object_binding_follow.as_ref().is_some_and(|token| {
matches!(
token.kind,
TokenKind::Punctuator(
Punctuator::Comma | Punctuator::Equal | Punctuator::RightBracket
)
)
});
if object_binding {
if is_rest
&& object_binding_follow.as_ref().is_some_and(|token| {
matches!(token.kind, TokenKind::Punctuator(Punctuator::Equal))
})
{
return Err(Error::syntax(
"rest element cannot have a default value",
object_binding_span,
));
}
if is_rest {
self.emit_array_binding_rest(0)?;
} else {
self.emit_instruction(Instruction::ForOfNext(0))?;
self.emit_instruction(Instruction::Drop)?;
}
self.parse_nested_object_binding_element(declaration, is_const, site, is_rest)?;
if self.is_punctuator(Punctuator::RightBracket) {
break;
}
if is_rest {
return Err(self.syntax_here("rest element must be the last one"));
}
self.expect_punctuator(Punctuator::Comma)?;
continue;
}
let nested_array_span = self.current().span;
let nested_array_follow = self
.is_punctuator(Punctuator::LeftBracket)
.then(|| self.array_binding_following_token())
.flatten();
let is_nested_array = nested_array_follow.as_ref().is_some_and(|token| {
matches!(
token.kind,
TokenKind::Punctuator(
Punctuator::Comma | Punctuator::Equal | Punctuator::RightBracket
)
)
});
if is_nested_array {
if is_rest
&& nested_array_follow.as_ref().is_some_and(|token| {
matches!(token.kind, TokenKind::Punctuator(Punctuator::Equal))
})
{
return Err(Error::syntax(
"rest element cannot have a default value",
nested_array_span,
));
}
if is_rest {
self.emit_array_binding_rest(0)?;
} else {
self.emit_instruction(Instruction::ForOfNext(0))?;
self.emit_instruction(Instruction::Drop)?;
}
self.parse_nested_array_binding_element(declaration, is_const, site, is_rest)?;
if self.is_punctuator(Punctuator::RightBracket) {
break;
}
if is_rest {
return Err(self.syntax_here("rest element must be the last one"));
}
self.expect_punctuator(Punctuator::Comma)?;
continue;
}
if !is_rest && self.consume_punctuator(Punctuator::Comma)? {
self.emit_instruction(Instruction::ForOfNext(0))?;
self.emit_instruction(Instruction::Drop)?;
self.emit_instruction(Instruction::Drop)?;
continue;
}
let token = *self.current();
let TokenKind::Identifier(identifier) = token.kind else {
if is_rest
&& matches!(
token.kind,
TokenKind::Punctuator(Punctuator::Comma | Punctuator::RightBracket)
)
{
return Err(Error::syntax("missing binding pattern...", token.span));
}
return Err(Error::syntax("invalid destructuring target", token.span));
};
if identifier.escaped_reserved_word {
return Err(Error::syntax("invalid destructuring target", token.span));
}
self.validate_identifier_reservation(
&identifier,
token.span,
self.current_ir().strict,
IdentifierContext::Variable,
)?;
let invalid_lexical_let = declaration == ForAssignmentDeclaration::Lexical
&& self.identical_name(&identifier, "let");
let name = self.intern_identifier(&identifier);
let strict = self.current_ir().strict;
self.advance()?;
if invalid_lexical_let {
return Err(self.syntax_here("invalid lexical variable name"));
}
if strict && matches!(self.names.name(name), "eval" | "arguments") {
return Err(Error::syntax("invalid destructuring target", token.span));
}
match declaration {
ForAssignmentDeclaration::Lexical => {
self.register_lexical_binding(
name,
token.span,
self.current().span,
is_const,
false,
)?;
}
ForAssignmentDeclaration::Var => {
if site == BindingSite::Parameter {
self.register_pattern_parameter_binding(name, token.span, token.span)?;
} else {
self.register_var_binding(name, token.span, self.current().span)?;
}
}
ForAssignmentDeclaration::Assignment => {
unreachable!("array binding declaration was validated before parsing elements")
}
}
let reference_scope = self.current_ir().context.current_scope;
let parameter_lexical = declaration == ForAssignmentDeclaration::Var
&& site == BindingSite::Parameter
&& self.current_ir().parameter_scope.is_some();
let next_offset = if declaration == ForAssignmentDeclaration::Var && !parameter_lexical
{
self.emit_identifier_reference_inherited(
name,
token.span,
reference_scope,
IdentifierReferenceAccess::Prepare,
)?;
1
} else {
0
};
if is_rest {
self.emit_array_binding_rest(next_offset)?;
} else {
self.emit_instruction(Instruction::ForOfNext(next_offset))?;
self.emit_instruction(Instruction::Drop)?;
if self.consume_punctuator(Punctuator::Equal)? {
self.emit_instruction(Instruction::Dup)?;
self.emit_instruction(Instruction::Undefined)?;
self.emit_instruction(Instruction::StrictEq)?;
let has_value = self.emit_instruction(Instruction::IfFalse(u32::MAX))?;
self.emit_instruction(Instruction::Drop)?;
self.parse_assignment_allow_in()?;
if let Some(definition) = self.take_anonymous_function_definition() {
let js_name = JsString::try_from_utf8(self.names.name(name))?;
let name_constant =
self.add_constant(IrConstant::Primitive(Value::String(js_name)))?;
self.emit_anonymous_set_name(
definition,
Instruction::SetName(name_constant),
)?;
}
let has_value_target = self.current_ir().ops.len();
self.patch_jump(has_value, has_value_target)?;
}
}
if declaration == ForAssignmentDeclaration::Var && !parameter_lexical {
self.emit_identifier_reference_inherited(
name,
token.span,
reference_scope,
IdentifierReferenceAccess::Set,
)?;
self.emit_instruction(Instruction::Drop)?;
} else {
self.emit_identifier_inherited(
name,
token.span,
reference_scope,
IdentifierAccess::Initialize,
)?;
}
if self.is_punctuator(Punctuator::RightBracket) {
break;
}
if is_rest {
return Err(self.syntax_here("rest element must be the last one"));
}
self.expect_punctuator(Punctuator::Comma)?;
}
self.expect_punctuator(Punctuator::RightBracket)?;
self.emit_instruction(Instruction::IteratorClose)?;
self.pop_destructuring_iterator_control()?;
Ok(())
}
fn parse_object_binding_pattern(
&mut self,
declaration: ForAssignmentDeclaration,
is_const: bool,
site: BindingSite,
) -> Result<(), Error> {
let pattern_span = self.current().span;
let has_rest = self.object_binding_has_rest();
self.expect_punctuator(Punctuator::LeftBrace)?;
if site == BindingSite::Catch {
self.emit_instruction(Instruction::ToObject)?;
} else {
self.emit_instruction_at(Instruction::ToObject, source_offset(pattern_span)?)?;
}
if has_rest {
self.emit_instruction(Instruction::Object)?;
self.emit_instruction(Instruction::Insert2)?;
self.emit_instruction(Instruction::Drop)?;
}
while !self.is_punctuator(Punctuator::RightBrace) {
if self.is_punctuator(Punctuator::Ellipsis) {
self.parse_object_binding_rest(declaration, is_const, site, has_rest)?;
break;
}
let property = self.parse_object_binding_property_name()?;
let shorthand = matches!(
&property,
ObjectBindingPropertyKey::Fixed {
shorthand: Some(_),
..
}
);
if !shorthand {
self.advance()?;
}
let computed_key_is_canonical =
self.emit_object_binding_exclusion(&property, has_rest)?;
let nested_pattern = if shorthand {
None
} else {
self.nested_binding_pattern_kind(Punctuator::RightBrace)
};
if let Some(pattern) = nested_pattern {
self.emit_nested_object_property_value(&property)?;
self.parse_nested_binding_element(declaration, is_const, site, false, pattern)?;
} else {
self.parse_object_binding_leaf(
property,
declaration,
is_const,
site,
computed_key_is_canonical,
)?;
}
if self.is_punctuator(Punctuator::RightBrace) {
break;
}
self.expect_punctuator(Punctuator::Comma)?;
}
self.expect_punctuator(Punctuator::RightBrace)?;
self.emit_instruction(Instruction::Drop)?;
if has_rest {
self.emit_instruction(Instruction::Drop)?;
}
Ok(())
}
fn emit_object_binding_exclusion(
&mut self,
property: &ObjectBindingPropertyKey<'source>,
has_rest: bool,
) -> Result<bool, Error> {
if !has_rest {
return Ok(false);
}
match property {
ObjectBindingPropertyKey::Fixed { key, token, .. } => {
self.emit_instruction(Instruction::Insert2)?;
self.emit_instruction(Instruction::Drop)?;
self.emit_instruction(Instruction::Null)?;
let key = self.add_constant(IrConstant::Primitive(Value::String(key.clone())))?;
self.emit_instruction_at(
Instruction::DefineField(key),
source_offset(token.span)?,
)?;
self.emit_instruction(Instruction::Insert2)?;
self.emit_instruction(Instruction::Drop)?;
Ok(false)
}
ObjectBindingPropertyKey::Computed { span } => {
self.emit_instruction_at(Instruction::ToPropKey, source_offset(*span)?)?;
self.emit_instruction(Instruction::Perm3)?;
self.emit_instruction(Instruction::Null)?;
self.emit_instruction(Instruction::DefineArrayEl)?;
self.emit_instruction(Instruction::Perm3)?;
Ok(true)
}
}
}
fn parse_object_binding_property_name(
&mut self,
) -> Result<ObjectBindingPropertyKey<'source>, Error> {
let token = *self.current();
match token.kind {
TokenKind::Identifier(identifier) => {
let key = JsString::try_from_utf8(&self.identifier_text(&identifier))?;
self.advance()?;
let shorthand = (!identifier.escaped_reserved_word
&& !self.is_punctuator(Punctuator::Colon))
.then_some(identifier);
Ok(ObjectBindingPropertyKey::Fixed {
key,
token,
shorthand,
})
}
TokenKind::Keyword(keyword) => {
self.advance()?;
Ok(ObjectBindingPropertyKey::Fixed {
key: JsString::from_static(keyword.as_str()),
token,
shorthand: None,
})
}
TokenKind::String(string) => {
if self.current_ir().strict && string.has_legacy_octal_escape {
return Err(Error::syntax(
"legacy octal escapes are forbidden in strict mode",
token.span,
));
}
self.advance()?;
Ok(ObjectBindingPropertyKey::Fixed {
key: self.decode_string_literal(token.span)?,
token,
shorthand: None,
})
}
TokenKind::Number(number) => {
if self.current_ir().strict
&& matches!(
number.kind,
NumberKind::LegacyOctal | NumberKind::LegacyDecimal
)
{
return Err(Error::syntax(
"legacy leading-zero numeric literals are forbidden in strict mode",
token.span,
));
}
self.advance()?;
let key = parse_number(&number)
.map_err(|message| Error::syntax(message, token.span))?
.to_js_string()?;
Ok(ObjectBindingPropertyKey::Fixed {
key,
token,
shorthand: None,
})
}
TokenKind::Punctuator(Punctuator::LeftBracket) => {
self.advance_expression_start()?;
self.parse_assignment_allow_in()?;
self.anonymous_function_definition = None;
self.expect_punctuator(Punctuator::RightBracket)?;
Ok(ObjectBindingPropertyKey::Computed { span: token.span })
}
TokenKind::PrivateIdentifier(_) => {
Err(Error::syntax("invalid property name", token.span))
}
_ => Err(Error::syntax("invalid property name", token.span)),
}
}
fn nested_binding_pattern_kind(&self, closing: Punctuator) -> Option<BindingPatternKind> {
let (pattern, following) = if self.is_punctuator(Punctuator::LeftBracket) {
(
BindingPatternKind::Array,
self.array_binding_following_token(),
)
} else if self.is_punctuator(Punctuator::LeftBrace) {
(
BindingPatternKind::Object,
self.object_binding_following_token(),
)
} else {
return None;
};
let following = following?;
match following.kind {
TokenKind::Punctuator(Punctuator::Comma | Punctuator::Equal) => Some(pattern),
TokenKind::Punctuator(punctuator) if punctuator == closing => Some(pattern),
_ => None,
}
}
fn emit_nested_object_property_value(
&mut self,
property: &ObjectBindingPropertyKey<'source>,
) -> Result<(), Error> {
match property {
ObjectBindingPropertyKey::Fixed { key, token, .. } => {
let key = self.add_constant(IrConstant::Primitive(Value::String(key.clone())))?;
self.emit_instruction_at(Instruction::GetField2(key), source_offset(token.span)?)?;
}
ObjectBindingPropertyKey::Computed { span } => {
self.emit_instruction_at(Instruction::GetArrayEl2, source_offset(*span)?)?;
}
}
self.anonymous_function_definition = None;
Ok(())
}
fn parse_object_binding_leaf(
&mut self,
property: ObjectBindingPropertyKey<'source>,
declaration: ForAssignmentDeclaration,
is_const: bool,
site: BindingSite,
computed_key_is_canonical: bool,
) -> Result<(), Error> {
let (fixed_key, property_span, shorthand) = match property {
ObjectBindingPropertyKey::Fixed {
key,
token,
shorthand,
} => (
Some(key),
token.span,
shorthand.map(|identifier| (token, identifier)),
),
ObjectBindingPropertyKey::Computed { span } => (None, span, None),
};
let shorthand_binding = shorthand.is_some();
let (token, identifier) = if let Some(binding) = shorthand {
binding
} else {
let token = *self.current();
let TokenKind::Identifier(identifier) = token.kind else {
return Err(Error::syntax("invalid destructuring target", token.span));
};
self.advance()?;
(token, identifier)
};
if identifier.escaped_reserved_word {
return Err(Error::syntax("invalid destructuring target", token.span));
}
self.validate_identifier_reservation(
&identifier,
token.span,
self.current_ir().strict,
IdentifierContext::Variable,
)?;
let invalid_lexical_let = declaration == ForAssignmentDeclaration::Lexical
&& self.identical_name(&identifier, "let");
if invalid_lexical_let {
return Err(self.syntax_here("invalid lexical variable name"));
}
let name = self.intern_identifier(&identifier);
if self.current_ir().strict && matches!(self.names.name(name), "eval" | "arguments") {
let span = if shorthand_binding {
self.current().span
} else {
token.span
};
return Err(Error::syntax("invalid destructuring target", span));
}
match declaration {
ForAssignmentDeclaration::Lexical => self.register_lexical_binding(
name,
token.span,
self.current().span,
is_const,
false,
)?,
ForAssignmentDeclaration::Var => {
if site == BindingSite::Parameter {
let conflict_span = if shorthand_binding {
self.current().span
} else {
token.span
};
self.register_pattern_parameter_binding(name, token.span, conflict_span)?;
} else {
self.register_var_binding(name, token.span, self.current().span)?;
}
}
ForAssignmentDeclaration::Assignment => {
unreachable!("object binding declaration was validated before parsing properties")
}
}
let reference_scope = self.current_ir().context.current_scope;
let parameter_lexical = declaration == ForAssignmentDeclaration::Var
&& site == BindingSite::Parameter
&& self.current_ir().parameter_scope.is_some();
if fixed_key.is_none() && !computed_key_is_canonical {
self.emit_instruction(Instruction::ToPropKey)?;
}
if declaration == ForAssignmentDeclaration::Var && !parameter_lexical {
self.emit_identifier_reference_inherited(
name,
token.span,
reference_scope,
IdentifierReferenceAccess::Prepare,
)?;
if fixed_key.is_some() {
self.emit_instruction(Instruction::Insert2)?;
} else {
self.emit_instruction(Instruction::Insert3)?;
}
self.emit_instruction(Instruction::Drop)?;
}
if let Some(key) = fixed_key {
let key = self.add_constant(IrConstant::Primitive(Value::String(key)))?;
self.emit_instruction_at(Instruction::GetField2(key), source_offset(property_span)?)?;
} else {
self.emit_instruction_at(Instruction::GetArrayEl2, source_offset(property_span)?)?;
}
if declaration == ForAssignmentDeclaration::Var && !parameter_lexical {
self.emit_instruction(Instruction::Perm3)?;
}
if self.consume_punctuator(Punctuator::Equal)? {
self.emit_instruction(Instruction::Dup)?;
self.emit_instruction(Instruction::Undefined)?;
self.emit_instruction(Instruction::StrictEq)?;
let has_value = self.emit_instruction(Instruction::IfFalse(u32::MAX))?;
self.emit_instruction(Instruction::Drop)?;
self.parse_assignment_allow_in()?;
if let Some(definition) = self.take_anonymous_function_definition() {
let name_constant = self.add_constant(IrConstant::Primitive(Value::String(
JsString::try_from_utf8(self.names.name(name))?,
)))?;
self.emit_anonymous_set_name(definition, Instruction::SetName(name_constant))?;
}
let has_value_target = self.current_ir().ops.len();
self.patch_jump(has_value, has_value_target)?;
}
if declaration == ForAssignmentDeclaration::Var && !parameter_lexical {
self.emit_identifier_reference_inherited(
name,
token.span,
reference_scope,
IdentifierReferenceAccess::Set,
)?;
self.emit_instruction(Instruction::Drop)?;
} else {
self.emit_identifier_inherited(
name,
token.span,
reference_scope,
IdentifierAccess::Initialize,
)?;
}
if shorthand_binding {
self.anonymous_function_definition = None;
}
Ok(())
}
fn parse_object_binding_rest(
&mut self,
declaration: ForAssignmentDeclaration,
is_const: bool,
site: BindingSite,
has_rest: bool,
) -> Result<(), Error> {
let rest_span = self.current().span;
self.advance()?;
let token = *self.current();
let TokenKind::Identifier(identifier) = token.kind else {
return Err(Error::syntax("invalid destructuring target", token.span));
};
if identifier.escaped_reserved_word {
return Err(Error::syntax("invalid destructuring target", token.span));
}
let name = self.intern_identifier(&identifier);
self.validate_identifier_reservation(
&identifier,
token.span,
self.current_ir().strict,
IdentifierContext::Variable,
)?;
if self.current_ir().strict && matches!(self.names.name(name), "eval" | "arguments") {
return Err(Error::syntax("invalid destructuring target", token.span));
}
self.advance()?;
if !self.is_punctuator(Punctuator::RightBrace) {
return Err(self.syntax_here("assignment rest property must be last"));
}
if declaration == ForAssignmentDeclaration::Lexical && self.names.name(name) == "let" {
return Err(self.syntax_here("invalid lexical variable name"));
}
match declaration {
ForAssignmentDeclaration::Lexical => self.register_lexical_binding(
name,
token.span,
self.current().span,
is_const,
false,
)?,
ForAssignmentDeclaration::Var => {
if site == BindingSite::Parameter {
self.register_pattern_parameter_binding(name, token.span, token.span)?;
} else {
self.register_var_binding(name, token.span, self.current().span)?;
}
}
ForAssignmentDeclaration::Assignment => {
unreachable!("object binding declaration was validated before parsing rest")
}
}
if !has_rest {
return Err(Error::internal(
"object rest binding was absent from its pattern skip-scan",
));
}
let reference_scope = self.current_ir().context.current_scope;
let parameter_lexical = declaration == ForAssignmentDeclaration::Var
&& site == BindingSite::Parameter
&& self.current_ir().parameter_scope.is_some();
if declaration == ForAssignmentDeclaration::Var && !parameter_lexical {
self.emit_identifier_reference_inherited(
name,
token.span,
reference_scope,
IdentifierReferenceAccess::Prepare,
)?;
}
self.emit_instruction(Instruction::Object)?;
let (source_depth, excluded_depth) =
if declaration == ForAssignmentDeclaration::Var && !parameter_lexical {
(2, 3)
} else {
(1, 2)
};
self.emit_instruction_at(
Instruction::CopyDataPropertiesExcluded {
target_depth: 0,
source_depth,
excluded_depth,
},
source_offset(rest_span)?,
)?;
if declaration == ForAssignmentDeclaration::Var && !parameter_lexical {
self.emit_identifier_reference_inherited(
name,
token.span,
reference_scope,
IdentifierReferenceAccess::Set,
)?;
self.emit_instruction(Instruction::Drop)?;
} else {
self.emit_identifier_inherited(
name,
token.span,
reference_scope,
IdentifierAccess::Initialize,
)?;
}
Ok(())
}
fn parse_nested_array_binding_element(
&mut self,
declaration: ForAssignmentDeclaration,
is_const: bool,
site: BindingSite,
is_rest: bool,
) -> Result<(), Error> {
self.parse_nested_binding_element(
declaration,
is_const,
site,
is_rest,
BindingPatternKind::Array,
)
}
fn parse_nested_object_binding_element(
&mut self,
declaration: ForAssignmentDeclaration,
is_const: bool,
site: BindingSite,
is_rest: bool,
) -> Result<(), Error> {
self.parse_nested_binding_element(
declaration,
is_const,
site,
is_rest,
BindingPatternKind::Object,
)
}
fn parse_nested_binding_element(
&mut self,
declaration: ForAssignmentDeclaration,
is_const: bool,
site: BindingSite,
is_rest: bool,
pattern: BindingPatternKind,
) -> Result<(), Error> {
let value_depth = self.current_ir().context.stack_depth;
if value_depth == 0 {
return Err(Error::internal(
"nested binding pattern has no value to consume",
));
}
if is_rest {
self.parse_binding_pattern(pattern, declaration, is_const, site)?;
if self.is_punctuator(Punctuator::Equal) {
return Err(self.syntax_here("rest element cannot have a default value"));
}
return Ok(());
}
self.emit_instruction(Instruction::Dup)?;
self.emit_instruction(Instruction::Undefined)?;
self.emit_instruction(Instruction::StrictEq)?;
let use_initializer = self.emit_instruction(Instruction::IfTrue(u32::MAX))?;
let assignment_target = self.current_ir().ops.len();
self.parse_binding_pattern(pattern, declaration, is_const, site)?;
self.require_stack_depth(value_depth - 1, "nested binding pattern assignment")?;
if self.consume_punctuator(Punctuator::Equal)? {
let done_jump = self.emit_instruction(Instruction::Goto(u32::MAX))?;
let initializer_target = self.current_ir().ops.len();
self.patch_jump(use_initializer, initializer_target)?;
self.current_ir_mut().context.stack_depth = value_depth;
self.emit_instruction(Instruction::Drop)?;
self.parse_assignment_allow_in()?;
self.anonymous_function_definition = None;
self.require_stack_depth(value_depth, "nested binding pattern initializer")?;
self.emit_instruction(Instruction::Goto(
u32::try_from(assignment_target)
.map_err(|_| Error::new(ErrorKind::JsInternal, "out of memory"))?,
))?;
let done_target = self.current_ir().ops.len();
self.patch_jump(done_jump, done_target)?;
self.current_ir_mut().context.stack_depth = value_depth - 1;
} else {
self.patch_jump(use_initializer, assignment_target)?;
}
Ok(())
}
fn parse_binding_pattern(
&mut self,
pattern: BindingPatternKind,
declaration: ForAssignmentDeclaration,
is_const: bool,
site: BindingSite,
) -> Result<(), Error> {
match pattern {
BindingPatternKind::Array => {
self.parse_array_binding_pattern(declaration, is_const, site)
}
BindingPatternKind::Object => {
self.parse_object_binding_pattern(declaration, is_const, site)
}
}
}
fn emit_array_binding_rest(&mut self, reference_depth: u8) -> Result<(), Error> {
self.emit_array_rest(reference_depth, None)
}
fn emit_array_assignment_rest(
&mut self,
reference_depth: u8,
site: SourceOffset,
) -> Result<(), Error> {
self.emit_array_rest(reference_depth, Some(site))
}
fn emit_array_rest(
&mut self,
reference_depth: u8,
site: Option<SourceOffset>,
) -> Result<(), Error> {
self.emit_instruction(Instruction::ArrayFrom(0))?;
self.emit_instruction(Instruction::PushI32(0))?;
let next_target = self.current_ir().ops.len();
let next = Instruction::ForOfNext(
reference_depth
.checked_add(2)
.ok_or_else(|| Error::new(ErrorKind::JsInternal, "stack overflow"))?,
);
if let Some(site) = site {
self.emit_instruction_at(next, site)?;
} else {
self.emit_instruction(next)?;
}
let done_jump = self.emit_instruction(Instruction::IfTrue(u32::MAX))?;
self.emit_instruction(Instruction::DefineArrayEl)?;
self.emit_instruction(Instruction::Inc)?;
self.emit_instruction(Instruction::Goto(
u32::try_from(next_target)
.map_err(|_| Error::new(ErrorKind::JsInternal, "out of memory"))?,
))?;
let done_target = self.current_ir().ops.len();
self.patch_jump(done_jump, done_target)?;
self.current_ir_mut().context.stack_depth = self
.current_ir()
.context
.stack_depth
.checked_add(1)
.ok_or_else(|| Error::new(ErrorKind::JsInternal, "stack overflow"))?;
self.emit_instruction(Instruction::Drop)?;
self.emit_instruction(Instruction::Drop)?;
Ok(())
}
}