Bug 930414 - Add initial bytecode emitter support for modules r=shu
authorJon Coppeard <jcoppeard@mozilla.com>
Mon, 24 Aug 2015 15:58:35 +0100
changeset 259047 52f4c8ddfe07f83c3a36ff96118cc3db1ce6af94
parent 259046 f78c80504443f2f66b2550837dbd6de3724e54ff
child 259048 7fc1a5cd4b301a3f9507697090e71d9f7e975c9b
push id29268
push userryanvm@gmail.com
push dateTue, 25 Aug 2015 00:37:23 +0000
treeherdermozilla-central@08015770c9d6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersshu
bugs930414
milestone43.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 930414 - Add initial bytecode emitter support for modules r=shu
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/jit-test/tests/modules/shell-parse.js
js/src/js.msg
js/src/vm/Xdr.h
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -5878,17 +5878,17 @@ BytecodeEmitter::emitFunction(ParseNode*
      * is executed. This extra work for top-level scripts is not necessary
      * when we emit the code for a function. It is fully parsed prior to
      * invocation of the emitter and calls to emitTree for function
      * definitions can be scheduled before generating the rest of code.
      */
     if (!sc->isFunctionBox()) {
         MOZ_ASSERT(pn->pn_scopecoord.isFree());
         MOZ_ASSERT(pn->getOp() == JSOP_NOP);
-        MOZ_ASSERT(!innermostStmt());
+        MOZ_ASSERT(!innermostStmt() || sc->isModuleBox());
         switchToPrologue();
         if (!emitIndex32(JSOP_DEFFUN, index))
             return false;
         if (!updateSourceCoordNotes(pn->pn_pos.begin))
             return false;
         switchToMain();
     } else {
 #ifdef DEBUG
@@ -7909,22 +7909,46 @@ BytecodeEmitter::emitTree(ParseNode* pn)
         break;
 
       case PNK_CONST:
       case PNK_LET:
         ok = emitVariables(pn, InitializeVars);
         break;
 
       case PNK_IMPORT:
+        if (!checkIsModule())
+            return false;
+        ok = true;
+        break;
+
       case PNK_EXPORT:
+        if (!checkIsModule())
+            return false;
+        if (pn->pn_kid->getKind() != PNK_EXPORT_SPEC_LIST)
+            ok = emitTree(pn->pn_kid);
+        else
+            ok = true;
+        break;
+
       case PNK_EXPORT_DEFAULT:
+        if (!checkIsModule())
+            return false;
+        if (pn->pn_kid->isDefn()) {
+            ok = emitTree(pn->pn_kid);
+        } else {
+            // TODO: Emit a definition of *default* from child expression.
+            ok = true;
+        }
+        break;
+
       case PNK_EXPORT_FROM:
-       // TODO: Implement emitter support for modules
-       reportError(nullptr, JSMSG_MODULES_NOT_IMPLEMENTED);
-       return false;
+        if (!checkIsModule())
+            return false;
+        ok = true;
+        break;
 
       case PNK_ARRAYPUSH: {
         /*
          * The array object's stack index is in arrayCompDepth. See below
          * under the array initialiser code generator for array comprehension
          * special casing. Note that the array object is a pure stack value,
          * unaliased by blocks, so we can emitUnaliasedVarOp.
          */
@@ -8047,16 +8071,26 @@ BytecodeEmitter::emitTree(ParseNode* pn)
     if (ok && emitLevel == 1) {
         if (!updateSourceCoordNotes(pn->pn_pos.end))
             return false;
     }
 
     return ok;
 }
 
+bool
+BytecodeEmitter::checkIsModule()
+{
+    if (!sc->isModuleBox()) {
+        reportError(nullptr, JSMSG_INVALID_OUTSIDE_MODULE);
+        return false;
+    }
+    return true;
+}
+
 static bool
 AllocSrcNote(ExclusiveContext* cx, SrcNotesVector& notes, unsigned* index)
 {
     // Start it off moderately large to avoid repeated resizings early on.
     // ~99% of cases fit within 256 bytes.
     if (notes.capacity() == 0 && !notes.reserve(256))
         return false;
 
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -318,16 +318,19 @@ struct BytecodeEmitter
     bool emitTree(ParseNode* pn);
 
     // Emit function code for the tree rooted at body.
     bool emitFunctionScript(ParseNode* body);
 
     // Emit module code for the tree rooted at body.
     bool emitModuleScript(ParseNode* body);
 
+    // Report an error if we are not processing a module.
+    bool checkIsModule();
+
     // If op is JOF_TYPESET (see the type barriers comment in TypeInference.h),
     // reserve a type set to store its result.
     void checkTypeSet(JSOp op);
 
     void updateDepth(ptrdiff_t target);
     bool updateLineNumberNotes(uint32_t offset);
     bool updateSourceCoordNotes(uint32_t offset);
 
--- a/js/src/jit-test/tests/modules/shell-parse.js
+++ b/js/src/jit-test/tests/modules/shell-parse.js
@@ -1,4 +1,28 @@
 // Exercise shell parseModule function.
 
+function testEvalError(source) {
+    // Test |source| throws when passed to eval.
+    var caught = false;
+    try {
+        eval(source);
+    } catch (e) {
+        caught = true;
+    }
+    assertEq(caught, true);
+}
+
+function testModuleSource(source) {
+    // Test |source| parses as a module, but throws when passed to eval.
+    testEvalError(source);
+    parseModule(source);
+}
+
 parseModule("");
 parseModule("const foo = 1;");
+
+testModuleSource("import * as ns from 'bar';");
+testModuleSource("export { a } from 'b';");
+testModuleSource("export * from 'b';");
+testModuleSource("export const foo = 1;");
+testModuleSource("export default function() {};");
+testModuleSource("export default 1;");
--- a/js/src/js.msg
+++ b/js/src/js.msg
@@ -255,30 +255,30 @@ MSG_DEF(JSMSG_EXPORT_DECL_AT_TOP_LEVEL,0
 MSG_DEF(JSMSG_FINALLY_WITHOUT_TRY,     0, JSEXN_SYNTAXERR, "finally without try")
 MSG_DEF(JSMSG_FROM_AFTER_IMPORT_CLAUSE, 0, JSEXN_SYNTAXERR, "missing keyword 'from' after import clause")
 MSG_DEF(JSMSG_FROM_AFTER_EXPORT_STAR,  0, JSEXN_SYNTAXERR, "missing keyword 'from' after export *")
 MSG_DEF(JSMSG_GARBAGE_AFTER_INPUT,     2, JSEXN_SYNTAXERR, "unexpected garbage after {0}, starting with {1}")
 MSG_DEF(JSMSG_IDSTART_AFTER_NUMBER,    0, JSEXN_SYNTAXERR, "identifier starts immediately after numeric literal")
 MSG_DEF(JSMSG_ILLEGAL_CHARACTER,       0, JSEXN_SYNTAXERR, "illegal character")
 MSG_DEF(JSMSG_IMPORT_DECL_AT_TOP_LEVEL, 0, JSEXN_SYNTAXERR, "import declarations may only appear at top level")
 MSG_DEF(JSMSG_INVALID_FOR_INOF_DECL_WITH_INIT,1,JSEXN_SYNTAXERR,"for-{0} loop head declarations may not have initializers")
+MSG_DEF(JSMSG_INVALID_OUTSIDE_MODULE,  0, JSEXN_SYNTAXERR, "import and export declarations are only valid at the top level of a module")
 MSG_DEF(JSMSG_IN_AFTER_FOR_NAME,       0, JSEXN_SYNTAXERR, "missing 'in' or 'of' after for")
 MSG_DEF(JSMSG_LABEL_NOT_FOUND,         0, JSEXN_SYNTAXERR, "label not found")
 MSG_DEF(JSMSG_LET_CLASS_BINDING,       0, JSEXN_SYNTAXERR, "'let' is not a valid name for a class")
 MSG_DEF(JSMSG_LET_COMP_BINDING,        0, JSEXN_SYNTAXERR, "'let' is not a valid name for a comprehension variable")
 MSG_DEF(JSMSG_LEXICAL_DECL_NOT_IN_BLOCK,   1, JSEXN_SYNTAXERR, "{0} declaration not directly within block")
 MSG_DEF(JSMSG_LINE_BREAK_AFTER_THROW,  0, JSEXN_SYNTAXERR, "no line break is allowed between 'throw' and its expression")
 MSG_DEF(JSMSG_MALFORMED_ESCAPE,        1, JSEXN_SYNTAXERR, "malformed {0} character escape sequence")
 MSG_DEF(JSMSG_MISSING_BINARY_DIGITS,   0, JSEXN_SYNTAXERR, "missing binary digits after '0b'")
 MSG_DEF(JSMSG_MISSING_EXPONENT,        0, JSEXN_SYNTAXERR, "missing exponent")
 MSG_DEF(JSMSG_MISSING_EXPR_AFTER_THROW,0, JSEXN_SYNTAXERR, "throw statement is missing an expression")
 MSG_DEF(JSMSG_MISSING_FORMAL,          0, JSEXN_SYNTAXERR, "missing formal parameter")
 MSG_DEF(JSMSG_MISSING_HEXDIGITS,       0, JSEXN_SYNTAXERR, "missing hexadecimal digits after '0x'")
 MSG_DEF(JSMSG_MISSING_OCTAL_DIGITS,    0, JSEXN_SYNTAXERR, "missing octal digits after '0o'")
-MSG_DEF(JSMSG_MODULES_NOT_IMPLEMENTED, 0, JSEXN_SYNTAXERR, "modules are not implemented yet")
 MSG_DEF(JSMSG_MODULE_SPEC_AFTER_FROM,  0, JSEXN_SYNTAXERR, "missing module specifier after 'from' keyword")
 MSG_DEF(JSMSG_NAME_AFTER_DOT,          0, JSEXN_SYNTAXERR, "missing name after . operator")
 MSG_DEF(JSMSG_NAME_AFTER_IMPORT_STAR_AS, 0, JSEXN_SYNTAXERR, "missing name after import * as")
 MSG_DEF(JSMSG_NAMED_IMPORTS_OR_NAMESPACE_IMPORT, 0, JSEXN_SYNTAXERR, "expected named imports or namespace import after comma")
 MSG_DEF(JSMSG_NONDEFAULT_FORMAL_AFTER_DEFAULT, 0, JSEXN_SYNTAXERR, "parameter(s) with default followed by parameter without default")
 MSG_DEF(JSMSG_NO_BINDING_NAME,        0, JSEXN_SYNTAXERR, "missing binding name")
 MSG_DEF(JSMSG_NO_CLASS_CONSTRUCTOR,    0, JSEXN_TYPEERR,   "class default constructors not yet implemented")
 MSG_DEF(JSMSG_NO_EXPORT_NAME,          0, JSEXN_SYNTAXERR, "missing export name")
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -24,17 +24,17 @@ namespace js {
  * versions.  If deserialization fails, the data should be invalidated if
  * possible.
  *
  * When you change this, run make_opcode_doc.py and copy the new output into
  * this wiki page:
  *
  *  https://developer.mozilla.org/en-US/docs/SpiderMonkey/Internals/Bytecode
  */
-static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 301;
+static const uint32_t XDR_BYTECODE_VERSION_SUBTRAHEND = 302;
 static const uint32_t XDR_BYTECODE_VERSION =
     uint32_t(0xb973c0de - XDR_BYTECODE_VERSION_SUBTRAHEND);
 
 static_assert(JSErr_Limit == 407,
               "GREETINGS, POTENTIAL SUBTRAHEND INCREMENTER! If you added or "
               "removed MSG_DEFs from js.msg, you should increment "
               "XDR_BYTECODE_VERSION_SUBTRAHEND and update this assertion's "
               "expected JSErr_Limit value.");