Bug 1736060 - Part 7: Implement Reflect.parse for import assertions. r=mgaudet,arai
authorJonatan Klemets <jonatan.r.klemets@gmail.com>
Wed, 01 Dec 2021 18:26:11 +0000
changeset 600836 6384f90ac01eb2ffba0255d5a336b631c8eef1d0
parent 600835 07f0b26b54495095213f80a7808cc7ef90c7454d
child 600837 d96803d8f620c24863a78d3c5e016e651b8d937e
push id153996
push usermgaudet@mozilla.com
push dateWed, 01 Dec 2021 18:33:42 +0000
treeherderautoland@3bd18dfc114f [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmgaudet, arai
bugs1736060
milestone96.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1736060 - Part 7: Implement Reflect.parse for import assertions. r=mgaudet,arai Differential Revision: https://phabricator.services.mozilla.com/D126047
js/src/builtin/ReflectParse.cpp
js/src/jit-test/tests/modules/dynamic-import-expression.js
js/src/jit-test/tests/modules/export-declaration.js
js/src/jit-test/tests/modules/import-declaration.js
js/src/jsast.tbl
js/src/tests/non262/reflect-parse/module-export-name.js
--- a/js/src/builtin/ReflectParse.cpp
+++ b/js/src/builtin/ReflectParse.cpp
@@ -595,16 +595,23 @@ class NodeBuilder {
                                      MutableHandleValue dst);
 
   [[nodiscard]] bool tryStatement(HandleValue body, HandleValue handler,
                                   HandleValue finally, TokenPos* pos,
                                   MutableHandleValue dst);
 
   [[nodiscard]] bool debuggerStatement(TokenPos* pos, MutableHandleValue dst);
 
+  [[nodiscard]] bool moduleRequest(HandleValue moduleSpec,
+                                   NodeVector& assertions, TokenPos* pos,
+                                   MutableHandleValue dst);
+
+  [[nodiscard]] bool importAssertion(HandleValue key, HandleValue value,
+                                     TokenPos* pos, MutableHandleValue dst);
+
   [[nodiscard]] bool importDeclaration(NodeVector& elts, HandleValue moduleSpec,
                                        TokenPos* pos, MutableHandleValue dst);
 
   [[nodiscard]] bool importSpecifier(HandleValue importName,
                                      HandleValue bindingName, TokenPos* pos,
                                      MutableHandleValue dst);
 
   [[nodiscard]] bool importNamespaceSpecifier(HandleValue bindingName,
@@ -711,17 +718,17 @@ class NodeBuilder {
   [[nodiscard]] bool thisExpression(TokenPos* pos, MutableHandleValue dst);
 
   [[nodiscard]] bool yieldExpression(HandleValue arg, YieldKind kind,
                                      TokenPos* pos, MutableHandleValue dst);
 
   [[nodiscard]] bool metaProperty(HandleValue meta, HandleValue property,
                                   TokenPos* pos, MutableHandleValue dst);
 
-  [[nodiscard]] bool callImportExpression(HandleValue ident, HandleValue arg,
+  [[nodiscard]] bool callImportExpression(HandleValue ident, NodeVector& args,
                                           TokenPos* pos,
                                           MutableHandleValue dst);
 
   [[nodiscard]] bool super(TokenPos* pos, MutableHandleValue dst);
 
   /*
    * declarations
    */
@@ -1374,30 +1381,56 @@ bool NodeBuilder::yieldExpression(Handle
 
   if (!cb.isNull()) {
     return callback(cb, opt(arg), delegateVal, pos, dst);
   }
   return newNode(AST_YIELD_EXPR, pos, "argument", arg, "delegate", delegateVal,
                  dst);
 }
 
-bool NodeBuilder::importDeclaration(NodeVector& elts, HandleValue moduleSpec,
+bool NodeBuilder::moduleRequest(HandleValue moduleSpec, NodeVector& assertions,
+                                TokenPos* pos, MutableHandleValue dst) {
+  RootedValue array(cx);
+  if (!newArray(assertions, &array)) {
+    return false;
+  }
+
+  RootedValue cb(cx, callbacks[AST_MODULE_REQUEST]);
+  if (!cb.isNull()) {
+    return callback(cb, array, moduleSpec, pos, dst);
+  }
+
+  return newNode(AST_MODULE_REQUEST, pos, "source", moduleSpec, "assertions",
+                 array, dst);
+}
+
+bool NodeBuilder::importAssertion(HandleValue key, HandleValue value,
+                                  TokenPos* pos, MutableHandleValue dst) {
+  RootedValue cb(cx, callbacks[AST_IMPORT_ASSERTION]);
+  if (!cb.isNull()) {
+    return callback(cb, key, value, pos, dst);
+  }
+
+  return newNode(AST_IMPORT_ASSERTION, pos, "key", key, "value", value, dst);
+}
+
+bool NodeBuilder::importDeclaration(NodeVector& elts, HandleValue moduleRequest,
                                     TokenPos* pos, MutableHandleValue dst) {
   RootedValue array(cx);
   if (!newArray(elts, &array)) {
     return false;
   }
 
   RootedValue cb(cx, callbacks[AST_IMPORT_DECL]);
   if (!cb.isNull()) {
-    return callback(cb, array, moduleSpec, pos, dst);
+    return callback(cb, array, moduleRequest, pos, dst);
   }
 
-  return newNode(AST_IMPORT_DECL, pos, "specifiers", array, "source",
-                 moduleSpec, dst);
+  return newNode(AST_IMPORT_DECL, pos, "specifiers", array, "moduleRequest",
+                 moduleRequest, dst);
 }
 
 bool NodeBuilder::importSpecifier(HandleValue importName,
                                   HandleValue bindingName, TokenPos* pos,
                                   MutableHandleValue dst) {
   RootedValue cb(cx, callbacks[AST_IMPORT_SPEC]);
   if (!cb.isNull()) {
     return callback(cb, importName, bindingName, pos, dst);
@@ -1414,32 +1447,32 @@ bool NodeBuilder::importNamespaceSpecifi
   if (!cb.isNull()) {
     return callback(cb, bindingName, pos, dst);
   }
 
   return newNode(AST_IMPORT_NAMESPACE_SPEC, pos, "name", bindingName, dst);
 }
 
 bool NodeBuilder::exportDeclaration(HandleValue decl, NodeVector& elts,
-                                    HandleValue moduleSpec,
+                                    HandleValue moduleRequest,
                                     HandleValue isDefault, TokenPos* pos,
                                     MutableHandleValue dst) {
   RootedValue array(cx, NullValue());
   if (decl.isNull() && !newArray(elts, &array)) {
     return false;
   }
 
   RootedValue cb(cx, callbacks[AST_EXPORT_DECL]);
 
   if (!cb.isNull()) {
-    return callback(cb, decl, array, moduleSpec, pos, dst);
+    return callback(cb, decl, array, moduleRequest, pos, dst);
   }
 
   return newNode(AST_EXPORT_DECL, pos, "declaration", decl, "specifiers", array,
-                 "source", moduleSpec, "isDefault", isDefault, dst);
+                 "moduleRequest", moduleRequest, "isDefault", isDefault, dst);
 }
 
 bool NodeBuilder::exportSpecifier(HandleValue bindingName,
                                   HandleValue exportName, TokenPos* pos,
                                   MutableHandleValue dst) {
   RootedValue cb(cx, callbacks[AST_EXPORT_SPEC]);
   if (!cb.isNull()) {
     return callback(cb, bindingName, exportName, pos, dst);
@@ -1661,24 +1694,29 @@ bool NodeBuilder::metaProperty(HandleVal
   if (!cb.isNull()) {
     return callback(cb, meta, property, pos, dst);
   }
 
   return newNode(AST_METAPROPERTY, pos, "meta", meta, "property", property,
                  dst);
 }
 
-bool NodeBuilder::callImportExpression(HandleValue ident, HandleValue arg,
+bool NodeBuilder::callImportExpression(HandleValue ident, NodeVector& args,
                                        TokenPos* pos, MutableHandleValue dst) {
+  RootedValue array(cx);
+  if (!newArray(args, &array)) {
+    return false;
+  }
+
   RootedValue cb(cx, callbacks[AST_CALL_IMPORT]);
   if (!cb.isNull()) {
-    return callback(cb, arg, pos, dst);
+    return callback(cb, ident, array, pos, dst);
   }
 
-  return newNode(AST_CALL_IMPORT, pos, "ident", ident, "arg", arg, dst);
+  return newNode(AST_CALL_IMPORT, pos, "ident", ident, "arguments", array, dst);
 }
 
 bool NodeBuilder::super(TokenPos* pos, MutableHandleValue dst) {
   RootedValue cb(cx, callbacks[AST_SUPER]);
   if (!cb.isNull()) {
     return callback(cb, pos, dst);
   }
 
@@ -1721,16 +1759,17 @@ class ASTSerializer {
   bool variableDeclarator(ParseNode* pn, MutableHandleValue dst);
   bool importDeclaration(BinaryNode* importNode, MutableHandleValue dst);
   bool importSpecifier(BinaryNode* importSpec, MutableHandleValue dst);
   bool importNamespaceSpecifier(UnaryNode* importSpec, MutableHandleValue dst);
   bool exportDeclaration(ParseNode* exportNode, MutableHandleValue dst);
   bool exportSpecifier(BinaryNode* exportSpec, MutableHandleValue dst);
   bool exportNamespaceSpecifier(UnaryNode* exportSpec, MutableHandleValue dst);
   bool classDefinition(ClassNode* pn, bool expr, MutableHandleValue dst);
+  bool importAssertions(ListNode* assertionList, NodeVector& assertions);
 
   bool optStatement(ParseNode* pn, MutableHandleValue dst) {
     if (!pn) {
       dst.setMagic(JS_SERIALIZE_NO_NODE);
       return true;
     }
     return statement(pn, dst);
   }
@@ -2082,19 +2121,25 @@ bool ASTSerializer::variableDeclarator(P
 
 bool ASTSerializer::importDeclaration(BinaryNode* importNode,
                                       MutableHandleValue dst) {
   MOZ_ASSERT(importNode->isKind(ParseNodeKind::ImportDecl));
 
   ListNode* specList = &importNode->left()->as<ListNode>();
   MOZ_ASSERT(specList->isKind(ParseNodeKind::ImportSpecList));
 
-  ParseNode* moduleSpecNode = importNode->right();
+  auto* moduleRequest = &importNode->right()->as<BinaryNode>();
+  MOZ_ASSERT(moduleRequest->isKind(ParseNodeKind::ImportModuleRequest));
+
+  ParseNode* moduleSpecNode = moduleRequest->left();
   MOZ_ASSERT(moduleSpecNode->isKind(ParseNodeKind::StringExpr));
 
+  auto* assertionList = &moduleRequest->right()->as<ListNode>();
+  MOZ_ASSERT(assertionList->isKind(ParseNodeKind::ImportAssertionList));
+
   NodeVector elts(cx);
   if (!elts.reserve(specList->count())) {
     return false;
   }
 
   for (ParseNode* item : specList->contents()) {
     RootedValue elt(cx);
     if (item->is<UnaryNode>()) {
@@ -2107,18 +2152,33 @@ bool ASTSerializer::importDeclaration(Bi
       if (!importSpecifier(spec, &elt)) {
         return false;
       }
     }
     elts.infallibleAppend(elt);
   }
 
   RootedValue moduleSpec(cx);
-  return literal(moduleSpecNode, &moduleSpec) &&
-         builder.importDeclaration(elts, moduleSpec, &importNode->pn_pos, dst);
+  if (!literal(moduleSpecNode, &moduleSpec)) {
+    return false;
+  }
+
+  NodeVector assertions(cx);
+  if (!importAssertions(assertionList, assertions)) {
+    return false;
+  }
+
+  RootedValue moduleRequestValue(cx);
+  if (!builder.moduleRequest(moduleSpec, assertions, &importNode->pn_pos,
+                             &moduleRequestValue)) {
+    return false;
+  }
+
+  return builder.importDeclaration(elts, moduleRequestValue,
+                                   &importNode->pn_pos, dst);
 }
 
 bool ASTSerializer::importSpecifier(BinaryNode* importSpec,
                                     MutableHandleValue dst) {
   MOZ_ASSERT(importSpec->isKind(ParseNodeKind::ImportSpec));
   NameNode* importNameNode = &importSpec->left()->as<NameNode>();
   NameNode* bindingNameNode = &importSpec->right()->as<NameNode>();
 
@@ -2143,19 +2203,19 @@ bool ASTSerializer::importNamespaceSpeci
 
 bool ASTSerializer::exportDeclaration(ParseNode* exportNode,
                                       MutableHandleValue dst) {
   MOZ_ASSERT(exportNode->isKind(ParseNodeKind::ExportStmt) ||
              exportNode->isKind(ParseNodeKind::ExportFromStmt) ||
              exportNode->isKind(ParseNodeKind::ExportDefaultStmt));
   MOZ_ASSERT_IF(exportNode->isKind(ParseNodeKind::ExportStmt),
                 exportNode->is<UnaryNode>());
-  MOZ_ASSERT_IF(
-      exportNode->isKind(ParseNodeKind::ExportFromStmt),
-      exportNode->as<BinaryNode>().right()->isKind(ParseNodeKind::StringExpr));
+  MOZ_ASSERT_IF(exportNode->isKind(ParseNodeKind::ExportFromStmt),
+                exportNode->as<BinaryNode>().right()->isKind(
+                    ParseNodeKind::ImportModuleRequest));
 
   RootedValue decl(cx, NullValue());
   NodeVector elts(cx);
 
   ParseNode* kid = exportNode->isKind(ParseNodeKind::ExportStmt)
                        ? exportNode->as<UnaryNode>().kid()
                        : exportNode->as<BinaryNode>().left();
   switch (ParseNodeKind kind = kid->getKind()) {
@@ -2210,28 +2270,44 @@ bool ASTSerializer::exportDeclaration(Pa
     default:
       if (!expression(kid, &decl)) {
         return false;
       }
       break;
   }
 
   RootedValue moduleSpec(cx, NullValue());
+  RootedValue moduleRequestValue(cx, NullValue());
   if (exportNode->isKind(ParseNodeKind::ExportFromStmt)) {
-    if (!literal(exportNode->as<BinaryNode>().right(), &moduleSpec)) {
+    ParseNode* moduleRequest = exportNode->as<BinaryNode>().right();
+    if (!literal(moduleRequest->as<BinaryNode>().left(), &moduleSpec)) {
+      return false;
+    }
+
+    auto* assertionList =
+        &moduleRequest->as<BinaryNode>().right()->as<ListNode>();
+    MOZ_ASSERT(assertionList->isKind(ParseNodeKind::ImportAssertionList));
+
+    NodeVector assertions(cx);
+    if (!importAssertions(assertionList, assertions)) {
+      return false;
+    }
+
+    if (!builder.moduleRequest(moduleSpec, assertions, &exportNode->pn_pos,
+                               &moduleRequestValue)) {
       return false;
     }
   }
 
   RootedValue isDefault(cx, BooleanValue(false));
   if (exportNode->isKind(ParseNodeKind::ExportDefaultStmt)) {
     isDefault.setBoolean(true);
   }
 
-  return builder.exportDeclaration(decl, elts, moduleSpec, isDefault,
+  return builder.exportDeclaration(decl, elts, moduleRequestValue, isDefault,
                                    &exportNode->pn_pos, dst);
 }
 
 bool ASTSerializer::exportSpecifier(BinaryNode* exportSpec,
                                     MutableHandleValue dst) {
   MOZ_ASSERT(exportSpec->isKind(ParseNodeKind::ExportSpec));
   NameNode* bindingNameNode = &exportSpec->left()->as<NameNode>();
   NameNode* exportNameNode = &exportSpec->right()->as<NameNode>();
@@ -2249,16 +2325,49 @@ bool ASTSerializer::exportNamespaceSpeci
   MOZ_ASSERT(exportSpec->isKind(ParseNodeKind::ExportNamespaceSpec));
   NameNode* exportNameNode = &exportSpec->kid()->as<NameNode>();
 
   RootedValue exportName(cx);
   return identifierOrLiteral(exportNameNode, &exportName) &&
          builder.exportNamespaceSpecifier(exportName, &exportSpec->pn_pos, dst);
 }
 
+bool ASTSerializer::importAssertions(ListNode* assertionList,
+                                     NodeVector& assertions) {
+  for (ParseNode* assertionItem : assertionList->contents()) {
+    BinaryNode* assertionNode = &assertionItem->as<BinaryNode>();
+    MOZ_ASSERT(assertionNode->isKind(ParseNodeKind::ImportAssertion));
+
+    NameNode* keyNameNode = &assertionNode->left()->as<NameNode>();
+    NameNode* valueNameNode = &assertionNode->right()->as<NameNode>();
+
+    RootedValue key(cx);
+    if (!identifierOrLiteral(keyNameNode, &key)) {
+      return false;
+    }
+
+    RootedValue value(cx);
+    if (!literal(valueNameNode, &value)) {
+      return false;
+    }
+
+    RootedValue assertion(cx);
+    if (!builder.importAssertion(key, value, &assertionNode->pn_pos,
+                                 &assertion)) {
+      return false;
+    }
+
+    if (!assertions.append(assertion)) {
+      return false;
+    }
+  }
+
+  return true;
+}
+
 bool ASTSerializer::switchCase(CaseClause* caseClause, MutableHandleValue dst) {
   MOZ_ASSERT_IF(
       caseClause->caseExpression(),
       caseClause->pn_pos.encloses(caseClause->caseExpression()->pn_pos));
   MOZ_ASSERT(caseClause->pn_pos.encloses(caseClause->statementList()->pn_pos));
 
   NodeVector stmts(cx);
   RootedValue expr(cx);
@@ -3307,26 +3416,53 @@ bool ASTSerializer::expression(ParseNode
     }
 
     case ParseNodeKind::CallImportExpr: {
       BinaryNode* node = &pn->as<BinaryNode>();
       ParseNode* identNode = node->left();
       MOZ_ASSERT(identNode->isKind(ParseNodeKind::PosHolder));
       MOZ_ASSERT(identNode->pn_pos.encloses(identNode->pn_pos));
 
-      ParseNode* argNode = node->right();
+      ParseNode* specNode = node->right();
+      MOZ_ASSERT(specNode->is<BinaryNode>());
+      MOZ_ASSERT(specNode->isKind(ParseNodeKind::CallImportSpec));
+
+      ParseNode* argNode = specNode->as<BinaryNode>().left();
       MOZ_ASSERT(node->pn_pos.encloses(argNode->pn_pos));
 
+      ParseNode* optionsArgNode = specNode->as<BinaryNode>().right();
+      MOZ_ASSERT(node->pn_pos.encloses(optionsArgNode->pn_pos));
+
       RootedValue ident(cx);
+      HandlePropertyName name = cx->names().import;
+      if (!identifier(name, &identNode->pn_pos, &ident)) {
+        return false;
+      }
+
+      NodeVector args(cx);
+
       RootedValue arg(cx);
-
-      HandlePropertyName name = cx->names().import;
-      return identifier(name, &identNode->pn_pos, &ident) &&
-             expression(argNode, &arg) &&
-             builder.callImportExpression(ident, arg, &pn->pn_pos, dst);
+      if (!expression(argNode, &arg)) {
+        return false;
+      }
+      if (!args.append(arg)) {
+        return false;
+      }
+
+      if (!optionsArgNode->isKind(ParseNodeKind::PosHolder)) {
+        RootedValue optionsArg(cx);
+        if (!expression(optionsArgNode, &optionsArg)) {
+          return false;
+        }
+        if (!args.append(optionsArg)) {
+          return false;
+        }
+      }
+
+      return builder.callImportExpression(ident, args, &pn->pn_pos, dst);
     }
 
     case ParseNodeKind::SetThis: {
       // SETTHIS is used to assign the result of a super() call to |this|.
       // It's not part of the original AST, so just forward to the call.
       BinaryNode* node = &pn->as<BinaryNode>();
       MOZ_ASSERT(node->left()->isKind(ParseNodeKind::Name));
       return expression(node->right(), dst);
--- a/js/src/jit-test/tests/modules/dynamic-import-expression.js
+++ b/js/src/jit-test/tests/modules/dynamic-import-expression.js
@@ -16,20 +16,40 @@ assignmentExpression = (left, operator, 
     operator: operator,
     left: left,
     right: right
 });
 ident = (name) => Pattern({
     type: "Identifier",
     name: name
 });
-importCall = (ident, singleArg) => Pattern({
+importCall = (ident, args) => Pattern({
     type: "CallImport",
     ident: ident,
-    arg: singleArg
+    arguments: args
+});
+
+objExpr = (elts) => Pattern({
+    type: "ObjectExpression",
+    properties: elts
+});
+property = (key, value) => Pattern({
+    type: "Property",
+    kind: "init",
+    key: key,
+    value: value,
+});
+lit = (val) => Pattern({
+    type: "Literal",
+    value: val
+});
+callExpression = (callee, args) => Pattern({
+    type: "CallExpression",
+    callee: callee,
+    arguments: args
 });
 
 function parseAsClassicScript(source)
 {
     return Reflect.parse(source);
 }
 
 function parseAsModuleScript(source)
@@ -37,33 +57,141 @@ function parseAsModuleScript(source)
     return Reflect.parse(source, {target: "module"});
 }
 
 for (let parse of [parseAsModuleScript, parseAsClassicScript]) {
     program([
         expressionStatement(
             importCall(
                 ident("import"),
-                ident("foo")
+                [
+                    ident("foo")
+                ]
             )
         )
     ]).assert(parse("import(foo);"));
 
     program([
         expressionStatement(
             assignmentExpression(
                 ident("x"),
                 "=",
                 importCall(
                     ident("import"),
-                    ident("foo")
+                    [
+                        ident("foo")
+                    ]
                 )
             )
         )
     ]).assert(parse("x = import(foo);"));
+
+
+    if (getRealmConfiguration()['importAssertions']) {
+        program([
+            expressionStatement(
+                importCall(
+                    ident("import"),
+                    [
+                        ident("foo"),
+                        objExpr([])
+                    ]
+                )
+            )
+        ]).assert(parse("import(foo, {});"));
+
+        program([
+            expressionStatement(
+                importCall(
+                    ident("import"),
+
+                    [
+                        ident("foo"),
+                        objExpr([
+                            property(
+                                ident("assert"),
+                                objExpr([]
+                            ))
+                        ])
+                    ]
+
+                )
+            )
+        ]).assert(parse("import(foo, { assert: {} });"));
+
+        program([
+            expressionStatement(
+                importCall(
+                    ident("import"),
+                    [
+                        ident("foo"),
+                        objExpr([
+                            property(
+                                ident("assert"),
+                                objExpr([
+                                    property(
+                                        ident("type"),
+                                        lit('json')
+                                    )
+                                ]
+                            ))
+                        ])
+                    ]
+                )
+            )
+        ]).assert(parse("import(foo, { assert: { type: 'json' } });"));
+
+        program([
+            expressionStatement(
+                importCall(
+                    ident("import"),
+                    [
+                        ident("foo"),
+                        objExpr([
+                            property(
+                                ident("assert"),
+                                objExpr([
+                                    property(
+                                        ident("type"),
+                                        lit('json')
+                                    ),
+                                    property(
+                                        ident("foo"),
+                                        lit('bar')
+                                    )
+                                ]
+                            ))
+                        ])
+                    ]
+                )
+            )
+        ]).assert(parse("import(foo, { assert: { type: 'json', foo: 'bar' } });"));
+
+        program([
+            expressionStatement(
+                importCall(
+                    ident("import"),
+                    [
+                        ident("foo"),
+                        objExpr([
+                            property(
+                                ident("assert"),
+                                objExpr([
+                                    property(
+                                        ident("type"),
+                                        callExpression(ident('getType'), [])
+                                    )
+                                ]
+                            ))
+                        ])
+                    ]
+                )
+            )
+        ]).assert(parse("import(foo, { assert: { type: getType() } });"));
+    }
 }
 
 function assertParseThrowsSyntaxError(source)
 {
     assertThrowsInstanceOf(() => parseAsClassicScript(source), SyntaxError);
     assertThrowsInstanceOf(() => parseAsModuleScript(source), SyntaxError);
 }
 
--- a/js/src/jit-test/tests/modules/export-declaration.js
+++ b/js/src/jit-test/tests/modules/export-declaration.js
@@ -2,23 +2,33 @@ load(libdir + "match.js");
 load(libdir + "asserts.js");
 
 var { Pattern, MatchError } = Match;
 
 program = (elts) => Pattern({
     type: "Program",
     body: elts
 })
-exportDeclaration = (declaration, specifiers, source, isDefault) => Pattern({
+exportDeclaration = (declaration, specifiers, moduleRequest, isDefault) => Pattern({
     type: "ExportDeclaration",
     declaration: declaration,
     specifiers: specifiers,
-    source: source,
+    moduleRequest: moduleRequest,
     isDefault: isDefault
 });
+moduleRequest = (specifier, assertions) => Pattern({
+    type: "ModuleRequest",
+    source: specifier,
+    assertions: assertions
+});
+importAssertion = (key, value) => Pattern({
+    type: "ImportAssertion",
+    key: key,
+    value : value
+});
 exportSpecifier = (id, name) => Pattern({
     type: "ExportSpecifier",
     id: id,
     name: name
 });
 exportBatchSpecifier = () => Pattern({
     type: "ExportBatchSpecifier"
 });
@@ -217,28 +227,34 @@ program([
     exportDeclaration(
         null,
         [
             exportSpecifier(
                 ident("a"),
                 ident("a")
             )
         ],
-        lit("b"),
+        moduleRequest(
+            lit("b"),
+            [],
+        ),
         false
     )
 ]).assert(parseAsModule("export { a } from 'b'"));
 
 program([
     exportDeclaration(
         null,
         [
             exportBatchSpecifier()
         ],
-        lit("a"),
+        moduleRequest(
+            lit("a"),
+            [],
+        ),
         false
     )
 ]).assert(parseAsModule("export * from 'a'"));
 
 program([
     exportDeclaration(
         functionDeclaration(
             ident("f"),
@@ -365,16 +381,81 @@ program([
     exportDeclaration(
         lit(1234),
         null,
         null,
         true
     )
 ]).assert(parseAsModule("export default 1234"));
 
+if (getRealmConfiguration()['importAssertions']) {
+    program([
+        exportDeclaration(
+            null,
+            [
+                exportSpecifier(
+                    ident("a"),
+                    ident("a")
+                )
+            ],
+            moduleRequest(
+                lit("b"),
+                [
+                    importAssertion(ident('type'), lit('js')),
+                ]
+            ),
+            false
+        )
+    ]).assert(parseAsModule("export { a } from 'b'  assert { type: 'js' } "));
+
+    program([
+        exportDeclaration(
+            null,
+            [
+                exportSpecifier(
+                    ident("a"),
+                    ident("a")
+                )
+            ],
+            moduleRequest(
+                lit("b"),
+                [
+                    importAssertion(ident('foo'), lit('bar')),
+                ],
+            ),
+            false
+        )
+    ]).assert(parseAsModule("export { a } from 'b'  assert { foo: 'bar', } "));
+
+    program([
+        exportDeclaration(
+            null,
+            [
+                exportSpecifier(
+                    ident("a"),
+                    ident("a")
+                )
+            ],
+            moduleRequest(
+                lit("b"),
+                [
+                    importAssertion(ident('type'), lit('js')),
+                    importAssertion(ident('foo'), lit('bar')),
+                    importAssertion(ident('test'), lit('123')),
+                ]
+            ),
+            false
+        )
+    ]).assert(parseAsModule("export { a } from 'b'  assert { type: 'js', foo: 'bar', 'test': '123' } "));
+
+    assertThrowsInstanceOf(function () {
+        parseAsModule("export { a } from 'b'  assert { type: type }");
+    }, SyntaxError);
+}
+
 assertThrowsInstanceOf(function () {
    parseAsModule("export default 1234 5678");
 }, SyntaxError);
 
 var loc = parseAsModule("export { a as b } from 'c'", {
     loc: true
 }).body[0].loc;
 
@@ -407,31 +488,37 @@ program([
     exportDeclaration(
         null,
         [
             exportSpecifier(
                 ident("true"),
                 ident("true")
             ),
         ],
-        lit("b"),
+        moduleRequest(
+            lit("b"),
+            [],
+        ),
         false
     )
 ]).assert(parseAsModule("export { true } from 'b'"));
 
 program([
     exportDeclaration(
         null,
         [
             exportSpecifier(
                 ident("true"),
                 ident("name")
             ),
         ],
-        lit("b"),
+        moduleRequest(
+            lit("b"),
+            [],
+        ),
         false
     )
 ]).assert(parseAsModule("export { true as name } from 'b'"));
 
 assertThrowsInstanceOf(function() {
     parseAsModule("export { true }");
 }, SyntaxError);
 
--- a/js/src/jit-test/tests/modules/import-declaration.js
+++ b/js/src/jit-test/tests/modules/import-declaration.js
@@ -5,23 +5,33 @@ var { Pattern, MatchError } = Match;
 
 program = (elts) => Pattern({
     type: "Program",
     body: elts
 })
 importDeclaration = (specifiers, source) => Pattern({
     type: "ImportDeclaration",
     specifiers: specifiers,
-    source: source
+    moduleRequest: source
 });
 importSpecifier = (id, name) => Pattern({
     type: "ImportSpecifier",
     id: id,
     name: name
 });
+moduleRequest = (specifier, assertions) => Pattern({
+    type: "ModuleRequest",
+    source: specifier,
+    assertions: assertions
+});
+importAssertion = (key, value) => Pattern({
+    type: "ImportAssertion",
+    key: key,
+    value : value
+});
 importNamespaceSpecifier = (name) => Pattern({
   type: "ImportNamespaceSpecifier",
   name: name
 });
 ident = (name) => Pattern({
     type: "Identifier",
     name: name
 })
@@ -38,142 +48,175 @@ function parseAsModule(source)
 program([
     importDeclaration(
         [
             importSpecifier(
                 ident("default"),
                 ident("a")
             )
         ],
-        lit("b")
+        moduleRequest(
+            lit("b"),
+            []
+        )
     )
 ]).assert(parseAsModule("import a from 'b'"));
 
 program([
     importDeclaration(
         [
             importNamespaceSpecifier(
                 ident("a")
             )
         ],
-        lit("b")
+        moduleRequest(
+            lit("b"),
+            []
+        )
     )
 ]).assert(parseAsModule("import * as a from 'b'"));
 
 program([
     importDeclaration(
         [],
-        lit("a")
+        moduleRequest(
+            lit("a"),
+            []
+        )
     )
 ]).assert(parseAsModule("import {} from 'a'"));
 
 program([
     importDeclaration(
         [
             importSpecifier(
                 ident("a"),
                 ident("a")
             )
         ],
-        lit("b")
+        moduleRequest(
+            lit("b"),
+            []
+        )
     )
 ]).assert(parseAsModule("import { a } from 'b'"));
 
 program([
     importDeclaration(
         [
             importSpecifier(
                 ident("a"),
                 ident("a")
             )
         ],
-        lit("b")
+        moduleRequest(
+            lit("b"),
+            []
+        )
     )
 ]).assert(parseAsModule("import { a, } from 'b'"));
 
 program([
     importDeclaration(
         [
             importSpecifier(
                 ident("a"),
                 ident("b")
             )
         ],
-        lit("c")
+        moduleRequest(
+            lit("c"),
+            []
+        )
     )
 ]).assert(parseAsModule("import { a as b } from 'c'"));
 
 program([
     importDeclaration(
         [
             importSpecifier(
                 ident("as"),
                 ident("as")
             )
         ],
-        lit("a")
+        moduleRequest(
+            lit("a"),
+            []
+        )
     )
 ]).assert(parseAsModule("import { as as as } from 'a'"));
 
 program([
     importDeclaration(
         [
             importSpecifier(
                 ident("default"),
                 ident("a")
             ),
             importNamespaceSpecifier(
                 ident("b")
             )
         ],
-        lit("c")
+        moduleRequest(
+            lit("c"),
+            []
+        )
     )
 ]).assert(parseAsModule("import a, * as b from 'c'"));
 
 program([
     importDeclaration(
         [
             importSpecifier(
                 ident("default"),
                 ident("d")
             )
         ],
-        lit("a")
+        moduleRequest(
+            lit("a"),
+            []
+        )
     )
 ]).assert(parseAsModule("import d, {} from 'a'"));
 
 program([
     importDeclaration(
         [
             importSpecifier(
                 ident("default"),
                 ident("d")
             ),
             importSpecifier(
                 ident("a"),
                 ident("a")
             )
         ],
-        lit("b")
+        moduleRequest(
+            lit("b"),
+            []
+        )
     )
 ]).assert(parseAsModule("import d, { a } from 'b'"));
 
 program([
     importDeclaration(
         [
             importSpecifier(
                 ident("default"),
                 ident("d")
             ),
             importSpecifier(
                 ident("a"),
                 ident("b")
             )
         ],
-        lit("c")
+        moduleRequest(
+            lit("c"),
+            []
+        )
     )
 ]).assert(parseAsModule("import d, { a as b } from 'c'"));
 
 program([
     importDeclaration(
         [
             importSpecifier(
                 ident("default"),
@@ -183,17 +226,20 @@ program([
                 ident("a"),
                 ident("a")
             ),
             importSpecifier(
                 ident("b"),
                 ident("b")
             ),
         ],
-        lit("c")
+        moduleRequest(
+            lit("c"),
+            []
+        )
     )
 ]).assert(parseAsModule("import d, { a, b } from 'c'"));
 
 program([
     importDeclaration(
         [
             importSpecifier(
                 ident("default"),
@@ -203,71 +249,159 @@ program([
                 ident("a"),
                 ident("b")
             ),
             importSpecifier(
                 ident("c"),
                 ident("f")
             ),
         ],
-        lit("e")
+        moduleRequest(
+            lit("e"),
+            []
+        )
     )
 ]).assert(parseAsModule("import d, { a as b, c as f } from 'e'"));
 
 program([
     importDeclaration(
         [
             importSpecifier(
                 ident("true"),
                 ident("a")
             )
         ],
-        lit("b")
+        moduleRequest(
+            lit("b"),
+            []
+        )
     )
 ]).assert(parseAsModule("import { true as a } from 'b'"));
 
 program([
     importDeclaration(
         [
             importSpecifier(
                 ident("a"),
                 ident("a")
             ),
             importSpecifier(
                 ident("b"),
                 ident("b")
             ),
         ],
-        lit("c")
+        moduleRequest(
+            lit("c"),
+            []
+        )
     )
 ]).assert(parseAsModule("import { a, b } from 'c'"));
 
 program([
     importDeclaration(
         [
             importSpecifier(
                 ident("a"),
                 ident("b")
             ),
             importSpecifier(
                 ident("c"),
                 ident("d")
             ),
         ],
-        lit("e")
+        moduleRequest(
+            lit("e"),
+            []
+        )
     )
 ]).assert(parseAsModule("import { a as b, c as d } from 'e'"));
 
 program([
     importDeclaration(
         [],
-        lit("a")
+        moduleRequest(
+            lit("a"),
+            []
+        )
     )
 ]).assert(parseAsModule("import 'a'"));
 
+if (getRealmConfiguration()['importAssertions']) {
+    program([
+        importDeclaration(
+            [
+                importSpecifier(
+                    ident("default"),
+                    ident("a")
+                )
+            ],
+            moduleRequest(
+                lit("b"),
+                []
+            )
+        )
+    ]).assert(parseAsModule("import a from 'b' assert {}"));
+
+    program([
+        importDeclaration(
+            [
+                importSpecifier(
+                    ident("default"),
+                    ident("a")
+                )
+            ],
+            moduleRequest(
+                lit("b"),
+                [
+                    importAssertion(ident('type'), lit('js')),
+                ]
+            )
+        )
+    ]).assert(parseAsModule("import a from 'b' assert { type: 'js' }"));
+
+    program([
+        importDeclaration(
+            [
+                importSpecifier(
+                    ident("default"),
+                    ident("a")
+                )
+            ],
+            moduleRequest(
+                lit("b"),
+                [
+                    importAssertion(ident('foo'), lit('bar')),
+                ]
+            )
+        )
+    ]).assert(parseAsModule("import a from 'b' assert { foo: 'bar' }"));
+
+    program([
+        importDeclaration(
+            [
+                importSpecifier(
+                    ident("default"),
+                    ident("a")
+                )
+            ],
+            moduleRequest(
+                lit("b"),
+                [
+                    importAssertion(ident('type'), lit('js')),
+                    importAssertion(ident('foo'), lit('bar')),
+                ]
+            )
+        )
+    ]).assert(parseAsModule("import a from 'b' assert { type: 'js', foo: 'bar' }"));
+
+    assertThrowsInstanceOf(function () {
+        parseAsModule("import a from 'b' assert { type: type }");
+    }, SyntaxError);
+}
+
 var loc = parseAsModule("import { a as b } from 'c'", {
     loc: true
 }).body[0].loc;
 
 assertEq(loc.start.line, 1);
 assertEq(loc.start.column, 0);
 assertEq(loc.start.line, 1);
 assertEq(loc.end.column, 26);
--- a/js/src/jsast.tbl
+++ b/js/src/jsast.tbl
@@ -58,16 +58,18 @@ ASTDEF(AST_FOR_OF_STMT,           "ForOf
 ASTDEF(AST_BREAK_STMT,            "BreakStatement",                 "breakStatement")
 ASTDEF(AST_CONTINUE_STMT,         "ContinueStatement",              "continueStatement")
 ASTDEF(AST_WITH_STMT,             "WithStatement",                  "withStatement")
 ASTDEF(AST_RETURN_STMT,           "ReturnStatement",                "returnStatement")
 ASTDEF(AST_TRY_STMT,              "TryStatement",                   "tryStatement")
 ASTDEF(AST_THROW_STMT,            "ThrowStatement",                 "throwStatement")
 ASTDEF(AST_DEBUGGER_STMT,         "DebuggerStatement",              "debuggerStatement")
 ASTDEF(AST_LET_STMT,              "LetStatement",                   "letStatement")
+ASTDEF(AST_MODULE_REQUEST,        "ModuleRequest",                  "moduleRequest")
+ASTDEF(AST_IMPORT_ASSERTION,      "ImportAssertion",                "importAssertion")
 ASTDEF(AST_IMPORT_DECL,           "ImportDeclaration",              "importDeclaration")
 ASTDEF(AST_IMPORT_SPEC,           "ImportSpecifier",                "importSpecifier")
 ASTDEF(AST_IMPORT_NAMESPACE_SPEC, "ImportNamespaceSpecifier",       "importNamespaceSpecifier")
 ASTDEF(AST_EXPORT_DECL,           "ExportDeclaration",              "exportDeclaration")
 ASTDEF(AST_EXPORT_SPEC,           "ExportSpecifier",                "exportSpecifier")
 ASTDEF(AST_EXPORT_NAMESPACE_SPEC, "ExportNamespaceSpecifier",       "exportNamespaceSpecifier")
 ASTDEF(AST_EXPORT_BATCH_SPEC,     "ExportBatchSpecifier",           "exportBatchSpecifier")
 
--- a/js/src/tests/non262/reflect-parse/module-export-name.js
+++ b/js/src/tests/non262/reflect-parse/module-export-name.js
@@ -1,32 +1,48 @@
 // |reftest| skip-if(!xulRuntime.shell)
 
-function importDecl(specifiers, source) {
+function moduleRequest(source, assertions) {
+  return {
+    type: "ModuleRequest",
+    source,
+    assertions,
+  };
+}
+
+function importAssertion(key, value) {
+  return {
+    type: "ImportAssertion",
+    key,
+    value,
+  };
+}
+
+function importDecl(specifiers, moduleRequest) {
   return {
     type: "ImportDeclaration",
     specifiers,
-    source,
+    moduleRequest,
   };
 }
 
 function importSpec(id, name) {
   return {
     type: "ImportSpecifier",
     id,
     name,
   };
 }
 
-function exportDecl(declaration, specifiers, source, isDefault) {
+function exportDecl(declaration, specifiers, moduleRequest, isDefault) {
   return {
     type: "ExportDeclaration",
     declaration,
     specifiers,
-    source,
+    moduleRequest,
     isDefault,
   };
 }
 
 function exportSpec(id, name) {
   return {
     type: "ExportSpecifier",
     id,
@@ -43,51 +59,59 @@ function exportNamespaceSpec(name) {
 
 function assertModule(src, patt) {
   program(patt).assert(Reflect.parse(src, {target: "module"}));
 }
 
 assertModule(`
   import {"x" as y} from "module";
 `, [
-  importDecl([importSpec(literal("x"), ident("y"))], literal("module")),
+  importDecl([importSpec(literal("x"), ident("y"))], moduleRequest(literal("module"), [])),
 ]);
 
 assertModule(`
   var x;
   export {x as "y"};
 `, [
   varDecl([{id: ident("x"), init: null}]),
   exportDecl(null, [exportSpec(ident("x"), literal("y"))], null, false),
 ]);
 
 assertModule(`
   export {x as "y"} from "module";
 `, [
-  exportDecl(null, [exportSpec(ident("x"), literal("y"))], literal("module"), false),
+  exportDecl(null, [exportSpec(ident("x"), literal("y"))], moduleRequest(literal("module"), []), false),
 ]);
 
 assertModule(`
   export {"x" as y} from "module";
 `, [
-  exportDecl(null, [exportSpec(literal("x"), ident("y"))], literal("module"), false),
+  exportDecl(null, [exportSpec(literal("x"), ident("y"))], moduleRequest(literal("module"), []), false),
 ]);
 
 assertModule(`
   export {"x" as "y"} from "module";
 `, [
-  exportDecl(null, [exportSpec(literal("x"), literal("y"))], literal("module"), false),
+  exportDecl(null, [exportSpec(literal("x"), literal("y"))], moduleRequest(literal("module"), []), false),
 ]);
 
 assertModule(`
   export {"x"} from "module";
 `, [
-  exportDecl(null, [exportSpec(literal("x"), literal("x"))], literal("module"), false),
+  exportDecl(null, [exportSpec(literal("x"), literal("x"))], moduleRequest(literal("module"), []), false),
 ]);
 
 assertModule(`
   export * as "x" from "module";
 `, [
-  exportDecl(null, [exportNamespaceSpec(literal("x"))], literal("module"), false),
+  exportDecl(null, [exportNamespaceSpec(literal("x"))], moduleRequest(literal("module"), []), false),
 ]);
 
+if (getRealmConfiguration()['importAssertions']) {
+  assertModule(`
+    import {"x" as y} from "module" assert {type: "json"};
+  `, [
+    importDecl([importSpec(literal("x"), ident("y"))], moduleRequest(literal("module"), [importAssertion(ident("type"), literal("json"))])),
+  ]);
+}
+
 if (typeof reportCompare === "function")
   reportCompare(true, true);