summaryrefslogtreecommitdiff
path: root/Userland/Libraries
diff options
context:
space:
mode:
authordavidot <davidot@serenityos.org>2021-11-26 23:45:10 +0100
committerLinus Groh <mail@linusgroh.de>2021-11-30 17:05:32 +0000
commit045a42cf35ae9f58d0a4bcfb9af94118d580d83f (patch)
tree392e74c525e13e64e3c489696eb442c26596f219 /Userland/Libraries
parent73eb29dabe855488e9c436323f6959ac423bebe6 (diff)
downloadserenity-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.cpp28
-rw-r--r--Userland/Libraries/LibJS/AST.h17
-rw-r--r--Userland/Libraries/LibJS/Parser.cpp84
-rw-r--r--Userland/Libraries/LibJS/Parser.h2
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();