diff --git a/core/ast/src/keyword/mod.rs b/core/ast/src/keyword/mod.rs index f1c36ec1130..a781f77c28b 100644 --- a/core/ast/src/keyword/mod.rs +++ b/core/ast/src/keyword/mod.rs @@ -196,16 +196,6 @@ pub enum Keyword { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Classes/extends Extends, - /// The `false` keyword. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-BooleanLiteral - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean - False, - /// The `finally` keyword. /// /// More information: @@ -308,16 +298,6 @@ pub enum Keyword { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/new New, - /// The `null` keyword. - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-NullLiteral - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/null - Null, - /// The `of` keyword. /// /// More information: @@ -386,16 +366,6 @@ pub enum Keyword { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/throw Throw, - /// The `true` keyword - /// - /// More information: - /// - [ECMAScript reference][spec] - /// - [MDN documentation][mdn] - /// - /// [spec]: https://tc39.es/ecma262/#prod-BooleanLiteral - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean - True, - /// The `try` keyword. /// /// More information: @@ -509,7 +479,6 @@ impl Keyword { Self::Enum => ("enum", utf16!("enum")), Self::Extends => ("extends", utf16!("extends")), Self::Export => ("export", utf16!("export")), - Self::False => ("false", utf16!("false")), Self::Finally => ("finally", utf16!("finally")), Self::For => ("for", utf16!("for")), Self::Function => ("function", utf16!("function")), @@ -519,14 +488,12 @@ impl Keyword { Self::Import => ("import", utf16!("import")), Self::Let => ("let", utf16!("let")), Self::New => ("new", utf16!("new")), - Self::Null => ("null", utf16!("null")), Self::Of => ("of", utf16!("of")), Self::Return => ("return", utf16!("return")), Self::Super => ("super", utf16!("super")), Self::Switch => ("switch", utf16!("switch")), Self::This => ("this", utf16!("this")), Self::Throw => ("throw", utf16!("throw")), - Self::True => ("true", utf16!("true")), Self::Try => ("try", utf16!("try")), Self::TypeOf => ("typeof", utf16!("typeof")), Self::Var => ("var", utf16!("var")), @@ -557,7 +524,6 @@ impl Keyword { Self::Enum => Sym::ENUM, Self::Export => Sym::EXPORT, Self::Extends => Sym::EXTENDS, - Self::False => Sym::FALSE, Self::Finally => Sym::FINALLY, Self::For => Sym::FOR, Self::Function => Sym::FUNCTION, @@ -567,14 +533,12 @@ impl Keyword { Self::Import => Sym::IMPORT, Self::Let => Sym::LET, Self::New => Sym::NEW, - Self::Null => Sym::NULL, Self::Of => Sym::OF, Self::Return => Sym::RETURN, Self::Super => Sym::SUPER, Self::Switch => Sym::SWITCH, Self::This => Sym::THIS, Self::Throw => Sym::THROW, - Self::True => Sym::TRUE, Self::Try => Sym::TRY, Self::TypeOf => Sym::TYPEOF, Self::Var => Sym::VAR, @@ -629,7 +593,6 @@ impl FromStr for Keyword { "enum" => Ok(Self::Enum), "extends" => Ok(Self::Extends), "export" => Ok(Self::Export), - "false" => Ok(Self::False), "finally" => Ok(Self::Finally), "for" => Ok(Self::For), "function" => Ok(Self::Function), @@ -639,14 +602,12 @@ impl FromStr for Keyword { "import" => Ok(Self::Import), "let" => Ok(Self::Let), "new" => Ok(Self::New), - "null" => Ok(Self::Null), "of" => Ok(Self::Of), "return" => Ok(Self::Return), "super" => Ok(Self::Super), "switch" => Ok(Self::Switch), "this" => Ok(Self::This), "throw" => Ok(Self::Throw), - "true" => Ok(Self::True), "try" => Ok(Self::Try), "typeof" => Ok(Self::TypeOf), "var" => Ok(Self::Var), diff --git a/core/ast/src/keyword/tests.rs b/core/ast/src/keyword/tests.rs index 182f685e65a..ec3cc6bfe75 100644 --- a/core/ast/src/keyword/tests.rs +++ b/core/ast/src/keyword/tests.rs @@ -21,7 +21,6 @@ fn all_keywords() -> impl Iterator { Keyword::Enum, Keyword::Export, Keyword::Extends, - Keyword::False, Keyword::Finally, Keyword::For, Keyword::Function, @@ -31,14 +30,12 @@ fn all_keywords() -> impl Iterator { Keyword::Import, Keyword::Let, Keyword::New, - Keyword::Null, Keyword::Of, Keyword::Return, Keyword::Super, Keyword::Switch, Keyword::This, Keyword::Throw, - Keyword::True, Keyword::Try, Keyword::TypeOf, Keyword::Var, @@ -135,10 +132,6 @@ fn as_str() { assert_eq!(k, Keyword::Export); assert_eq!(utf16, utf16!("export")); } - ("false", utf16) => { - assert_eq!(k, Keyword::False); - assert_eq!(utf16, utf16!("false")); - } ("finally", utf16) => { assert_eq!(k, Keyword::Finally); assert_eq!(utf16, utf16!("finally")); @@ -175,10 +168,6 @@ fn as_str() { assert_eq!(k, Keyword::New); assert_eq!(utf16, utf16!("new")); } - ("null", utf16) => { - assert_eq!(k, Keyword::Null); - assert_eq!(utf16, utf16!("null")); - } ("of", utf16) => { assert_eq!(k, Keyword::Of); assert_eq!(utf16, utf16!("of")); @@ -203,10 +192,6 @@ fn as_str() { assert_eq!(k, Keyword::Throw); assert_eq!(utf16, utf16!("throw")); } - ("true", utf16) => { - assert_eq!(k, Keyword::True); - assert_eq!(utf16, utf16!("true")); - } ("try", utf16) => { assert_eq!(k, Keyword::Try); assert_eq!(utf16, utf16!("try")); @@ -260,7 +245,6 @@ fn to_sym() { Sym::ENUM => assert_eq!(k, Keyword::Enum), Sym::EXPORT => assert_eq!(k, Keyword::Export), Sym::EXTENDS => assert_eq!(k, Keyword::Extends), - Sym::FALSE => assert_eq!(k, Keyword::False), Sym::FINALLY => assert_eq!(k, Keyword::Finally), Sym::FOR => assert_eq!(k, Keyword::For), Sym::FUNCTION => assert_eq!(k, Keyword::Function), @@ -270,14 +254,12 @@ fn to_sym() { Sym::IMPORT => assert_eq!(k, Keyword::Import), Sym::LET => assert_eq!(k, Keyword::Let), Sym::NEW => assert_eq!(k, Keyword::New), - Sym::NULL => assert_eq!(k, Keyword::Null), Sym::OF => assert_eq!(k, Keyword::Of), Sym::RETURN => assert_eq!(k, Keyword::Return), Sym::SUPER => assert_eq!(k, Keyword::Super), Sym::SWITCH => assert_eq!(k, Keyword::Switch), Sym::THIS => assert_eq!(k, Keyword::This), Sym::THROW => assert_eq!(k, Keyword::Throw), - Sym::TRUE => assert_eq!(k, Keyword::True), Sym::TRY => assert_eq!(k, Keyword::Try), Sym::TYPEOF => assert_eq!(k, Keyword::TypeOf), Sym::VAR => assert_eq!(k, Keyword::Var), @@ -311,7 +293,6 @@ fn try_into_binary_op() { | Keyword::Enum | Keyword::Export | Keyword::Extends - | Keyword::False | Keyword::Finally | Keyword::For | Keyword::Function @@ -319,14 +300,12 @@ fn try_into_binary_op() { | Keyword::Import | Keyword::Let | Keyword::New - | Keyword::Null | Keyword::Of | Keyword::Return | Keyword::Super | Keyword::Switch | Keyword::This | Keyword::Throw - | Keyword::True | Keyword::Try | Keyword::TypeOf | Keyword::Var diff --git a/core/parser/src/lexer/identifier.rs b/core/parser/src/lexer/identifier.rs index 6251a18b99e..35ac699dbbb 100644 --- a/core/parser/src/lexer/identifier.rs +++ b/core/parser/src/lexer/identifier.rs @@ -4,7 +4,7 @@ use crate::lexer::{ token::ContainsEscapeSequence, Cursor, Error, StringLiteral, Token, TokenKind, Tokenizer, }; use crate::source::ReadChar; -use boa_ast::{Keyword, Position, Span}; +use boa_ast::{Position, Span}; use boa_interner::Interner; use boa_profiler::Profiler; @@ -68,17 +68,17 @@ impl Tokenizer for Identifier { Self::take_identifier_name(cursor, start_pos, self.init)?; let token_kind = match identifier_name.parse() { - Ok(Keyword::True) => { + Ok(keyword) => TokenKind::Keyword((keyword, contains_escaped_chars)), + Err(_) if identifier_name == "true" => { TokenKind::BooleanLiteral((true, ContainsEscapeSequence(contains_escaped_chars))) } - Ok(Keyword::False) => { + Err(_) if identifier_name == "false" => { TokenKind::BooleanLiteral((false, ContainsEscapeSequence(contains_escaped_chars))) } - Ok(Keyword::Null) => { + Err(_) if identifier_name == "null" => { TokenKind::NullLiteral(ContainsEscapeSequence(contains_escaped_chars)) } - Ok(keyword) => TokenKind::Keyword((keyword, contains_escaped_chars)), - _ => TokenKind::IdentifierName(( + Err(_) => TokenKind::IdentifierName(( interner.get_or_intern(identifier_name.as_str()), ContainsEscapeSequence(contains_escaped_chars), )), diff --git a/core/parser/src/parser/statement/declaration/export.rs b/core/parser/src/parser/statement/declaration/export.rs index dcca0ec8419..7f23c9f2489 100644 --- a/core/parser/src/parser/statement/declaration/export.rs +++ b/core/parser/src/parser/statement/declaration/export.rs @@ -317,6 +317,10 @@ where } TokenKind::IdentifierName((ident, _)) => Ok((*ident, false)), TokenKind::Keyword((kw, _)) => Ok((kw.to_sym(), false)), + TokenKind::BooleanLiteral((b, _)) => { + Ok((if *b { Sym::TRUE } else { Sym::FALSE }, false)) + } + TokenKind::NullLiteral(_) => Ok((Sym::NULL, false)), _ => Err(Error::expected( ["identifier".to_owned(), "string literal".to_owned()], tok.to_string(interner), diff --git a/core/parser/src/parser/statement/declaration/tests.rs b/core/parser/src/parser/statement/declaration/tests.rs index 078248147c4..054c45d5be9 100644 --- a/core/parser/src/parser/statement/declaration/tests.rs +++ b/core/parser/src/parser/statement/declaration/tests.rs @@ -1,8 +1,10 @@ -use crate::parser::tests::{check_invalid_script, check_script_parser}; +use crate::parser::tests::{check_invalid_script, check_module_parser, check_script_parser}; use boa_ast::{ - declaration::{LexicalDeclaration, VarDeclaration, Variable}, + declaration::{ + ExportDeclaration, ExportSpecifier, LexicalDeclaration, VarDeclaration, Variable, + }, expression::literal::Literal, - Declaration, Statement, + Declaration, ModuleItem, Statement, }; use boa_interner::{Interner, Sym}; use boa_macros::utf16; @@ -360,3 +362,38 @@ fn lexical_declaration_early_errors() { check_invalid_script("for (let a = 0, a = 0; ; ) {}"); check_invalid_script("for (const a = 0, a = 0; ; ) {}"); } + +/// Checks module exports with reserved keywords +#[test] +fn module_export_reserved() { + let interner = &mut Interner::default(); + let val = interner.get_or_intern_static("val", utf16!("val")); + check_module_parser( + r#" + const val = null; + export { val as null, val as true, val as false }; + "#, + vec![ + ModuleItem::StatementListItem( + Declaration::Lexical(LexicalDeclaration::Const( + vec![Variable::from_identifier( + val.into(), + Some(Literal::Null.into()), + )] + .try_into() + .unwrap(), + )) + .into(), + ), + ModuleItem::ExportDeclaration(ExportDeclaration::List( + vec![ + ExportSpecifier::new(Sym::NULL, val, false), + ExportSpecifier::new(Sym::TRUE, val, false), + ExportSpecifier::new(Sym::FALSE, val, false), + ] + .into(), + )), + ], + interner, + ); +} diff --git a/core/parser/src/parser/tests/mod.rs b/core/parser/src/parser/tests/mod.rs index 5cc9eb2842d..37831df2713 100644 --- a/core/parser/src/parser/tests/mod.rs +++ b/core/parser/src/parser/tests/mod.rs @@ -6,8 +6,7 @@ use std::convert::TryInto; use crate::{Parser, Source}; use boa_ast::{ - declaration::{Declaration, LexicalDeclaration, VarDeclaration, Variable}, - expression::{ + declaration::{Declaration, LexicalDeclaration, VarDeclaration, Variable}, expression::{ access::SimplePropertyAccess, literal::{Literal, ObjectLiteral, PropertyDefinition}, operator::{ @@ -17,14 +16,10 @@ use boa_ast::{ Assign, Binary, Update, }, Call, Identifier, New, Parenthesized, - }, - function::{ + }, function::{ ArrowFunction, FormalParameter, FormalParameterList, FormalParameterListFlags, FunctionBody, FunctionDeclaration, - }, - scope::Scope, - statement::{If, Return}, - Expression, Script, Statement, StatementList, StatementListItem, + }, scope::Scope, statement::{If, Return}, Expression, Module, ModuleItem, ModuleItemList, Script, Statement, StatementList, StatementListItem }; use boa_interner::Interner; use boa_macros::utf16; @@ -46,6 +41,23 @@ where ); } +/// Checks that the given JavaScript string gives the expected expression. +#[track_caller] +pub(super) fn check_module_parser(js: &str, expr: L, interner: &mut Interner) +where + L: Into>, +{ + let mut module = Module::new(ModuleItemList::from(expr.into())); + let scope = Scope::new_global(); + module.analyze_scope(&scope, interner); + assert_eq!( + Parser::new(Source::from_bytes(js)) + .parse_module(&Scope::new_global(), interner) + .expect("failed to parse"), + module, + ); +} + /// Checks that the given javascript string creates a parse error. #[track_caller] pub(super) fn check_invalid_script(js: &str) {