use crate::engine::api::error::Error;
use crate::engine::code::bytecode::Instruction;
use crate::engine::code::function::metadata::ClassInitializerKind;
use crate::engine::code::function::metadata::FunctionKind as BytecodeFunctionKind;
use crate::engine::compiler::lexer::LexContext;
use crate::engine::compiler::lexer::LexicalGoal;
use crate::engine::compiler::lexer::Punctuator;
use crate::engine::compiler::lexer::Span;
use crate::engine::compiler::lexer::TemplatePartKind;
use crate::engine::compiler::lexer::TokenKind;
use crate::engine::compiler::model::ir::IrConstant;
use crate::engine::compiler::model::ir::IrOp;
use crate::engine::compiler::model::ir::function::FunctionIrOptions;
use crate::engine::compiler::model::ir::function::FunctionKind;
use crate::engine::compiler::model::ir::function::FunctionSourceInfo;
use crate::engine::compiler::model::ir::function::ParentLink;
use crate::engine::compiler::model::ir::function::SuperCapabilities;
use crate::engine::compiler::parser::builder::FunctionBuilder;
use crate::engine::compiler::parser::context::AnonymousFunctionDefinition;
use crate::engine::compiler::parser::context::ForHeadDelimiter;
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::tokens::for_head_regexp_allowed_after;
use crate::engine::compiler::stack_guard::ParserStackFrame;
use crate::source::SourceOffset;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub(super) enum ArrowHead {
Identifier,
Parenthesized,
}
impl<'source> Parser<'source> {
pub(super) fn parse_arrow_function(&mut self, head: ArrowHead) -> Result<(), Error> {
self.parse_arrow_function_with_kind(
head,
self.current().span,
self.lexer.context(),
BytecodeFunctionKind::Normal,
)
}
pub(super) fn parse_async_arrow_function(&mut self, head: ArrowHead) -> Result<(), Error> {
let function_span = self.current().span;
let parent_context = self.lexer.context();
self.advance()?;
self.parse_arrow_function_with_kind(
head,
function_span,
parent_context,
BytecodeFunctionKind::Async,
)
}
fn parse_arrow_function_with_kind(
&mut self,
head: ArrowHead,
function_span: Span,
parent_context: LexContext,
execution_kind: BytecodeFunctionKind,
) -> Result<(), Error> {
if !matches!(
execution_kind,
BytecodeFunctionKind::Normal | BytecodeFunctionKind::Async
) {
return Err(Error::internal(
"arrow parser received an invalid execution kind",
));
}
let parent = self.current_function;
let parent_strict = self.functions[parent].strict;
let mut parameter_tokens = Vec::new();
let child = self.functions.len();
let parent_scope = self.functions[parent].context.current_scope;
let parent_execution_kind = self.functions[parent].execution_kind;
let parent_is_class_static_block = self.functions[parent].class_initializer_kind
== Some(ClassInitializerKind::StaticBlock);
let super_capabilities = SuperCapabilities {
super_call_allowed: self.functions[parent].super_call_allowed,
super_allowed: self.functions[parent].super_allowed,
};
self.functions.push(FunctionBuilder::new(
Some(ParentLink {
function: parent,
definition_scope: parent_scope,
}),
FunctionKind::Arrow,
FunctionSourceInfo {
span: function_span,
definition: source_offset(function_span)?,
range: None,
},
FunctionIrOptions {
function_name: None,
private_name_binding: false,
class_constructor: false,
derived_class_constructor: false,
defined_argument_count: 0,
has_simple_parameter_list: true,
rest_parameter: None,
parameters: Vec::new(),
strict: parent_strict,
super_capabilities,
},
&mut self.names,
)?);
self.functions[child].execution_kind = execution_kind;
self.functions[child].arguments_forbidden = self.functions[parent].arguments_forbidden;
self.current_function = child;
let mut parameter_context = parent_context;
parameter_context.strict = parent_strict;
parameter_context.generator = matches!(
parent_execution_kind,
BytecodeFunctionKind::Generator | BytecodeFunctionKind::AsyncGenerator
);
parameter_context.async_function = execution_kind == BytecodeFunctionKind::Async
|| matches!(
parent_execution_kind,
BytecodeFunctionKind::Async | BytecodeFunctionKind::AsyncGenerator
)
|| parent_is_class_static_block;
self.set_future_lex_context(parameter_context);
match head {
ArrowHead::Identifier => {
let token = *self.current();
let TokenKind::Identifier(identifier) = token.kind else {
return Err(Error::internal(
"identifier arrow lookahead lost its parameter token",
));
};
self.validate_identifier(
&identifier,
token.span,
false,
IdentifierContext::Argument,
)?;
let parameter = self.intern_identifier(&identifier);
self.register_plain_identifier_parameter(parameter, token.span)?;
parameter_tokens.push((identifier, token.span));
self.advance()?;
}
ArrowHead::Parenthesized => {
let parameter_scan = self.parenthesized_parameter_scan();
if parameter_scan.is_some_and(|scan| scan.has_assignment) {
self.activate_parameter_environment_from_scan(
parameter_scan.and_then(|scan| scan.bound_name_count),
)?;
}
self.expect_punctuator(Punctuator::LeftParen)?;
if !self.consume_punctuator(Punctuator::RightParen)? {
loop {
let is_rest = self.consume_punctuator(Punctuator::Ellipsis)?;
let pattern = match self.current().kind {
TokenKind::Punctuator(Punctuator::LeftBracket) => Some(true),
TokenKind::Punctuator(Punctuator::LeftBrace) => Some(false),
_ => None,
};
if let Some(array_pattern) = pattern {
let span = self.current().span;
self.activate_pattern_parameter_initialization()?;
if is_rest {
let start = self.register_rest_pattern_parameter()?;
let has_initializer = if array_pattern {
self.parse_array_rest_parameter_binding_pattern(start)?
} else {
self.parse_object_rest_parameter_binding_pattern(start)?
};
self.finish_pattern_parameter_length(start, has_initializer)?;
if !self.is_punctuator(Punctuator::RightParen) {
return Err(self.syntax_here("expecting ')'"));
}
self.advance()?;
break;
}
let argument = self.append_pattern_parameter(span)?;
let has_initializer = if array_pattern {
self.parse_array_parameter_binding_pattern(argument)?
} else {
self.parse_object_parameter_binding_pattern(argument)?
};
self.finish_pattern_parameter_length(argument, has_initializer)?;
if self.consume_punctuator(Punctuator::RightParen)? {
break;
}
self.expect_punctuator(Punctuator::Comma)?;
if self.consume_punctuator(Punctuator::RightParen)? {
break;
}
continue;
}
let token = *self.current();
let TokenKind::Identifier(identifier) = token.kind else {
return Err(self.syntax_here("missing formal parameter"));
};
self.validate_identifier(
&identifier,
token.span,
false,
IdentifierContext::Argument,
)?;
let parameter = self.intern_identifier(&identifier);
parameter_tokens.push((identifier, token.span));
self.advance()?;
if is_rest {
self.register_rest_identifier_parameter(parameter, token.span)?;
if !self.is_punctuator(Punctuator::RightParen) {
return Err(self.syntax_here("expecting ')'"));
}
self.advance()?;
break;
}
if self.is_punctuator(Punctuator::Equal) {
self.parse_default_identifier_parameter(parameter, token.span)?;
} else {
self.register_plain_identifier_parameter(parameter, token.span)?;
}
if self.consume_punctuator(Punctuator::RightParen)? {
break;
}
self.expect_punctuator(Punctuator::Comma)?;
if self.consume_punctuator(Punctuator::RightParen)? {
break;
}
}
}
}
}
if !self.is_punctuator(Punctuator::Arrow) || self.current().line_terminator_before {
return Err(self.syntax_here("expecting '=>'"));
}
self.advance_expression_start()?;
let mut child_context = parent_context;
child_context.strict = parent_strict;
child_context.generator = false;
child_context.async_function = execution_kind == BytecodeFunctionKind::Async;
self.relex_current_with_context(child_context)?;
let block_body = self.is_punctuator(Punctuator::LeftBrace);
if block_body {
self.advance()?;
}
let has_use_strict = if block_body {
self.directive_prologue_has_use_strict(parent_strict)?
} else {
false
};
let strict = parent_strict || has_use_strict;
if block_body {
child_context.strict = strict;
self.relex_current_with_context(child_context)?;
}
let has_simple_parameter_list = self.functions[child].has_simple_parameter_list;
if has_use_strict && !has_simple_parameter_list {
return Err(Error::syntax(
"\"use strict\" not allowed in function with default or destructuring parameter",
self.current().span,
));
}
if strict {
let strict_validation_span = self.current().span;
for (identifier, _) in ¶meter_tokens {
self.validate_identifier(
identifier,
strict_validation_span,
true,
IdentifierContext::Argument,
)?;
}
}
let parameters = &self.functions[child].parameter_names;
for (index, parameter) in parameters.iter().enumerate() {
if parameters[..index].contains(parameter) {
return Err(Error::syntax(
"duplicate argument names not allowed in this context",
self.current().span,
));
}
}
self.functions[child].strict = strict;
self.finish_identifier_parameter_environment()?;
self.functions[child].context.in_function_body = true;
let range_end = self.parse_recursion(ParserStackFrame::Arrow, |parser| {
if block_body {
parser
.parse_recursion(ParserStackFrame::ArrowBlockBody, Self::parse_function_body)?;
let closing_brace = parser.current().span;
parser.relex_current_with_context(parent_context)?;
parser.expect_punctuator(Punctuator::RightBrace)?;
Ok(closing_brace.end.byte_offset)
} else {
parser.parse_assignment()?;
parser.emit_instruction(Instruction::Return)?;
let range_end = parser
.previous_end
.unwrap_or(parser.current().span.start.byte_offset);
parser.relex_current_with_context(parent_context)?;
Ok(range_end)
}
})?;
self.functions[child].source.range = Some(
source_offset(function_span)?
..SourceOffset::try_from_usize(range_end)
.map_err(|error| Error::internal(error.to_string()))?,
);
self.functions[child].finish_parsing()?;
self.current_function = parent;
let constant = self.add_constant(IrConstant::Child(child))?;
self.emit(IrOp::MakeClosure(constant))?;
self.anonymous_function_definition = Some(AnonymousFunctionDefinition::Function);
Ok(())
}
fn parenthesized_arrow_ahead(&self, opening: Span) -> bool {
if let Some(arrow) = self.cached_parenthesized_arrow(opening.start.byte_offset) {
return arrow;
}
let mut lexer = self.lexer.clone();
lexer.seek(opening.start);
let Ok(first) = self.probe_token(&mut lexer, LexicalGoal::Div) else {
return false;
};
if !matches!(first.kind, TokenKind::Punctuator(Punctuator::LeftParen)) {
return false;
}
let mut delimiters = self.delimiter_stack();
delimiters.push(ForHeadDelimiter::Parenthesis);
let mut parentheses = vec![opening.start.byte_offset];
let mut closed_parenthesis = None;
let mut goal = LexicalGoal::Div;
let mut regexp_allowed = true;
loop {
let requested_goal = goal;
goal = LexicalGoal::Div;
let Ok(mut token) = self.probe_token(&mut lexer, requested_goal) else {
return false;
};
if let Some(start) = closed_parenthesis.take() {
self.cache_parenthesized_arrow(
start,
!token.line_terminator_before
&& matches!(token.kind, TokenKind::Punctuator(Punctuator::Arrow)),
);
}
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 false;
};
token = regexp;
}
match &token.kind {
TokenKind::Punctuator(Punctuator::LeftParen) => {
if delimiters.len() >= 255 {
return false;
}
delimiters.push(ForHeadDelimiter::Parenthesis);
parentheses.push(token.span.start.byte_offset);
}
TokenKind::Punctuator(Punctuator::LeftBracket) => {
if delimiters.len() >= 255 {
return false;
}
delimiters.push(ForHeadDelimiter::Bracket);
}
TokenKind::Punctuator(Punctuator::LeftBrace) => {
if delimiters.len() >= 255 {
return false;
}
delimiters.push(ForHeadDelimiter::Brace);
}
TokenKind::Punctuator(Punctuator::RightParen) => {
if delimiters.pop() != Some(ForHeadDelimiter::Parenthesis) {
return false;
}
let start = parentheses.pop().expect("balanced parenthesis probe");
if delimiters.is_empty() {
let Ok(arrow) = self.probe_token(&mut lexer, LexicalGoal::Div) else {
return false;
};
let result = !arrow.line_terminator_before
&& matches!(arrow.kind, TokenKind::Punctuator(Punctuator::Arrow));
self.cache_parenthesized_arrow(start, result);
return result;
}
closed_parenthesis = Some(start);
}
TokenKind::Punctuator(Punctuator::RightBracket) => {
if delimiters.pop() != Some(ForHeadDelimiter::Bracket) {
return false;
}
}
TokenKind::Punctuator(Punctuator::RightBrace) => {
if delimiters.last() == Some(&ForHeadDelimiter::Template) {
goal = LexicalGoal::TemplateContinuation;
regexp_allowed = true;
continue;
}
if delimiters.pop() != Some(ForHeadDelimiter::Brace) {
return false;
}
}
TokenKind::Template(part) => match part.kind {
TemplatePartKind::Head => {
if delimiters.len() >= 255 {
return false;
}
delimiters.push(ForHeadDelimiter::Template);
}
TemplatePartKind::Middle => {
if delimiters.last() != Some(&ForHeadDelimiter::Template) {
return false;
}
}
TemplatePartKind::Tail => {
if delimiters.pop() != Some(ForHeadDelimiter::Template) {
return false;
}
}
TemplatePartKind::NoSubstitution => {}
},
TokenKind::Eof => return false,
_ => {}
}
regexp_allowed = for_head_regexp_allowed_after(&token.kind);
}
}
pub(super) fn arrow_head_ahead(&self) -> Option<ArrowHead> {
match &self.current().kind {
TokenKind::Identifier(_) => {
if !self.arrow_may_follow_current() {
return None;
}
let mut lexer = self.lexer.clone();
lexer.seek(self.current().span.end);
let next = self.probe_token(&mut lexer, LexicalGoal::Div).ok()?;
(!next.line_terminator_before
&& matches!(next.kind, TokenKind::Punctuator(Punctuator::Arrow)))
.then_some(ArrowHead::Identifier)
}
TokenKind::Punctuator(Punctuator::LeftParen)
if self.parenthesized_arrow_ahead(self.current().span) =>
{
Some(ArrowHead::Parenthesized)
}
_ => None,
}
}
fn arrow_may_follow_current(&self) -> bool {
let bytes = self.lexer.source().as_bytes();
let mut offset = self.current().span.end.byte_offset;
while matches!(bytes.get(offset), Some(b' ' | b'\t' | 0x0b | 0x0c)) {
offset += 1;
}
match bytes.get(offset) {
Some(b'=') => bytes.get(offset + 1) == Some(&b'>'),
Some(b'/' | b'<' | b'-' | 0x80..=0xff) => true,
_ => false,
}
}
pub(super) fn async_arrow_ahead(&self) -> Option<ArrowHead> {
let TokenKind::Identifier(identifier) = &self.current().kind else {
return None;
};
if !self.is_unescaped_name(identifier, "async") {
return None;
}
let mut lexer = self.lexer.clone();
lexer.seek(self.current().span.end);
let Ok(parameter) = self.probe_token(&mut lexer, LexicalGoal::Div) else {
return None;
};
if parameter.line_terminator_before {
return None;
}
match parameter.kind {
TokenKind::Identifier(identifier) if !identifier.escaped_reserved_word => {
let Ok(arrow) = self.probe_token(&mut lexer, LexicalGoal::Div) else {
return None;
};
(!arrow.line_terminator_before
&& matches!(arrow.kind, TokenKind::Punctuator(Punctuator::Arrow)))
.then_some(ArrowHead::Identifier)
}
TokenKind::Punctuator(Punctuator::LeftParen) => self
.parenthesized_arrow_ahead(parameter.span)
.then_some(ArrowHead::Parenthesized),
_ => None,
}
}
}