1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-26 03:37:43 +00:00

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 :^).
This commit is contained in:
davidot 2021-11-26 23:45:10 +01:00 committed by Linus Groh
parent 73eb29dabe
commit 045a42cf35
4 changed files with 125 additions and 6 deletions

View file

@ -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 };

View file

@ -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)

View file

@ -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

View file

@ -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();