|
|
@ -9,14 +9,13 @@ |
|
|
|
|
|
|
|
|
|
|
|
#[cfg(test)] |
|
|
|
#[cfg(test)] |
|
|
|
mod tests; |
|
|
|
mod tests; |
|
|
|
use crate::syntax::ast::node::{Identifier, PropertyName}; |
|
|
|
|
|
|
|
use crate::syntax::lexer::TokenKind; |
|
|
|
|
|
|
|
use crate::{ |
|
|
|
use crate::{ |
|
|
|
syntax::{ |
|
|
|
syntax::{ |
|
|
|
ast::{ |
|
|
|
ast::{ |
|
|
|
node::{self, FunctionExpr, MethodDefinitionKind, Node, Object}, |
|
|
|
node::{self, FunctionExpr, Identifier, MethodDefinitionKind, Node, Object}, |
|
|
|
Punctuator, |
|
|
|
Keyword, Punctuator, |
|
|
|
}, |
|
|
|
}, |
|
|
|
|
|
|
|
lexer::{Error as LexError, Position, TokenKind}, |
|
|
|
parser::{ |
|
|
|
parser::{ |
|
|
|
expression::AssignmentExpression, |
|
|
|
expression::AssignmentExpression, |
|
|
|
function::{FormalParameters, FunctionBody}, |
|
|
|
function::{FormalParameters, FunctionBody}, |
|
|
@ -129,226 +128,331 @@ where |
|
|
|
fn parse(self, cursor: &mut Cursor<R>) -> Result<Self::Output, ParseError> { |
|
|
|
fn parse(self, cursor: &mut Cursor<R>) -> Result<Self::Output, ParseError> { |
|
|
|
let _timer = BoaProfiler::global().start_event("PropertyDefinition", "Parsing"); |
|
|
|
let _timer = BoaProfiler::global().start_event("PropertyDefinition", "Parsing"); |
|
|
|
|
|
|
|
|
|
|
|
if cursor.next_if(Punctuator::Spread)?.is_some() { |
|
|
|
// IdentifierReference[?Yield, ?Await]
|
|
|
|
let node = AssignmentExpression::new(true, self.allow_yield, self.allow_await) |
|
|
|
if let Some(next_token) = cursor.peek(1)? { |
|
|
|
.parse(cursor)?; |
|
|
|
|
|
|
|
return Ok(node::PropertyDefinition::SpreadObject(node)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ComputedPropertyName
|
|
|
|
|
|
|
|
// https://tc39.es/ecma262/#prod-ComputedPropertyName
|
|
|
|
|
|
|
|
if cursor.next_if(Punctuator::OpenBracket)?.is_some() { |
|
|
|
|
|
|
|
let node = AssignmentExpression::new(false, self.allow_yield, self.allow_await) |
|
|
|
|
|
|
|
.parse(cursor)?; |
|
|
|
|
|
|
|
cursor.expect(Punctuator::CloseBracket, "expected token ']'")?; |
|
|
|
|
|
|
|
let next_token = cursor.next()?.ok_or(ParseError::AbruptEnd)?; |
|
|
|
|
|
|
|
match next_token.kind() { |
|
|
|
match next_token.kind() { |
|
|
|
TokenKind::Punctuator(Punctuator::Colon) => { |
|
|
|
TokenKind::Punctuator(Punctuator::CloseBlock) |
|
|
|
let val = AssignmentExpression::new(false, self.allow_yield, self.allow_await) |
|
|
|
| TokenKind::Punctuator(Punctuator::Comma) => { |
|
|
|
.parse(cursor)?; |
|
|
|
let token = cursor.next()?.ok_or(ParseError::AbruptEnd)?; |
|
|
|
return Ok(node::PropertyDefinition::property(node, val)); |
|
|
|
let ident = match token.kind() { |
|
|
|
|
|
|
|
TokenKind::Identifier(ident) => Identifier::from(ident.as_ref()), |
|
|
|
|
|
|
|
TokenKind::Keyword(Keyword::Yield) if self.allow_yield.0 => { |
|
|
|
|
|
|
|
// Early Error: It is a Syntax Error if this production has a [Yield] parameter and StringValue of Identifier is "yield".
|
|
|
|
|
|
|
|
return Err(ParseError::general( |
|
|
|
|
|
|
|
"Unexpected identifier", |
|
|
|
|
|
|
|
token.span().start(), |
|
|
|
|
|
|
|
)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
TokenKind::Keyword(Keyword::Yield) if !self.allow_yield.0 => { |
|
|
|
|
|
|
|
if cursor.strict_mode() { |
|
|
|
|
|
|
|
// Early Error: It is a Syntax Error if the code matched by this production is contained in strict mode code.
|
|
|
|
|
|
|
|
return Err(ParseError::general( |
|
|
|
|
|
|
|
"Unexpected strict mode reserved word", |
|
|
|
|
|
|
|
token.span().start(), |
|
|
|
|
|
|
|
)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
Identifier::from("yield") |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
TokenKind::Keyword(Keyword::Await) if self.allow_await.0 => { |
|
|
|
|
|
|
|
// Early Error: It is a Syntax Error if this production has an [Await] parameter and StringValue of Identifier is "await".
|
|
|
|
|
|
|
|
return Err(ParseError::general( |
|
|
|
|
|
|
|
"Unexpected identifier", |
|
|
|
|
|
|
|
token.span().start(), |
|
|
|
|
|
|
|
)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
TokenKind::Keyword(Keyword::Await) if !self.allow_await.0 => { |
|
|
|
|
|
|
|
if cursor.strict_mode() { |
|
|
|
|
|
|
|
// Early Error: It is a Syntax Error if the code matched by this production is contained in strict mode code.
|
|
|
|
|
|
|
|
return Err(ParseError::general( |
|
|
|
|
|
|
|
"Unexpected strict mode reserved word", |
|
|
|
|
|
|
|
token.span().start(), |
|
|
|
|
|
|
|
)); |
|
|
|
} |
|
|
|
} |
|
|
|
TokenKind::Punctuator(Punctuator::OpenParen) => { |
|
|
|
Identifier::from("yield") |
|
|
|
return MethodDefinition::new(self.allow_yield, self.allow_await, node) |
|
|
|
|
|
|
|
.parse(cursor); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
_ => { |
|
|
|
_ => { |
|
|
|
return Err(ParseError::unexpected( |
|
|
|
return Err(ParseError::unexpected( |
|
|
|
next_token, |
|
|
|
token.clone(), |
|
|
|
"expected AssignmentExpression or MethodDefinition", |
|
|
|
"expected IdentifierReference", |
|
|
|
)) |
|
|
|
)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
return Ok(node::PropertyDefinition::property( |
|
|
|
|
|
|
|
ident.clone().as_ref(), |
|
|
|
|
|
|
|
ident, |
|
|
|
|
|
|
|
)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
_ => {} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ... AssignmentExpression[+In, ?Yield, ?Await]
|
|
|
|
|
|
|
|
if cursor.next_if(Punctuator::Spread)?.is_some() { |
|
|
|
|
|
|
|
let node = AssignmentExpression::new(true, self.allow_yield, self.allow_await) |
|
|
|
|
|
|
|
.parse(cursor)?; |
|
|
|
|
|
|
|
return Ok(node::PropertyDefinition::SpreadObject(node)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Peek for '}' or ',' to indicate shorthand property name
|
|
|
|
// MethodDefinition[?Yield, ?Await] -> GeneratorMethod[?Yield, ?Await]
|
|
|
|
if let Some(next_token) = cursor.peek(1)? { |
|
|
|
if cursor.next_if(Punctuator::Mul)?.is_some() { |
|
|
|
match next_token.kind() { |
|
|
|
let property_name = |
|
|
|
TokenKind::Punctuator(Punctuator::CloseBlock) |
|
|
|
PropertyName::new(self.allow_yield, self.allow_await).parse(cursor)?; |
|
|
|
| TokenKind::Punctuator(Punctuator::Comma) => { |
|
|
|
|
|
|
|
let token = cursor.peek(0)?.ok_or(ParseError::AbruptEnd)?; |
|
|
|
let params_start_position = cursor |
|
|
|
if let TokenKind::Identifier(ident) = token.kind() { |
|
|
|
.expect(Punctuator::OpenParen, "generator method definition")? |
|
|
|
// ident is both the name and value in a shorthand property
|
|
|
|
.span() |
|
|
|
let name = ident.to_string(); |
|
|
|
.start(); |
|
|
|
let value = Identifier::from(ident.to_owned()); |
|
|
|
let params = FormalParameters::new(false, false).parse(cursor)?; |
|
|
|
cursor.next()?.expect("token vanished"); // Consume the token.
|
|
|
|
cursor.expect(Punctuator::CloseParen, "generator method definition")?; |
|
|
|
return Ok(node::PropertyDefinition::property(name, value)); |
|
|
|
|
|
|
|
} else { |
|
|
|
// Early Error: UniqueFormalParameters : FormalParameters
|
|
|
|
// Anything besides an identifier is a syntax error
|
|
|
|
if params.has_duplicates { |
|
|
|
return Err(ParseError::unexpected(token.clone(), "object literal")); |
|
|
|
return Err(ParseError::lex(LexError::Syntax( |
|
|
|
|
|
|
|
"Duplicate parameter name not allowed in this context".into(), |
|
|
|
|
|
|
|
params_start_position, |
|
|
|
|
|
|
|
))); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cursor.expect( |
|
|
|
|
|
|
|
TokenKind::Punctuator(Punctuator::OpenBlock), |
|
|
|
|
|
|
|
"generator method definition", |
|
|
|
|
|
|
|
)?; |
|
|
|
|
|
|
|
let body = FunctionBody::new(true, false).parse(cursor)?; |
|
|
|
|
|
|
|
cursor.expect( |
|
|
|
|
|
|
|
TokenKind::Punctuator(Punctuator::CloseBlock), |
|
|
|
|
|
|
|
"generator method definition", |
|
|
|
|
|
|
|
)?; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Early Error: It is a Syntax Error if FunctionBodyContainsUseStrict of FunctionBody is true
|
|
|
|
|
|
|
|
// and IsSimpleParameterList of UniqueFormalParameters is false.
|
|
|
|
|
|
|
|
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, |
|
|
|
|
|
|
|
))); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Early Error: It is a Syntax Error if any element of the BoundNames of UniqueFormalParameters also
|
|
|
|
|
|
|
|
// occurs in the LexicallyDeclaredNames of GeneratorBody.
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
let lexically_declared_names = body.lexically_declared_names(); |
|
|
|
|
|
|
|
for param in params.parameters.as_ref() { |
|
|
|
|
|
|
|
if lexically_declared_names.contains(param.name()) { |
|
|
|
|
|
|
|
return Err(ParseError::lex(LexError::Syntax( |
|
|
|
|
|
|
|
format!("Redeclaration of formal parameter `{}`", param.name()).into(), |
|
|
|
|
|
|
|
match cursor.peek(0)? { |
|
|
|
|
|
|
|
Some(token) => token.span().end(), |
|
|
|
|
|
|
|
None => Position::new(1, 1), |
|
|
|
|
|
|
|
}, |
|
|
|
|
|
|
|
))); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
_ => {} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return Ok(node::PropertyDefinition::method_definition( |
|
|
|
|
|
|
|
MethodDefinitionKind::Generator, |
|
|
|
|
|
|
|
property_name, |
|
|
|
|
|
|
|
FunctionExpr::new(None, params.parameters, body), |
|
|
|
|
|
|
|
)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
let prop_name = cursor.next()?.ok_or(ParseError::AbruptEnd)?.to_string(); |
|
|
|
let mut property_name = |
|
|
|
|
|
|
|
PropertyName::new(self.allow_yield, self.allow_await).parse(cursor)?; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// PropertyName[?Yield, ?Await] : AssignmentExpression[+In, ?Yield, ?Await]
|
|
|
|
if cursor.next_if(Punctuator::Colon)?.is_some() { |
|
|
|
if cursor.next_if(Punctuator::Colon)?.is_some() { |
|
|
|
let val = AssignmentExpression::new(true, self.allow_yield, self.allow_await) |
|
|
|
let value = AssignmentExpression::new(true, self.allow_yield, self.allow_await) |
|
|
|
.parse(cursor)?; |
|
|
|
.parse(cursor)?; |
|
|
|
return Ok(node::PropertyDefinition::property(prop_name, val)); |
|
|
|
return Ok(node::PropertyDefinition::property(property_name, value)); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// TODO GeneratorMethod
|
|
|
|
let ordinary_method = cursor.peek(0)?.ok_or(ParseError::AbruptEnd)?.kind() |
|
|
|
// https://tc39.es/ecma262/#prod-GeneratorMethod
|
|
|
|
== &TokenKind::Punctuator(Punctuator::OpenParen); |
|
|
|
|
|
|
|
|
|
|
|
if prop_name.as_str() == "async" { |
|
|
|
match property_name { |
|
|
|
// TODO - AsyncMethod.
|
|
|
|
// MethodDefinition[?Yield, ?Await] -> get ClassElementName[?Yield, ?Await] ( ) { FunctionBody[~Yield, ~Await] }
|
|
|
|
// https://tc39.es/ecma262/#prod-AsyncMethod
|
|
|
|
node::PropertyName::Literal(str) if str.as_ref() == "get" && !ordinary_method => { |
|
|
|
|
|
|
|
property_name = |
|
|
|
|
|
|
|
PropertyName::new(self.allow_yield, self.allow_await).parse(cursor)?; |
|
|
|
|
|
|
|
|
|
|
|
// TODO - AsyncGeneratorMethod
|
|
|
|
cursor.expect( |
|
|
|
// https://tc39.es/ecma262/#prod-AsyncGeneratorMethod
|
|
|
|
TokenKind::Punctuator(Punctuator::OpenParen), |
|
|
|
|
|
|
|
"get method definition", |
|
|
|
|
|
|
|
)?; |
|
|
|
|
|
|
|
cursor.expect( |
|
|
|
|
|
|
|
TokenKind::Punctuator(Punctuator::CloseParen), |
|
|
|
|
|
|
|
"get method definition", |
|
|
|
|
|
|
|
)?; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cursor.expect( |
|
|
|
|
|
|
|
TokenKind::Punctuator(Punctuator::OpenBlock), |
|
|
|
|
|
|
|
"get method definition", |
|
|
|
|
|
|
|
)?; |
|
|
|
|
|
|
|
let body = FunctionBody::new(false, false).parse(cursor)?; |
|
|
|
|
|
|
|
cursor.expect( |
|
|
|
|
|
|
|
TokenKind::Punctuator(Punctuator::CloseBlock), |
|
|
|
|
|
|
|
"get method definition", |
|
|
|
|
|
|
|
)?; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Ok(node::PropertyDefinition::method_definition( |
|
|
|
|
|
|
|
MethodDefinitionKind::Get, |
|
|
|
|
|
|
|
property_name, |
|
|
|
|
|
|
|
FunctionExpr::new(None, [], body), |
|
|
|
|
|
|
|
)) |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
// MethodDefinition[?Yield, ?Await] -> set ClassElementName[?Yield, ?Await] ( PropertySetParameterList ) { FunctionBody[~Yield, ~Await] }
|
|
|
|
|
|
|
|
node::PropertyName::Literal(str) if str.as_ref() == "set" && !ordinary_method => { |
|
|
|
|
|
|
|
property_name = |
|
|
|
|
|
|
|
PropertyName::new(self.allow_yield, self.allow_await).parse(cursor)?; |
|
|
|
|
|
|
|
|
|
|
|
if cursor |
|
|
|
let params_start_position = cursor |
|
|
|
.next_if(TokenKind::Punctuator(Punctuator::OpenParen))? |
|
|
|
.expect( |
|
|
|
.is_some() |
|
|
|
TokenKind::Punctuator(Punctuator::OpenParen), |
|
|
|
|| ["get", "set"].contains(&prop_name.as_str()) |
|
|
|
"set method definition", |
|
|
|
{ |
|
|
|
)? |
|
|
|
return MethodDefinition::new(self.allow_yield, self.allow_await, prop_name) |
|
|
|
.span() |
|
|
|
.parse(cursor); |
|
|
|
.end(); |
|
|
|
|
|
|
|
let params = FormalParameters::new(false, false).parse(cursor)?; |
|
|
|
|
|
|
|
cursor.expect( |
|
|
|
|
|
|
|
TokenKind::Punctuator(Punctuator::CloseParen), |
|
|
|
|
|
|
|
"set method definition", |
|
|
|
|
|
|
|
)?; |
|
|
|
|
|
|
|
if params.parameters.len() != 1 { |
|
|
|
|
|
|
|
return Err(ParseError::general( |
|
|
|
|
|
|
|
"set method definition must have one parameter", |
|
|
|
|
|
|
|
params_start_position, |
|
|
|
|
|
|
|
)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cursor.expect( |
|
|
|
|
|
|
|
TokenKind::Punctuator(Punctuator::OpenBlock), |
|
|
|
|
|
|
|
"set method definition", |
|
|
|
|
|
|
|
)?; |
|
|
|
|
|
|
|
let body = FunctionBody::new(false, false).parse(cursor)?; |
|
|
|
|
|
|
|
cursor.expect( |
|
|
|
|
|
|
|
TokenKind::Punctuator(Punctuator::CloseBlock), |
|
|
|
|
|
|
|
"set method definition", |
|
|
|
|
|
|
|
)?; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Early Error: It is a Syntax Error if FunctionBodyContainsUseStrict of FunctionBody is true
|
|
|
|
|
|
|
|
// and IsSimpleParameterList of PropertySetParameterList is false.
|
|
|
|
|
|
|
|
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, |
|
|
|
|
|
|
|
))); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Ok(node::PropertyDefinition::method_definition( |
|
|
|
|
|
|
|
MethodDefinitionKind::Set, |
|
|
|
|
|
|
|
property_name, |
|
|
|
|
|
|
|
FunctionExpr::new(None, params.parameters, body), |
|
|
|
|
|
|
|
)) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
// MethodDefinition[?Yield, ?Await] -> ClassElementName[?Yield, ?Await] ( UniqueFormalParameters[~Yield, ~Await] ) { FunctionBody[~Yield, ~Await] }
|
|
|
|
|
|
|
|
_ => { |
|
|
|
|
|
|
|
let params_start_position = cursor |
|
|
|
|
|
|
|
.expect( |
|
|
|
|
|
|
|
TokenKind::Punctuator(Punctuator::OpenParen), |
|
|
|
|
|
|
|
"method definition", |
|
|
|
|
|
|
|
)? |
|
|
|
|
|
|
|
.span() |
|
|
|
|
|
|
|
.end(); |
|
|
|
|
|
|
|
let params = FormalParameters::new(false, false).parse(cursor)?; |
|
|
|
|
|
|
|
cursor.expect( |
|
|
|
|
|
|
|
TokenKind::Punctuator(Punctuator::CloseParen), |
|
|
|
|
|
|
|
"method definition", |
|
|
|
|
|
|
|
)?; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Early Error: UniqueFormalParameters : FormalParameters
|
|
|
|
|
|
|
|
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", |
|
|
|
|
|
|
|
)?; |
|
|
|
|
|
|
|
let body = FunctionBody::new(false, false).parse(cursor)?; |
|
|
|
|
|
|
|
cursor.expect( |
|
|
|
|
|
|
|
TokenKind::Punctuator(Punctuator::CloseBlock), |
|
|
|
|
|
|
|
"method definition", |
|
|
|
|
|
|
|
)?; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// Early Error: It is a Syntax Error if FunctionBodyContainsUseStrict of FunctionBody is true
|
|
|
|
|
|
|
|
// and IsSimpleParameterList of UniqueFormalParameters is false.
|
|
|
|
|
|
|
|
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 pos = cursor.peek(0)?.ok_or(ParseError::AbruptEnd)?.span().start(); |
|
|
|
Ok(node::PropertyDefinition::method_definition( |
|
|
|
Err(ParseError::general("expected property definition", pos)) |
|
|
|
MethodDefinitionKind::Ordinary, |
|
|
|
|
|
|
|
property_name, |
|
|
|
|
|
|
|
FunctionExpr::new(None, params.parameters, body), |
|
|
|
|
|
|
|
)) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
/// Parses a method definition.
|
|
|
|
/// Parses a property name.
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// More information:
|
|
|
|
/// More information:
|
|
|
|
/// - [ECMAScript specification][spec]
|
|
|
|
/// - [ECMAScript specification][spec]
|
|
|
|
///
|
|
|
|
///
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#prod-MethodDefinition
|
|
|
|
/// [spec]: https://tc39.es/ecma262/#prod-PropertyName
|
|
|
|
#[derive(Debug, Clone)] |
|
|
|
#[derive(Debug, Clone)] |
|
|
|
struct MethodDefinition { |
|
|
|
struct PropertyName { |
|
|
|
allow_yield: AllowYield, |
|
|
|
allow_yield: AllowYield, |
|
|
|
allow_await: AllowAwait, |
|
|
|
allow_await: AllowAwait, |
|
|
|
identifier: PropertyName, |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl MethodDefinition { |
|
|
|
impl PropertyName { |
|
|
|
/// Creates a new `MethodDefinition` parser.
|
|
|
|
/// Creates a new `PropertyName` parser.
|
|
|
|
fn new<Y, A, I>(allow_yield: Y, allow_await: A, identifier: I) -> Self |
|
|
|
fn new<Y, A>(allow_yield: Y, allow_await: A) -> Self |
|
|
|
where |
|
|
|
where |
|
|
|
Y: Into<AllowYield>, |
|
|
|
Y: Into<AllowYield>, |
|
|
|
A: Into<AllowAwait>, |
|
|
|
A: Into<AllowAwait>, |
|
|
|
I: Into<PropertyName>, |
|
|
|
|
|
|
|
{ |
|
|
|
{ |
|
|
|
Self { |
|
|
|
Self { |
|
|
|
allow_yield: allow_yield.into(), |
|
|
|
allow_yield: allow_yield.into(), |
|
|
|
allow_await: allow_await.into(), |
|
|
|
allow_await: allow_await.into(), |
|
|
|
identifier: identifier.into(), |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
impl<R> TokenParser<R> for MethodDefinition |
|
|
|
impl<R> TokenParser<R> for PropertyName |
|
|
|
where |
|
|
|
where |
|
|
|
R: Read, |
|
|
|
R: Read, |
|
|
|
{ |
|
|
|
{ |
|
|
|
type Output = node::PropertyDefinition; |
|
|
|
type Output = node::PropertyName; |
|
|
|
|
|
|
|
|
|
|
|
fn parse(self, cursor: &mut Cursor<R>) -> Result<Self::Output, ParseError> { |
|
|
|
fn parse(self, cursor: &mut Cursor<R>) -> Result<Self::Output, ParseError> { |
|
|
|
let _timer = BoaProfiler::global().start_event("MethodDefinition", "Parsing"); |
|
|
|
let _timer = BoaProfiler::global().start_event("PropertyName", "Parsing"); |
|
|
|
|
|
|
|
|
|
|
|
let (method_kind, prop_name, params) = match self.identifier { |
|
|
|
// ComputedPropertyName[?Yield, ?Await] -> [ AssignmentExpression[+In, ?Yield, ?Await] ]
|
|
|
|
PropertyName::Literal(ident) |
|
|
|
if cursor.next_if(Punctuator::OpenBracket)?.is_some() { |
|
|
|
if ["get", "set"].contains(&ident.as_ref()) |
|
|
|
let node = AssignmentExpression::new(false, self.allow_yield, self.allow_await) |
|
|
|
&& matches!( |
|
|
|
|
|
|
|
cursor.peek(0)?.map(|t| t.kind()), |
|
|
|
|
|
|
|
Some(&TokenKind::Identifier(_)) |
|
|
|
|
|
|
|
| Some(&TokenKind::Keyword(_)) |
|
|
|
|
|
|
|
| Some(&TokenKind::BooleanLiteral(_)) |
|
|
|
|
|
|
|
| Some(&TokenKind::NullLiteral) |
|
|
|
|
|
|
|
| Some(&TokenKind::NumericLiteral(_)) |
|
|
|
|
|
|
|
) => |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
let prop_name = cursor.next()?.ok_or(ParseError::AbruptEnd)?.to_string(); |
|
|
|
|
|
|
|
cursor.expect( |
|
|
|
|
|
|
|
TokenKind::Punctuator(Punctuator::OpenParen), |
|
|
|
|
|
|
|
"property method definition", |
|
|
|
|
|
|
|
)?; |
|
|
|
|
|
|
|
let first_param = cursor.peek(0)?.expect("current token disappeared").clone(); |
|
|
|
|
|
|
|
let params = FormalParameters::new(false, false).parse(cursor)?; |
|
|
|
|
|
|
|
cursor.expect(Punctuator::CloseParen, "method definition")?; |
|
|
|
|
|
|
|
if ident.as_ref() == "get" { |
|
|
|
|
|
|
|
if !params.is_empty() { |
|
|
|
|
|
|
|
return Err(ParseError::unexpected( |
|
|
|
|
|
|
|
first_param, |
|
|
|
|
|
|
|
"getter functions must have no arguments", |
|
|
|
|
|
|
|
)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
(MethodDefinitionKind::Get, prop_name.into(), params) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
if params.len() != 1 { |
|
|
|
|
|
|
|
return Err(ParseError::unexpected( |
|
|
|
|
|
|
|
first_param, |
|
|
|
|
|
|
|
"setter functions must have one argument", |
|
|
|
|
|
|
|
)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
(MethodDefinitionKind::Set, prop_name.into(), params) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
PropertyName::Literal(ident) |
|
|
|
|
|
|
|
if ["get", "set"].contains(&ident.as_ref()) |
|
|
|
|
|
|
|
&& matches!( |
|
|
|
|
|
|
|
cursor.peek(0)?.map(|t| t.kind()), |
|
|
|
|
|
|
|
Some(&TokenKind::Punctuator(Punctuator::OpenBracket)) |
|
|
|
|
|
|
|
) => |
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
|
cursor.expect(Punctuator::OpenBracket, "token vanished")?; |
|
|
|
|
|
|
|
let prop_name = |
|
|
|
|
|
|
|
AssignmentExpression::new(false, self.allow_yield, self.allow_await) |
|
|
|
|
|
|
|
.parse(cursor)?; |
|
|
|
.parse(cursor)?; |
|
|
|
cursor.expect(Punctuator::CloseBracket, "expected token ']'")?; |
|
|
|
cursor.expect(Punctuator::CloseBracket, "expected token ']'")?; |
|
|
|
cursor.expect( |
|
|
|
return Ok(node.into()); |
|
|
|
TokenKind::Punctuator(Punctuator::OpenParen), |
|
|
|
|
|
|
|
"property method definition", |
|
|
|
|
|
|
|
)?; |
|
|
|
|
|
|
|
let first_param = cursor.peek(0)?.expect("current token disappeared").clone(); |
|
|
|
|
|
|
|
let params = FormalParameters::new(false, false).parse(cursor)?; |
|
|
|
|
|
|
|
cursor.expect(Punctuator::CloseParen, "method definition")?; |
|
|
|
|
|
|
|
if ident.as_ref() == "get" { |
|
|
|
|
|
|
|
if !params.is_empty() { |
|
|
|
|
|
|
|
return Err(ParseError::unexpected( |
|
|
|
|
|
|
|
first_param, |
|
|
|
|
|
|
|
"getter functions must have no arguments", |
|
|
|
|
|
|
|
)); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
(MethodDefinitionKind::Get, prop_name.into(), params) |
|
|
|
|
|
|
|
} else { |
|
|
|
|
|
|
|
if params.len() != 1 { |
|
|
|
|
|
|
|
return Err(ParseError::unexpected( |
|
|
|
|
|
|
|
first_param, |
|
|
|
|
|
|
|
"setter functions must have one argument", |
|
|
|
|
|
|
|
)); |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
(MethodDefinitionKind::Set, prop_name.into(), params) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
prop_name => { |
|
|
|
|
|
|
|
let params = FormalParameters::new(false, false).parse(cursor)?; |
|
|
|
|
|
|
|
cursor.expect(Punctuator::CloseParen, "method definition")?; |
|
|
|
|
|
|
|
(MethodDefinitionKind::Ordinary, prop_name, params) |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
cursor.expect( |
|
|
|
// LiteralPropertyName
|
|
|
|
TokenKind::Punctuator(Punctuator::OpenBlock), |
|
|
|
Ok(cursor |
|
|
|
"property method definition", |
|
|
|
.next()? |
|
|
|
)?; |
|
|
|
.ok_or(ParseError::AbruptEnd)? |
|
|
|
let body = FunctionBody::new(false, false).parse(cursor)?; |
|
|
|
.to_string() |
|
|
|
cursor.expect( |
|
|
|
.into()) |
|
|
|
TokenKind::Punctuator(Punctuator::CloseBlock), |
|
|
|
|
|
|
|
"property method definition", |
|
|
|
|
|
|
|
)?; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Ok(node::PropertyDefinition::method_definition( |
|
|
|
|
|
|
|
method_kind, |
|
|
|
|
|
|
|
prop_name, |
|
|
|
|
|
|
|
FunctionExpr::new(None, params, body), |
|
|
|
|
|
|
|
)) |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|