#[cfg(test)]
mod tests;
use crate::syntax::{
ast::{
node::{
function_contains_super, has_direct_super,
object::{self, MethodDefinition},
AsyncFunctionExpr, AsyncGeneratorExpr, FormalParameterList, FunctionExpr,
GeneratorExpr, Node, Object,
},
Const, Keyword, Punctuator,
},
lexer::{token::Numeric, Error as LexError, TokenKind},
parser::{
expression::{identifiers::IdentifierReference, AssignmentExpression},
function::{FormalParameter, FormalParameters, FunctionBody, UniqueFormalParameters},
AllowAwait, AllowIn, AllowYield, Cursor, ParseError, ParseResult, TokenParser,
},
};
use boa_interner::{Interner, Sym};
use boa_profiler::Profiler;
use std::io::Read;
#[derive(Debug, Clone, Copy)]
pub(super) struct ObjectLiteral {
allow_yield: AllowYield,
allow_await: AllowAwait,
}
impl ObjectLiteral {
pub(super) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self
where
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
}
}
}
impl<R> TokenParser<R> for ObjectLiteral
where
R: Read,
{
type Output = Object;
fn parse(
self,
cursor: &mut Cursor<R>,
interner: &mut Interner,
) -> Result<Self::Output, ParseError> {
let _timer = Profiler::global().start_event("ObjectLiteral", "Parsing");
let mut elements = Vec::new();
loop {
if cursor.next_if(Punctuator::CloseBlock, interner)?.is_some() {
break;
}
elements.push(
PropertyDefinition::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)?,
);
if cursor.next_if(Punctuator::CloseBlock, interner)?.is_some() {
break;
}
if cursor.next_if(Punctuator::Comma, interner)?.is_none() {
let next_token = cursor.next(interner)?.ok_or(ParseError::AbruptEnd)?;
return Err(ParseError::expected(
[",".to_owned(), "}".to_owned()],
next_token.to_string(interner),
next_token.span(),
"object literal",
));
}
}
Ok(Object::from(elements))
}
}
#[derive(Debug, Clone, Copy)]
pub(in crate::syntax::parser) struct PropertyDefinition {
allow_yield: AllowYield,
allow_await: AllowAwait,
}
impl PropertyDefinition {
pub(in crate::syntax::parser) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self
where
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
}
}
}
impl<R> TokenParser<R> for PropertyDefinition
where
R: Read,
{
type Output = object::PropertyDefinition;
fn parse(
self,
cursor: &mut Cursor<R>,
interner: &mut Interner,
) -> Result<Self::Output, ParseError> {
let _timer = Profiler::global().start_event("PropertyDefinition", "Parsing");
match cursor
.peek(1, interner)?
.ok_or(ParseError::AbruptEnd)?
.kind()
{
TokenKind::Punctuator(Punctuator::CloseBlock | Punctuator::Comma) => {
let ident = IdentifierReference::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
return Ok(object::PropertyDefinition::property(ident.sym(), ident));
}
TokenKind::Punctuator(Punctuator::Assign) => {
return CoverInitializedName::new(self.allow_yield, self.allow_await)
.parse(cursor, interner);
}
_ => {}
}
if cursor.next_if(Punctuator::Spread, interner)?.is_some() {
let node = AssignmentExpression::new(None, true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
return Ok(object::PropertyDefinition::SpreadObject(node));
}
let is_keyword = !matches!(
cursor
.peek(1, interner)?
.ok_or(ParseError::AbruptEnd)?
.kind(),
TokenKind::Punctuator(Punctuator::OpenParen | Punctuator::Colon)
);
let token = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?;
match token.kind() {
TokenKind::Keyword((Keyword::Async, true)) if is_keyword => {
return Err(ParseError::general(
"Keyword must not contain escaped characters",
token.span().start(),
));
}
TokenKind::Keyword((Keyword::Async, false)) if is_keyword => {
cursor.next(interner)?.expect("token disappeared");
cursor.peek_expect_no_lineterminator(0, "Async object methods", interner)?;
let token = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?;
let position = token.span().start();
if let TokenKind::Punctuator(Punctuator::Mul) = token.kind() {
let (class_element_name, method) =
AsyncGeneratorMethod::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
if has_direct_super(method.body(), method.parameters()) {
return Err(ParseError::general("invalid super usage", position));
}
let property_name =
if let object::ClassElementName::PropertyName(property_name) =
class_element_name
{
property_name
} else {
return Err(ParseError::general(
"private identifiers not allowed in object literal",
position,
));
};
return Ok(object::PropertyDefinition::method_definition(
method,
property_name,
));
}
let (property_name, method) =
AsyncMethod::new(self.allow_yield, self.allow_await).parse(cursor, interner)?;
if has_direct_super(method.body(), method.parameters()) {
return Err(ParseError::general("invalid super usage", position));
}
return Ok(object::PropertyDefinition::method_definition(
method,
property_name,
));
}
_ => {}
}
if cursor
.peek(0, interner)?
.ok_or(ParseError::AbruptEnd)?
.kind()
== &TokenKind::Punctuator(Punctuator::Mul)
{
let position = cursor
.peek(0, interner)?
.ok_or(ParseError::AbruptEnd)?
.span()
.start();
let (class_element_name, method) =
GeneratorMethod::new(self.allow_yield, self.allow_await).parse(cursor, interner)?;
if has_direct_super(method.body(), method.parameters()) {
return Err(ParseError::general("invalid super usage", position));
}
match class_element_name {
object::ClassElementName::PropertyName(property_name) => {
return Ok(object::PropertyDefinition::method_definition(
method,
property_name,
))
}
object::ClassElementName::PrivateIdentifier(_) => {
return Err(ParseError::general(
"private identifier not allowed in object literal",
position,
))
}
}
}
let mut property_name =
PropertyName::new(self.allow_yield, self.allow_await).parse(cursor, interner)?;
if cursor.next_if(Punctuator::Colon, interner)?.is_some() {
let value = AssignmentExpression::new(None, true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
return Ok(object::PropertyDefinition::property(property_name, value));
}
let ordinary_method = cursor
.peek(0, interner)?
.ok_or(ParseError::AbruptEnd)?
.kind()
== &TokenKind::Punctuator(Punctuator::OpenParen);
match property_name {
object::PropertyName::Literal(str) if str == Sym::GET && !ordinary_method => {
let position = cursor
.peek(0, interner)?
.ok_or(ParseError::AbruptEnd)?
.span()
.start();
property_name = PropertyName::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
cursor.expect(
TokenKind::Punctuator(Punctuator::OpenParen),
"get method definition",
interner,
)?;
cursor.expect(
TokenKind::Punctuator(Punctuator::CloseParen),
"get method definition",
interner,
)?;
cursor.expect(
TokenKind::Punctuator(Punctuator::OpenBlock),
"get method definition",
interner,
)?;
let body = FunctionBody::new(false, false).parse(cursor, interner)?;
cursor.expect(
TokenKind::Punctuator(Punctuator::CloseBlock),
"get method definition",
interner,
)?;
if has_direct_super(&body, &FormalParameterList::default()) {
return Err(ParseError::general("invalid super usage", position));
}
Ok(object::PropertyDefinition::method_definition(
MethodDefinition::Get(FunctionExpr::new(
None,
FormalParameterList::default(),
body,
)),
property_name,
))
}
object::PropertyName::Literal(str) if str == Sym::SET && !ordinary_method => {
property_name = PropertyName::new(self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
let params_start_position = cursor
.expect(
TokenKind::Punctuator(Punctuator::OpenParen),
"set method definition",
interner,
)?
.span()
.end();
let parameters: FormalParameterList = FormalParameter::new(false, false)
.parse(cursor, interner)?
.into();
cursor.expect(
TokenKind::Punctuator(Punctuator::CloseParen),
"set method definition",
interner,
)?;
cursor.expect(
TokenKind::Punctuator(Punctuator::OpenBlock),
"set method definition",
interner,
)?;
let body = FunctionBody::new(false, false).parse(cursor, interner)?;
cursor.expect(
TokenKind::Punctuator(Punctuator::CloseBlock),
"set method definition",
interner,
)?;
if body.strict() && !parameters.is_simple() {
return Err(ParseError::lex(LexError::Syntax(
"Illegal 'use strict' directive in function with non-simple parameter list"
.into(),
params_start_position,
)));
}
if has_direct_super(&body, ¶meters) {
return Err(ParseError::general(
"invalid super usage",
params_start_position,
));
}
Ok(object::PropertyDefinition::method_definition(
MethodDefinition::Set(FunctionExpr::new(None, parameters, body)),
property_name,
))
}
_ => {
let params_start_position = cursor
.expect(
TokenKind::Punctuator(Punctuator::OpenParen),
"method definition",
interner,
)?
.span()
.end();
let params = FormalParameters::new(false, false).parse(cursor, interner)?;
cursor.expect(
TokenKind::Punctuator(Punctuator::CloseParen),
"method definition",
interner,
)?;
if params.has_duplicates() {
return Err(ParseError::lex(LexError::Syntax(
"Duplicate parameter name not allowed in this context".into(),
params_start_position,
)));
}
cursor.expect(
TokenKind::Punctuator(Punctuator::OpenBlock),
"method definition",
interner,
)?;
let body = FunctionBody::new(false, false).parse(cursor, interner)?;
cursor.expect(
TokenKind::Punctuator(Punctuator::CloseBlock),
"method definition",
interner,
)?;
if body.strict() && !params.is_simple() {
return Err(ParseError::lex(LexError::Syntax(
"Illegal 'use strict' directive in function with non-simple parameter list"
.into(),
params_start_position,
)));
}
let lexically_declared_names = body.lexically_declared_names();
for parameter in params.parameters.iter() {
for name in ¶meter.names() {
if lexically_declared_names.contains(&(*name, false)) {
return Err(ParseError::general(
"formal parameter declared in lexically declared names",
params_start_position,
));
}
if lexically_declared_names.contains(&(*name, true)) {
return Err(ParseError::general(
"formal parameter declared in lexically declared names",
params_start_position,
));
}
}
}
if has_direct_super(&body, ¶ms) {
return Err(ParseError::general(
"invalid super usage",
params_start_position,
));
}
Ok(object::PropertyDefinition::method_definition(
MethodDefinition::Ordinary(FunctionExpr::new(None, params, body)),
property_name,
))
}
}
}
}
#[derive(Debug, Clone)]
pub(in crate::syntax::parser) struct PropertyName {
allow_yield: AllowYield,
allow_await: AllowAwait,
}
impl PropertyName {
pub(in crate::syntax::parser) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self
where
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
}
}
}
impl<R> TokenParser<R> for PropertyName
where
R: Read,
{
type Output = object::PropertyName;
fn parse(
self,
cursor: &mut Cursor<R>,
interner: &mut Interner,
) -> Result<Self::Output, ParseError> {
let _timer = Profiler::global().start_event("PropertyName", "Parsing");
let token = cursor.peek(0, interner)?.ok_or(ParseError::AbruptEnd)?;
let name = match token.kind() {
TokenKind::Punctuator(Punctuator::OpenBracket) => {
cursor.next(interner).expect("token disappeared");
let node =
AssignmentExpression::new(None, false, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
cursor.expect(Punctuator::CloseBracket, "expected token ']'", interner)?;
return Ok(node.into());
}
TokenKind::Identifier(name) => object::PropertyName::Literal(*name),
TokenKind::StringLiteral(name) => Node::Const(Const::from(*name)).into(),
TokenKind::NumericLiteral(num) => match num {
Numeric::Rational(num) => Node::Const(Const::from(*num)).into(),
Numeric::Integer(num) => Node::Const(Const::from(*num)).into(),
Numeric::BigInt(num) => Node::Const(Const::from(num.clone())).into(),
},
TokenKind::Keyword((word, _)) => {
Node::Const(Const::from(interner.get_or_intern_static(word.as_str()))).into()
}
TokenKind::NullLiteral => Node::Const(Const::from(Sym::NULL)).into(),
TokenKind::BooleanLiteral(bool) => match bool {
true => Node::Const(Const::from(interner.get_or_intern_static("true"))).into(),
false => Node::Const(Const::from(interner.get_or_intern_static("false"))).into(),
},
_ => return Err(ParseError::AbruptEnd),
};
cursor.next(interner).expect("token disappeared");
Ok(name)
}
}
#[derive(Debug, Clone)]
pub(in crate::syntax::parser) struct ClassElementName {
allow_yield: AllowYield,
allow_await: AllowAwait,
}
impl ClassElementName {
pub(in crate::syntax::parser) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self
where
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
}
}
}
impl<R> TokenParser<R> for ClassElementName
where
R: Read,
{
type Output = object::ClassElementName;
fn parse(
self,
cursor: &mut Cursor<R>,
interner: &mut Interner,
) -> Result<Self::Output, ParseError> {
let _timer = Profiler::global().start_event("ClassElementName", "Parsing");
match cursor
.peek(0, interner)?
.ok_or(ParseError::AbruptEnd)?
.kind()
{
TokenKind::PrivateIdentifier(ident) => {
let ident = *ident;
cursor.next(interner).expect("token disappeared");
Ok(object::ClassElementName::PrivateIdentifier(ident))
}
_ => Ok(object::ClassElementName::PropertyName(
PropertyName::new(self.allow_yield, self.allow_await).parse(cursor, interner)?,
)),
}
}
}
#[derive(Debug, Clone, Copy)]
pub(in crate::syntax::parser) struct Initializer {
name: Option<Sym>,
allow_in: AllowIn,
allow_yield: AllowYield,
allow_await: AllowAwait,
}
impl Initializer {
pub(in crate::syntax::parser) fn new<N, I, Y, A>(
name: N,
allow_in: I,
allow_yield: Y,
allow_await: A,
) -> Self
where
N: Into<Option<Sym>>,
I: Into<AllowIn>,
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
name: name.into(),
allow_in: allow_in.into(),
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
}
}
}
impl<R> TokenParser<R> for Initializer
where
R: Read,
{
type Output = Node;
fn parse(self, cursor: &mut Cursor<R>, interner: &mut Interner) -> ParseResult {
let _timer = Profiler::global().start_event("Initializer", "Parsing");
cursor.expect(Punctuator::Assign, "initializer", interner)?;
AssignmentExpression::new(self.name, self.allow_in, self.allow_yield, self.allow_await)
.parse(cursor, interner)
}
}
#[derive(Debug, Clone, Copy)]
pub(in crate::syntax::parser) struct GeneratorMethod {
allow_yield: AllowYield,
allow_await: AllowAwait,
}
impl GeneratorMethod {
pub(in crate::syntax::parser) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self
where
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
}
}
}
impl<R> TokenParser<R> for GeneratorMethod
where
R: Read,
{
type Output = (object::ClassElementName, MethodDefinition);
fn parse(
self,
cursor: &mut Cursor<R>,
interner: &mut Interner,
) -> Result<Self::Output, ParseError> {
let _timer = Profiler::global().start_event("GeneratorMethod", "Parsing");
cursor.expect(Punctuator::Mul, "generator method definition", interner)?;
let class_element_name =
ClassElementName::new(self.allow_yield, self.allow_await).parse(cursor, interner)?;
let params = UniqueFormalParameters::new(true, false).parse(cursor, interner)?;
let body_start = cursor
.expect(
TokenKind::Punctuator(Punctuator::OpenBlock),
"generator method definition",
interner,
)?
.span()
.start();
let body = FunctionBody::new(true, false).parse(cursor, interner)?;
cursor.expect(
TokenKind::Punctuator(Punctuator::CloseBlock),
"generator method definition",
interner,
)?;
if body.strict() && !params.is_simple() {
return Err(ParseError::lex(LexError::Syntax(
"Illegal 'use strict' directive in function with non-simple parameter list".into(),
body_start,
)));
}
params.name_in_lexically_declared_names(
&body.lexically_declared_names_top_level(),
body_start,
)?;
if function_contains_super(&body, ¶ms) {
return Err(ParseError::lex(LexError::Syntax(
"invalid super usage".into(),
body_start,
)));
}
Ok((
class_element_name,
MethodDefinition::Generator(GeneratorExpr::new(None, params, body)),
))
}
}
#[derive(Debug, Clone, Copy)]
pub(in crate::syntax::parser) struct AsyncGeneratorMethod {
allow_yield: AllowYield,
allow_await: AllowAwait,
}
impl AsyncGeneratorMethod {
pub(in crate::syntax::parser) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self
where
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
}
}
}
impl<R> TokenParser<R> for AsyncGeneratorMethod
where
R: Read,
{
type Output = (object::ClassElementName, MethodDefinition);
fn parse(
self,
cursor: &mut Cursor<R>,
interner: &mut Interner,
) -> Result<Self::Output, ParseError> {
let _timer = Profiler::global().start_event("AsyncGeneratorMethod", "Parsing");
cursor.expect(
Punctuator::Mul,
"async generator method definition",
interner,
)?;
let name =
ClassElementName::new(self.allow_yield, self.allow_await).parse(cursor, interner)?;
let params_start_position = cursor
.peek(0, interner)?
.ok_or(ParseError::AbruptEnd)?
.span()
.start();
let params = UniqueFormalParameters::new(true, true).parse(cursor, interner)?;
if params.contains_yield_expression() {
return Err(ParseError::lex(LexError::Syntax(
"yield expression not allowed in async generator method definition parameters"
.into(),
params_start_position,
)));
}
if params.contains_await_expression() {
return Err(ParseError::lex(LexError::Syntax(
"await expression not allowed in async generator method definition parameters"
.into(),
params_start_position,
)));
}
let body_start = cursor
.expect(
TokenKind::Punctuator(Punctuator::OpenBlock),
"async generator method definition",
interner,
)?
.span()
.start();
let body = FunctionBody::new(true, true).parse(cursor, interner)?;
cursor.expect(
TokenKind::Punctuator(Punctuator::CloseBlock),
"async generator method definition",
interner,
)?;
if body.strict() && !params.is_simple() {
return Err(ParseError::lex(LexError::Syntax(
"Illegal 'use strict' directive in function with non-simple parameter list".into(),
body_start,
)));
}
params.name_in_lexically_declared_names(
&body.lexically_declared_names_top_level(),
body_start,
)?;
if function_contains_super(&body, ¶ms) {
return Err(ParseError::lex(LexError::Syntax(
"invalid super usage".into(),
body_start,
)));
}
Ok((
name,
MethodDefinition::AsyncGenerator(AsyncGeneratorExpr::new(None, params, body)),
))
}
}
#[derive(Debug, Clone, Copy)]
pub(in crate::syntax::parser) struct AsyncMethod {
allow_yield: AllowYield,
allow_await: AllowAwait,
}
impl AsyncMethod {
pub(in crate::syntax::parser) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self
where
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
}
}
}
impl<R> TokenParser<R> for AsyncMethod
where
R: Read,
{
type Output = (object::PropertyName, MethodDefinition);
fn parse(
self,
cursor: &mut Cursor<R>,
interner: &mut Interner,
) -> Result<Self::Output, ParseError> {
let _timer = Profiler::global().start_event("AsyncMethod", "Parsing");
let property_name =
PropertyName::new(self.allow_yield, self.allow_await).parse(cursor, interner)?;
let params = UniqueFormalParameters::new(false, true).parse(cursor, interner)?;
let body_start = cursor
.expect(
TokenKind::Punctuator(Punctuator::OpenBlock),
"async method definition",
interner,
)?
.span()
.start();
let body = FunctionBody::new(true, true).parse(cursor, interner)?;
cursor.expect(
TokenKind::Punctuator(Punctuator::CloseBlock),
"async method definition",
interner,
)?;
if body.strict() && !params.is_simple() {
return Err(ParseError::lex(LexError::Syntax(
"Illegal 'use strict' directive in function with non-simple parameter list".into(),
body_start,
)));
}
params.name_in_lexically_declared_names(
&body.lexically_declared_names_top_level(),
body_start,
)?;
if function_contains_super(&body, ¶ms) {
return Err(ParseError::lex(LexError::Syntax(
"invalid super usage".into(),
body_start,
)));
}
Ok((
property_name,
MethodDefinition::Async(AsyncFunctionExpr::new(None, params, body)),
))
}
}
#[derive(Debug, Clone, Copy)]
pub(in crate::syntax::parser) struct CoverInitializedName {
allow_yield: AllowYield,
allow_await: AllowAwait,
}
impl CoverInitializedName {
pub(in crate::syntax::parser) fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self
where
Y: Into<AllowYield>,
A: Into<AllowAwait>,
{
Self {
allow_yield: allow_yield.into(),
allow_await: allow_await.into(),
}
}
}
impl<R> TokenParser<R> for CoverInitializedName
where
R: Read,
{
type Output = object::PropertyDefinition;
fn parse(
self,
cursor: &mut Cursor<R>,
interner: &mut Interner,
) -> Result<Self::Output, ParseError> {
let _timer = Profiler::global().start_event("CoverInitializedName", "Parsing");
let ident =
IdentifierReference::new(self.allow_yield, self.allow_await).parse(cursor, interner)?;
cursor.expect(Punctuator::Assign, "CoverInitializedName", interner)?;
let expr = AssignmentExpression::new(ident.sym(), true, self.allow_yield, self.allow_await)
.parse(cursor, interner)?;
Ok(object::PropertyDefinition::CoverInitializedName(
ident.sym(),
expr,
))
}
}