[parser] Parser support for import assertions in dynamic import()

There's a bit more work to do to add support for import assertions for
dynamic import().  This is the first of a series of changes to do that.

This adds parser support for the form of import() that takes import
assertions per https://tc39.es/proposal-import-assertions/#prod-ImportCall

A future change will pass the assertions expression along to
Runtime_DynamicImportCall where the assertions will be unpacked and
filtered per Isolate::supported_import_assertions_.

Bug: v8:10958
Change-Id: Ib1c80d15ac44923d97c5fdfcc4bd732cb9245cf9
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2612038
Reviewed-by: Adam Klein <adamk@chromium.org>
Reviewed-by: Marja Hölttä <marja@chromium.org>
Reviewed-by: Camillo Bruni <cbruni@chromium.org>
Commit-Queue: Dan Clark <daniec@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#71960}
This commit is contained in:
Daniel Clark 2021-01-07 10:36:04 -08:00 committed by Commit Bot
parent 34e7ae615d
commit 2893b9fbd6
7 changed files with 167 additions and 14 deletions

View File

@ -536,7 +536,10 @@ template <class Subclass>
void AstTraversalVisitor<Subclass>::VisitImportCallExpression(
ImportCallExpression* expr) {
PROCESS_EXPRESSION(expr);
RECURSE_EXPRESSION(Visit(expr->argument()));
RECURSE_EXPRESSION(Visit(expr->specifier()));
if (expr->import_assertions()) {
RECURSE_EXPRESSION(Visit(expr->import_assertions()));
}
}
template <class Subclass>

View File

@ -2543,16 +2543,26 @@ class SuperCallReference final : public Expression {
// import(argument).
class ImportCallExpression final : public Expression {
public:
Expression* argument() const { return argument_; }
Expression* specifier() const { return specifier_; }
Expression* import_assertions() const { return import_assertions_; }
private:
friend class AstNodeFactory;
friend Zone;
ImportCallExpression(Expression* argument, int pos)
: Expression(pos, kImportCallExpression), argument_(argument) {}
ImportCallExpression(Expression* specifier, int pos)
: Expression(pos, kImportCallExpression),
specifier_(specifier),
import_assertions_(nullptr) {}
Expression* argument_;
ImportCallExpression(Expression* specifier, Expression* import_assertions,
int pos)
: Expression(pos, kImportCallExpression),
specifier_(specifier),
import_assertions_(import_assertions) {}
Expression* specifier_;
Expression* import_assertions_;
};
// This class is produced when parsing the () in arrow functions without any
@ -3227,8 +3237,15 @@ class AstNodeFactory final {
return zone_->New<TemplateLiteral>(string_parts, substitutions, pos);
}
ImportCallExpression* NewImportCallExpression(Expression* args, int pos) {
return zone_->New<ImportCallExpression>(args, pos);
ImportCallExpression* NewImportCallExpression(Expression* specifier,
int pos) {
return zone_->New<ImportCallExpression>(specifier, pos);
}
ImportCallExpression* NewImportCallExpression(Expression* specifier,
Expression* import_assertions,
int pos) {
return zone_->New<ImportCallExpression>(specifier, import_assertions, pos);
}
InitializeClassMembersStatement* NewInitializeClassMembersStatement(

View File

@ -551,7 +551,10 @@ void CallPrinter::VisitTemplateLiteral(TemplateLiteral* node) {
void CallPrinter::VisitImportCallExpression(ImportCallExpression* node) {
Print("ImportCall(");
Find(node->argument(), true);
Find(node->specifier(), true);
if (node->import_assertions()) {
Find(node->import_assertions(), true);
}
Print(")");
}
@ -1434,7 +1437,10 @@ void AstPrinter::VisitTemplateLiteral(TemplateLiteral* node) {
void AstPrinter::VisitImportCallExpression(ImportCallExpression* node) {
IndentedScope indent(this, "IMPORT-CALL", node->position());
Visit(node->argument());
Visit(node->specifier());
if (node->import_assertions()) {
Visit(node->import_assertions());
}
}
void AstPrinter::VisitThisExpression(ThisExpression* node) {

View File

@ -5621,7 +5621,7 @@ void BytecodeGenerator::VisitEmptyParentheses(EmptyParentheses* expr) {
void BytecodeGenerator::VisitImportCallExpression(ImportCallExpression* expr) {
RegisterList args = register_allocator()->NewRegisterList(2);
VisitForRegisterValue(expr->argument(), args[1]);
VisitForRegisterValue(expr->specifier(), args[1]);
builder()
->MoveRegister(Register::function_closure(), args[0])
.CallRuntime(Runtime::kDynamicImportCall, args);

View File

@ -3574,11 +3574,26 @@ ParserBase<Impl>::ParseImportExpressions() {
MessageTemplate::kImportMissingSpecifier);
return impl()->FailureExpression();
}
AcceptINScope scope(this, true);
ExpressionT arg = ParseAssignmentExpressionCoverGrammar();
Expect(Token::RPAREN);
return factory()->NewImportCallExpression(arg, pos);
AcceptINScope scope(this, true);
ExpressionT specifier = ParseAssignmentExpressionCoverGrammar();
if (FLAG_harmony_import_assertions && Check(Token::COMMA)) {
if (Check(Token::RPAREN)) {
// A trailing comma allowed after the specifier.
return factory()->NewImportCallExpression(specifier, pos);
} else {
ExpressionT import_assertions = ParseAssignmentExpressionCoverGrammar();
Check(Token::COMMA); // A trailing comma is allowed after the import
// assertions.
Expect(Token::RPAREN);
return factory()->NewImportCallExpression(specifier, import_assertions,
pos);
}
}
Expect(Token::RPAREN);
return factory()->NewImportCallExpression(specifier, pos);
}
template <typename Impl>

View File

@ -792,6 +792,12 @@ class PreParserFactory {
return PreParserExpression::Default();
}
PreParserExpression NewImportCallExpression(
const PreParserExpression& specifier,
const PreParserExpression& import_assertions, int pos) {
return PreParserExpression::Default();
}
private:
// For creating VariableProxy objects to track unresolved variables.
AstNodeFactory ast_node_factory_;

View File

@ -4854,6 +4854,35 @@ TEST(ImportExpressionSuccess) {
RunModuleParserSyncTest(context_data, data, kSuccess);
}
TEST(ImportExpressionWithImportAssertionSuccess) {
i::FLAG_harmony_import_assertions = true;
// clang-format off
const char* context_data[][2] = {
{"", ""},
{nullptr, nullptr}
};
const char* data[] = {
"import(x,)",
"import(x,1)",
"import(x,y)",
"import(x,y,)",
"import(x, { 'a': 'b' })",
"import(x, { a: 'b', 'c': 'd' },)",
"import(x, { 'a': { b: 'c' }, 'd': 'e' },)",
"import(x,import(y))",
"import(x,y=z)",
"import(x,[y, z])",
"import(x,undefined)",
nullptr
};
// clang-format on
RunParserSyncTest(context_data, data, kSuccess);
RunModuleParserSyncTest(context_data, data, kSuccess);
}
TEST(ImportExpressionErrors) {
{
// clang-format off
@ -4955,6 +4984,83 @@ TEST(ImportExpressionErrors) {
}
}
TEST(ImportExpressionWithImportAssertionErrors) {
{
i::FLAG_harmony_import_assertions = true;
// clang-format off
const char* context_data[][2] = {
{"", ""},
{"var ", ""},
{"let ", ""},
{"new ", ""},
{nullptr, nullptr}
};
const char* data[] = {
"import(x,,)",
"import(x))",
"import(x,))",
"import(x,())",
"import(x,y,,)",
"import(x,y,z)",
"import(x,y",
"import(x,y,",
"import(x,y(",
nullptr
};
// clang-format on
RunParserSyncTest(context_data, data, kError);
RunModuleParserSyncTest(context_data, data, kError);
}
{
// clang-format off
const char* context_data[][2] = {
{"var ", ""},
{"let ", ""},
{nullptr, nullptr}
};
const char* data[] = {
"import('x',y)",
nullptr
};
// clang-format on
RunParserSyncTest(context_data, data, kError);
RunModuleParserSyncTest(context_data, data, kError);
}
// Import statements as arrow function params and destructuring targets.
{
// clang-format off
const char* context_data[][2] = {
{"(", ") => {}"},
{"(a, ", ") => {}"},
{"(1, ", ") => {}"},
{"let f = ", " => {}"},
{"[", "] = [1];"},
{"{", "} = {'a': 1};"},
{nullptr, nullptr}
};
const char* data[] = {
"import(foo,y)",
"import(1,y)",
"import(y=x,z)",
"import(import(x),y)",
"import(x,y).then()",
nullptr
};
// clang-format on
RunParserSyncTest(context_data, data, kError);
RunModuleParserSyncTest(context_data, data, kError);
}
}
TEST(BasicImportAssertionParsing) {
// clang-format off
const char* kSources[] = {