diff options
author | davidot <davidot@serenityos.org> | 2021-11-26 23:45:10 +0100 |
---|---|---|
committer | Linus Groh <mail@linusgroh.de> | 2021-11-30 17:05:32 +0000 |
commit | 045a42cf35ae9f58d0a4bcfb9af94118d580d83f (patch) | |
tree | 392e74c525e13e64e3c489696eb442c26596f219 /Userland/Libraries | |
parent | 73eb29dabe855488e9c436323f6959ac423bebe6 (diff) | |
download | serenity-045a42cf35ae9f58d0a4bcfb9af94118d580d83f.zip |
LibJS: Parse dynamic import calls 'import()' and 'import.meta'
For now both just throw when executing but this can be implemented when
modules are implemented :^).
Diffstat (limited to 'Userland/Libraries')
-rw-r--r-- | Userland/Libraries/LibJS/AST.cpp | 28 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/AST.h | 17 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Parser.cpp | 84 | ||||
-rw-r--r-- | Userland/Libraries/LibJS/Parser.h | 2 |
4 files changed, 125 insertions, 6 deletions
diff --git a/Userland/Libraries/LibJS/AST.cpp b/Userland/Libraries/LibJS/AST.cpp index f45de4533b..0b62e40848 100644 --- a/Userland/Libraries/LibJS/AST.cpp +++ b/Userland/Libraries/LibJS/AST.cpp @@ -2880,17 +2880,39 @@ void MetaProperty::dump(int indent) const outln("{} {}", class_name(), name); } -Value MetaProperty::execute(Interpreter& interpreter, GlobalObject&) const +Value MetaProperty::execute(Interpreter& interpreter, GlobalObject& global_object) const { InterpreterNodeScope node_scope { interpreter, *this }; if (m_type == MetaProperty::Type::NewTarget) return interpreter.vm().get_new_target().value_or(js_undefined()); - if (m_type == MetaProperty::Type::ImportMeta) - TODO(); + if (m_type == MetaProperty::Type::ImportMeta) { + interpreter.vm().throw_exception<InternalError>(global_object, ErrorType::NotImplemented, "'import.meta' in modules"); + return {}; + } + VERIFY_NOT_REACHED(); } +void ImportCall::dump(int indent) const +{ + ASTNode::dump(indent); + print_indent(indent); + outln("(Specifier)"); + m_specifier->dump(indent + 1); + if (m_options) { + outln("(Options)"); + m_options->dump(indent + 1); + } +} + +Value ImportCall::execute(Interpreter& interpreter, GlobalObject& global_object) const +{ + InterpreterNodeScope node_scope { interpreter, *this }; + interpreter.vm().throw_exception<InternalError>(global_object, ErrorType::NotImplemented, "'import(...)' in modules"); + return {}; +} + Value StringLiteral::execute(Interpreter& interpreter, GlobalObject&) const { InterpreterNodeScope node_scope { interpreter, *this }; diff --git a/Userland/Libraries/LibJS/AST.h b/Userland/Libraries/LibJS/AST.h index f0e630fd1d..db28e68fbc 100644 --- a/Userland/Libraries/LibJS/AST.h +++ b/Userland/Libraries/LibJS/AST.h @@ -1671,6 +1671,23 @@ private: Type m_type; }; +class ImportCall final : public Expression { +public: + ImportCall(SourceRange source_range, NonnullRefPtr<Expression> specifier, RefPtr<Expression> options) + : Expression(source_range) + , m_specifier(move(specifier)) + , m_options(move(options)) + { + } + + virtual void dump(int indent) const override; + virtual Value execute(Interpreter&, GlobalObject&) const override; + +private: + NonnullRefPtr<Expression> m_specifier; + RefPtr<Expression> m_options; +}; + class ConditionalExpression final : public Expression { public: ConditionalExpression(SourceRange source_range, NonnullRefPtr<Expression> test, NonnullRefPtr<Expression> consequent, NonnullRefPtr<Expression> alternate) diff --git a/Userland/Libraries/LibJS/Parser.cpp b/Userland/Libraries/LibJS/Parser.cpp index 0f1127d6c3..2fd41c36fd 100644 --- a/Userland/Libraries/LibJS/Parser.cpp +++ b/Userland/Libraries/LibJS/Parser.cpp @@ -874,6 +874,63 @@ RefPtr<MetaProperty> Parser::try_parse_new_target_expression() return create_ast_node<MetaProperty>({ m_state.current_token.filename(), rule_start.position(), position() }, MetaProperty::Type::NewTarget); } +RefPtr<MetaProperty> Parser::try_parse_import_meta_expression() +{ + // Optimization which skips the save/load state. + if (next_token().type() != TokenType::Period) + return {}; + + save_state(); + auto rule_start = push_start(); + ArmedScopeGuard state_rollback_guard = [&] { + load_state(); + }; + + consume(TokenType::Import); + consume(TokenType::Period); + if (!match(TokenType::Identifier)) + return {}; + // The string 'meta' cannot have escapes so we check original value. + if (consume().original_value() != "meta"sv) + return {}; + + state_rollback_guard.disarm(); + discard_saved_state(); + return create_ast_node<MetaProperty>({ m_state.current_token.filename(), rule_start.position(), position() }, MetaProperty::Type::ImportMeta); +} + +NonnullRefPtr<ImportCall> Parser::parse_import_call() +{ + auto rule_start = push_start(); + + // We use the extended definition: + // ImportCall[Yield, Await]: + // import(AssignmentExpression[+In, ?Yield, ?Await] ,opt) + // import(AssignmentExpression[+In, ?Yield, ?Await] ,AssignmentExpression[+In, ?Yield, ?Await] ,opt) + // From https://tc39.es/proposal-import-assertions/#sec-evaluate-import-call + + consume(TokenType::Import); + consume(TokenType::ParenOpen); + auto argument = parse_expression(2); + + RefPtr<Expression> options; + if (match(TokenType::Comma)) { + consume(TokenType::Comma); + + if (!match(TokenType::ParenClose)) { + options = parse_expression(2); + + // Second optional comma + if (match(TokenType::Comma)) + consume(TokenType::Comma); + } + } + + consume(TokenType::ParenClose); + + return create_ast_node<ImportCall>({ m_state.current_token.filename(), rule_start.position(), position() }, move(argument), move(options)); +} + NonnullRefPtr<ClassDeclaration> Parser::parse_class_declaration() { auto rule_start = push_start(); @@ -1305,6 +1362,19 @@ Parser::PrimaryExpressionParseResult Parser::parse_primary_expression() } return { parse_new_expression() }; } + case TokenType::Import: { + auto lookahead_token = next_token(); + VERIFY(lookahead_token.type() == TokenType::Period || lookahead_token.type() == TokenType::ParenOpen); + if (lookahead_token.type() == TokenType::ParenOpen) + return { parse_import_call() }; + + if (auto import_meta = try_parse_import_meta_expression()) { + if (m_program_type != Program::Type::Module) + syntax_error("import.meta is only allowed in modules"); + return { import_meta.release_nonnull() }; + } + break; + } case TokenType::Yield: if (!m_state.in_generator_function_context) goto read_as_identifier; @@ -1321,10 +1391,11 @@ Parser::PrimaryExpressionParseResult Parser::parse_primary_expression() default: if (match_identifier_name()) goto read_as_identifier; - expected("primary expression"); - consume(); - return { create_ast_node<ErrorExpression>({ m_state.current_token.filename(), rule_start.position(), position() }) }; + break; } + expected("primary expression"); + consume(); + return { create_ast_node<ErrorExpression>({ m_state.current_token.filename(), rule_start.position(), position() }) }; } NonnullRefPtr<RegExpLiteral> Parser::parse_regexp_literal() @@ -2115,6 +2186,8 @@ NonnullRefPtr<NewExpression> Parser::parse_new_expression() consume(TokenType::New); auto callee = parse_expression(g_operator_precedence.get(TokenType::New), Associativity::Right, { TokenType::ParenOpen, TokenType::QuestionMarkPeriod }); + if (is<ImportCall>(*callee)) + syntax_error("Cannot call new on dynamic import", callee->source_range().start); Vector<CallExpression::Argument> arguments; @@ -3343,6 +3416,11 @@ bool Parser::match(TokenType type) const bool Parser::match_expression() const { auto type = m_state.current_token.type(); + if (type == TokenType::Import) { + auto lookahead_token = next_token(); + return lookahead_token.type() == TokenType::Period || lookahead_token.type() == TokenType::ParenOpen; + } + return type == TokenType::BoolLiteral || type == TokenType::NumericLiteral || type == TokenType::BigIntLiteral diff --git a/Userland/Libraries/LibJS/Parser.h b/Userland/Libraries/LibJS/Parser.h index 7255058b7a..47a3d27af4 100644 --- a/Userland/Libraries/LibJS/Parser.h +++ b/Userland/Libraries/LibJS/Parser.h @@ -122,6 +122,8 @@ public: RefPtr<FunctionExpression> try_parse_arrow_function_expression(bool expect_parens, bool is_async = false); RefPtr<Statement> try_parse_labelled_statement(AllowLabelledFunction allow_function); RefPtr<MetaProperty> try_parse_new_target_expression(); + RefPtr<MetaProperty> try_parse_import_meta_expression(); + NonnullRefPtr<ImportCall> parse_import_call(); Vector<CallExpression::Argument> parse_arguments(); |