Bug 930414 - Add ModuleObject and CompileModule() function r=shu
authorJon Coppeard <jcoppeard@mozilla.com>
Mon, 24 Aug 2015 15:58:35 +0100
changeset 259027 f78c80504443f2f66b2550837dbd6de3724e54ff
parent 259026 793916ec6dd4c589a8d589967158ccff532527ac
child 259028 52f4c8ddfe07f83c3a36ff96118cc3db1ce6af94
push id64110
push userjcoppeard@mozilla.com
push dateMon, 24 Aug 2015 15:00:44 +0000
treeherdermozilla-inbound@0773712473c9 [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 ModuleObject and CompileModule() function r=shu
js/src/builtin/ModuleObject.cpp
js/src/builtin/ModuleObject.h
js/src/frontend/BytecodeCompiler.cpp
js/src/frontend/BytecodeCompiler.h
js/src/frontend/BytecodeEmitter.cpp
js/src/frontend/BytecodeEmitter.h
js/src/frontend/FoldConstants.cpp
js/src/frontend/FullParseHandler.h
js/src/frontend/NameFunctions.cpp
js/src/frontend/ParseNode.cpp
js/src/frontend/ParseNode.h
js/src/frontend/Parser.cpp
js/src/frontend/Parser.h
js/src/frontend/SharedContext.h
js/src/gc/Marking.cpp
js/src/jit-test/tests/modules/shell-parse.js
js/src/jsscript.cpp
js/src/jsscript.h
js/src/jsscriptinlines.h
js/src/moz.build
js/src/shell/js.cpp
js/src/vm/ScopeObject.h
js/src/vm/TraceLogging.cpp
js/src/vm/TraceLoggingTypes.h
new file mode 100644
--- /dev/null
+++ b/js/src/builtin/ModuleObject.cpp
@@ -0,0 +1,61 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "builtin/ModuleObject.h"
+
+#include "jsobjinlines.h"
+
+using namespace js;
+
+///////////////////////////////////////////////////////////////////////////
+// ModuleObject
+
+const Class ModuleObject::class_ = {
+    "Module",
+    JSCLASS_HAS_RESERVED_SLOTS(ModuleObject::SlotCount) |
+    JSCLASS_IS_ANONYMOUS |
+    JSCLASS_IMPLEMENTS_BARRIERS,
+    nullptr,        /* addProperty */
+    nullptr,        /* delProperty */
+    nullptr,        /* getProperty */
+    nullptr,        /* setProperty */
+    nullptr,        /* enumerate   */
+    nullptr,        /* resolve     */
+    nullptr,        /* mayResolve  */
+    nullptr,        /* convert     */
+    nullptr,        /* finalize    */
+    nullptr,        /* call        */
+    nullptr,        /* hasInstance */
+    nullptr,        /* construct   */
+    ModuleObject::trace
+};
+
+/* static */ ModuleObject*
+ModuleObject::create(ExclusiveContext* cx)
+{
+    return NewBuiltinClassInstance<ModuleObject>(cx, TenuredObject);
+}
+
+void
+ModuleObject::init(HandleScript script)
+{
+    initReservedSlot(ScriptSlot, PrivateValue(script));
+}
+
+JSScript*
+ModuleObject::script() const
+{
+    return static_cast<JSScript*>(getReservedSlot(ScriptSlot).toPrivate());
+}
+
+/* static */ void
+ModuleObject::trace(JSTracer* trc, JSObject* obj)
+{
+    ModuleObject& module = obj->as<ModuleObject>();
+    JSScript* script = module.script();
+    TraceManuallyBarrieredEdge(trc, &script, "Module script");
+    module.setReservedSlot(ScriptSlot, PrivateValue(script));
+}
new file mode 100644
--- /dev/null
+++ b/js/src/builtin/ModuleObject.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef builtin_ModuleObject_h
+#define builtin_ModuleObject_h
+
+#include "vm/NativeObject.h"
+
+namespace js {
+
+class ModuleObject : public NativeObject
+{
+  public:
+    enum
+    {
+        ScriptSlot = 0,
+        SlotCount
+    };
+
+    static const Class class_;
+
+    static ModuleObject* create(ExclusiveContext* cx);
+    void init(HandleScript script);
+
+    JSScript* script() const;
+
+  private:
+    static void trace(JSTracer* trc, JSObject* obj);
+};
+
+typedef Rooted<ModuleObject*> RootedModuleObject;
+typedef Handle<ModuleObject*> HandleModuleObject;
+
+} // namespace js
+
+#endif /* builtin_ModuleObject_h */
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -5,16 +5,17 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "frontend/BytecodeCompiler.h"
 
 #include "jscntxt.h"
 #include "jsscript.h"
 
 #include "asmjs/AsmJSLink.h"
+#include "builtin/ModuleObject.h"
 #include "frontend/BytecodeEmitter.h"
 #include "frontend/FoldConstants.h"
 #include "frontend/NameFunctions.h"
 #include "frontend/Parser.h"
 #include "vm/GlobalObject.h"
 #include "vm/TraceLogging.h"
 
 #include "jsobjinlines.h"
@@ -52,16 +53,17 @@ class MOZ_STACK_CLASS BytecodeCompiler
                      Handle<ScopeObject*> enclosingStaticScope,
                      TraceLoggerTextId logId);
 
     // Call setters for optional arguments.
     void maybeSetSourceCompressor(SourceCompressionTask* sourceCompressor);
     void setSourceArgumentsNotIncluded();
 
     JSScript* compileScript(HandleObject scopeChain, HandleScript evalCaller);
+    ModuleObject* compileModule();
     bool compileFunctionBody(MutableHandleFunction fun, Handle<PropertyNameVector> formals,
                              GeneratorKind generatorKind);
 
   private:
     bool checkLength();
     bool createScriptSource();
     bool maybeCompressSource();
     bool canLazilyParse();
@@ -601,16 +603,60 @@ BytecodeCompiler::compileScript(HandleOb
 
     if (!maybeCompleteCompressSource())
         return nullptr;
 
     MOZ_ASSERT_IF(cx->isJSContext(), !cx->asJSContext()->isExceptionPending());
     return script;
 }
 
+ModuleObject* BytecodeCompiler::compileModule()
+{
+    MOZ_ASSERT(!enclosingStaticScope);
+
+    if (!createSourceAndParser())
+        return nullptr;
+
+    if (!createScript())
+        return nullptr;
+
+    Rooted<ModuleObject*> module(cx, ModuleObject::create(cx));
+    if (!module)
+        return nullptr;
+
+    module->init(script);
+
+    ParseNode* pn = parser->standaloneModule(module);
+    if (!pn)
+        return nullptr;
+
+    if (!NameFunctions(cx, pn) ||
+        !maybeSetDisplayURL(parser->tokenStream) ||
+        !maybeSetSourceMap(parser->tokenStream))
+    {
+        return nullptr;
+    }
+
+    script->bindings = pn->pn_modulebox->bindings;
+
+    if (!createEmitter(pn->pn_modulebox) ||
+        !emitter->emitModuleScript(pn->pn_body))
+    {
+        return nullptr;
+    }
+
+    if (!maybeCompleteCompressSource())
+        return nullptr;
+
+    parser->handler.freeTree(pn);
+
+    MOZ_ASSERT_IF(cx->isJSContext(), !cx->asJSContext()->isExceptionPending());
+    return module;
+}
+
 bool
 BytecodeCompiler::compileFunctionBody(MutableHandleFunction fun,
                                       Handle<PropertyNameVector> formals,
                                       GeneratorKind generatorKind)
 {
     MOZ_ASSERT(fun);
     MOZ_ASSERT(fun->isTenured());
 
@@ -716,16 +762,32 @@ frontend::CompileScript(ExclusiveContext
     MOZ_ASSERT_IF(evalCaller && evalCaller->strict(), options.strictOption);
 
     BytecodeCompiler compiler(cx, alloc, options, srcBuf, enclosingStaticScope,
                               TraceLogger_ParserCompileScript);
     compiler.maybeSetSourceCompressor(extraSct);
     return compiler.compileScript(scopeChain, evalCaller);
 }
 
+ModuleObject*
+frontend::CompileModule(JSContext* cx, HandleObject obj,
+                        const ReadOnlyCompileOptions& optionsInput,
+                        SourceBufferHolder& srcBuf)
+{
+    MOZ_ASSERT(srcBuf.get());
+
+    CompileOptions options(cx, optionsInput);
+    options.maybeMakeStrictMode(true); // ES6 10.2.1 Module code is always strict mode code.
+    options.setIsRunOnce(true);
+
+    BytecodeCompiler compiler(cx, &cx->tempLifoAlloc(), options, srcBuf, nullptr,
+                              TraceLogger_ParserCompileModule);
+    return compiler.compileModule();
+}
+
 bool
 frontend::CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const char16_t* chars, size_t length)
 {
     MOZ_ASSERT(cx->compartment() == lazy->functionNonDelazifying()->compartment());
 
     CompileOptions options(cx, lazy->version());
     options.setMutedErrors(lazy->mutedErrors())
            .setFileAndLine(lazy->filename(), lazy->lineno())
--- a/js/src/frontend/BytecodeCompiler.h
+++ b/js/src/frontend/BytecodeCompiler.h
@@ -12,29 +12,34 @@
 #include "vm/String.h"
 
 class JSLinearString;
 
 namespace js {
 
 class LazyScript;
 class LifoAlloc;
+class ModuleObject;
 class ScriptSourceObject;
 class ScopeObject;
 struct SourceCompressionTask;
 
 namespace frontend {
 
 JSScript*
 CompileScript(ExclusiveContext* cx, LifoAlloc* alloc,
               HandleObject scopeChain, Handle<ScopeObject*> enclosingStaticScope,
               HandleScript evalCaller, const ReadOnlyCompileOptions& options,
               SourceBufferHolder& srcBuf, JSString* source_ = nullptr,
               SourceCompressionTask* extraSct = nullptr);
 
+ModuleObject *
+CompileModule(JSContext *cx, HandleObject obj, const ReadOnlyCompileOptions &options,
+              SourceBufferHolder &srcBuf);
+
 bool
 CompileLazyFunction(JSContext* cx, Handle<LazyScript*> lazy, const char16_t* chars, size_t length);
 
 /*
  * enclosingStaticScope is a static enclosing scope (e.g. a StaticWithObject).
  * Must be null if the enclosing scope is a global.
  */
 bool
--- a/js/src/frontend/BytecodeEmitter.cpp
+++ b/js/src/frontend/BytecodeEmitter.cpp
@@ -2207,16 +2207,20 @@ BytecodeEmitter::checkSideEffects(ParseN
          * we bind its name lexically (using JSOP_CALLEE) instead of creating
          * an Object instance and binding a readonly, permanent property in it
          * (the object and binding can be detected and hijacked or captured).
          * This is a bug fix to ES3; it is fixed in ES3.1 drafts.
          */
         *answer = false;
         return true;
 
+      case PNK_MODULE:
+        *answer = false;
+        return true;
+
       // Generator expressions have no side effects on their own.
       case PNK_GENEXP:
         MOZ_ASSERT(pn->isArity(PN_LIST));
         *answer = false;
         return true;
 
       case PNK_TRY:
         MOZ_ASSERT(pn->isArity(PN_TERNARY));
@@ -3496,16 +3500,65 @@ BytecodeEmitter::emitFunctionScript(Pars
     }
 
     tellDebuggerAboutCompiledScript(cx);
 
     return true;
 }
 
 bool
+BytecodeEmitter::emitModuleScript(ParseNode* body)
+{
+    if (!updateLocalsToFrameSlots())
+        return false;
+
+    /*
+     * IonBuilder has assumptions about what may occur immediately after
+     * script->main (e.g., in the case of destructuring params). Thus, put the
+     * following ops into the range [script->code, script->main). Note:
+     * execution starts from script->code, so this has no semantic effect.
+     */
+
+    ModuleBox* modulebox = sc->asModuleBox();
+
+    // Link the module and the script to each other, so that StaticScopeIter
+    // may walk the scope chain of currently compiling scripts.
+    JSScript::linkToModuleFromEmitter(cx, script, modulebox);
+
+    if (!emitTree(body))
+        return false;
+
+    // Always end the script with a JSOP_RETRVAL. Some other parts of the codebase
+    // depend on this opcode, e.g. InterpreterRegs::setToEndOfScript.
+    if (!emit1(JSOP_RETRVAL))
+        return false;
+
+    // If all locals are aliased, the frame's block slots won't be used, so we
+    // can set numBlockScoped = 0. This is nice for generators as it ensures
+    // nfixed == 0, so we don't have to initialize any local slots when resuming
+    // a generator.
+    if (sc->allLocalsAliased())
+        script->bindings.setAllLocalsAliased();
+
+    if (!JSScript::fullyInitFromEmitter(cx, script, this))
+        return false;
+
+    /*
+     * Since modules are only run once. Mark the script so that initializers
+     * created within it may be given more precise types.
+     */
+    script->setTreatAsRunOnce();
+    MOZ_ASSERT(!script->hasRunOnce());
+
+    tellDebuggerAboutCompiledScript(cx);
+
+    return true;
+}
+
+bool
 BytecodeEmitter::maybeEmitVarDecl(JSOp prologueOp, ParseNode* pn, jsatomid* result)
 {
     jsatomid atomIndex;
 
     if (!pn->pn_scopecoord.isFree()) {
         atomIndex = pn->pn_scopecoord.slot();
     } else {
         if (!makeAtomIndex(pn->pn_atom, &atomIndex))
--- a/js/src/frontend/BytecodeEmitter.h
+++ b/js/src/frontend/BytecodeEmitter.h
@@ -315,16 +315,19 @@ struct BytecodeEmitter
     void setJumpOffsetAt(ptrdiff_t off);
 
     // Emit code for the tree rooted at pn.
     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);
+
     // 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/frontend/FoldConstants.cpp
+++ b/js/src/frontend/FoldConstants.cpp
@@ -95,16 +95,20 @@ ContainsHoistedDeclaration(ExclusiveCont
       // the function statement is evaluated.  Thus any declaration introduced
       // by a function statement, as observed by this function, isn't a hoisted
       // declaration.
       case PNK_FUNCTION:
         MOZ_ASSERT(node->isArity(PN_CODE));
         *result = false;
         return true;
 
+      case PNK_MODULE:
+        *result = false;
+        return true;
+
       // Statements with no sub-components at all.
       case PNK_NOP: // induced by function f() {} function f() {}
       case PNK_DEBUGGER:
         MOZ_ASSERT(node->isArity(PN_NULLARY));
         *result = false;
         return true;
 
       // Statements containing only an expression have no declarations.
@@ -1110,16 +1114,27 @@ ComputeBinary(ParseNodeKind kind, double
     MOZ_ASSERT(kind == PNK_LSH || kind == PNK_RSH);
 
     int32_t i = ToInt32(left);
     uint32_t j = ToUint32(right) & 31;
     return int32_t((kind == PNK_LSH) ? uint32_t(i) << j : i >> j);
 }
 
 static bool
+FoldModule(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser)
+{
+    MOZ_ASSERT(node->isKind(PNK_MODULE));
+    MOZ_ASSERT(node->isArity(PN_CODE));
+
+    ParseNode*& moduleBody = node->pn_body;
+    MOZ_ASSERT(moduleBody);
+    return Fold(cx, &moduleBody, parser, false);
+}
+
+static bool
 FoldBinaryArithmetic(ExclusiveContext* cx, ParseNode* node, Parser<FullParseHandler>& parser,
                      bool inGenexpLambda)
 {
     MOZ_ASSERT(node->isKind(PNK_SUB) ||
                node->isKind(PNK_STAR) ||
                node->isKind(PNK_LSH) ||
                node->isKind(PNK_RSH) ||
                node->isKind(PNK_URSH) ||
@@ -1782,16 +1797,19 @@ Fold(ExclusiveContext* cx, ParseNode** p
 
       case PNK_AND:
       case PNK_OR:
         return FoldAndOr(cx, pnp, parser, inGenexpLambda);
 
       case PNK_FUNCTION:
         return FoldFunction(cx, pn, parser, inGenexpLambda);
 
+      case PNK_MODULE:
+        return FoldModule(cx, pn, parser);
+
       case PNK_SUB:
       case PNK_STAR:
       case PNK_LSH:
       case PNK_RSH:
       case PNK_URSH:
       case PNK_DIV:
       case PNK_MOD:
         return FoldBinaryArithmetic(cx, pn, parser, inGenexpLambda);
--- a/js/src/frontend/FullParseHandler.h
+++ b/js/src/frontend/FullParseHandler.h
@@ -630,33 +630,41 @@ class FullParseHandler
 
     inline bool addCatchBlock(ParseNode* catchList, ParseNode* letBlock,
                               ParseNode* catchName, ParseNode* catchGuard, ParseNode* catchBody);
 
     inline bool setLastFunctionArgumentDefault(ParseNode* funcpn, ParseNode* pn);
     inline void setLastFunctionArgumentDestructuring(ParseNode* funcpn, ParseNode* pn);
 
     ParseNode* newFunctionDefinition() {
-        return new_<CodeNode>(pos());
+        return new_<CodeNode>(PNK_FUNCTION, pos());
     }
     void setFunctionBody(ParseNode* pn, ParseNode* kid) {
         pn->pn_body = kid;
     }
     void setFunctionBox(ParseNode* pn, FunctionBox* funbox) {
         MOZ_ASSERT(pn->isKind(PNK_FUNCTION));
         pn->pn_funbox = funbox;
     }
     void addFunctionArgument(ParseNode* pn, ParseNode* argpn) {
         pn->pn_body->append(argpn);
     }
     void setDerivedClassConstructor(ParseNode* pn) {
         MOZ_ASSERT(pn->isKind(PNK_FUNCTION));
         pn->pn_funbox->setDerivedClassConstructor();
     }
 
+    ParseNode* newModule() {
+        return new_<CodeNode>(PNK_MODULE, pos());
+    }
+    void setModuleBox(ParseNode* pn, ModuleBox* modulebox) {
+        MOZ_ASSERT(pn->isKind(PNK_MODULE));
+        pn->pn_modulebox = modulebox;
+    }
+
     ParseNode* newLexicalScope(ObjectBox* blockBox) {
         return new_<LexicalScopeNode>(blockBox, pos());
     }
     void setLexicalScopeBody(ParseNode* block, ParseNode* body) {
         block->pn_expr = body;
     }
 
     ParseNode* newLetBlock(ParseNode* vars, ParseNode* block, const TokenPos& pos) {
--- a/js/src/frontend/NameFunctions.cpp
+++ b/js/src/frontend/NameFunctions.cpp
@@ -332,17 +332,17 @@ class NameResolver
      * ParseNode instance given. The prefix is for each subsequent name, and
      * should initially be nullptr.
      */
     bool resolve(ParseNode* cur, HandleAtom prefixArg = nullptr) {
         RootedAtom prefix(cx, prefixArg);
         if (cur == nullptr)
             return true;
 
-        MOZ_ASSERT(cur->isKind(PNK_FUNCTION) == cur->isArity(PN_CODE));
+        MOZ_ASSERT((cur->isKind(PNK_FUNCTION) || cur->isKind(PNK_MODULE)) == cur->isArity(PN_CODE));
         if (cur->isKind(PNK_FUNCTION)) {
             RootedAtom prefix2(cx);
             if (!resolveFun(cur, prefix, &prefix2))
                 return false;
 
             /*
              * If a function looks like (function(){})() where the parent node
              * of the definition of the function is a call, then it shouldn't
@@ -762,16 +762,17 @@ class NameResolver
           case PNK_LEXICALSCOPE:
           case PNK_NAME:
             MOZ_ASSERT(cur->isArity(PN_NAME));
             if (!resolve(cur->maybeExpr(), prefix))
                 return false;
             break;
 
           case PNK_FUNCTION:
+          case PNK_MODULE:
             MOZ_ASSERT(cur->isArity(PN_CODE));
             if (!resolve(cur->pn_body, prefix))
                 return false;
             break;
 
           // Kinds that should be handled by parent node resolution.
 
           case PNK_IMPORT_SPEC: // by PNK_IMPORT_SPEC_LIST
--- a/js/src/frontend/ParseNode.cpp
+++ b/js/src/frontend/ParseNode.cpp
@@ -516,16 +516,17 @@ PushNodeChildren(ParseNode* pn, NodeStac
 
       case PNK_LABEL:
       case PNK_DOT:
       case PNK_LEXICALSCOPE:
       case PNK_NAME:
         return PushNameNodeChildren(pn, stack);
 
       case PNK_FUNCTION:
+      case PNK_MODULE:
         return PushCodeNodeChildren(pn, stack);
 
       case PNK_LIMIT: // invalid sentinel value
         MOZ_CRASH("invalid node kind");
     }
 
     MOZ_CRASH("bad ParseNodeKind");
     return PushResult::CleanUpLater;
@@ -1146,23 +1147,33 @@ ObjectBox::ObjectBox(JSFunction* functio
 
 FunctionBox*
 ObjectBox::asFunctionBox()
 {
     MOZ_ASSERT(isFunctionBox());
     return static_cast<FunctionBox*>(this);
 }
 
+ModuleBox*
+ObjectBox::asModuleBox()
+{
+    MOZ_ASSERT(isModuleBox());
+    return static_cast<ModuleBox*>(this);
+}
+
 void
 ObjectBox::trace(JSTracer* trc)
 {
     ObjectBox* box = this;
     while (box) {
         TraceRoot(trc, &box->object, "parser.object");
         if (box->isFunctionBox()) {
             FunctionBox* funbox = box->asFunctionBox();
             funbox->bindings.trace(trc);
             if (funbox->enclosingStaticScope_)
                 TraceRoot(trc, &funbox->enclosingStaticScope_, "funbox-enclosingStaticScope");
+        } else if (box->isModuleBox()) {
+            ModuleBox* modulebox = box->asModuleBox();
+            modulebox->bindings.trace(trc);
         }
         box = box->traceLink;
     }
 }
--- a/js/src/frontend/ParseNode.h
+++ b/js/src/frontend/ParseNode.h
@@ -14,16 +14,17 @@
 namespace js {
 namespace frontend {
 
 template <typename ParseHandler>
 struct ParseContext;
 
 class FullParseHandler;
 class FunctionBox;
+class ModuleBox;
 class ObjectBox;
 
 // A packed ScopeCoordinate for use in the frontend during bytecode
 // compilation.
 //
 // Definitions start out !isFree() && isHopsUnknown().
 // Uses start out isFree().
 //
@@ -113,16 +114,17 @@ class PackedScopeCoordinate
     F(TAGGED_TEMPLATE) \
     F(CALLSITEOBJ) \
     F(REGEXP) \
     F(TRUE) \
     F(FALSE) \
     F(NULL) \
     F(THIS) \
     F(FUNCTION) \
+    F(MODULE) \
     F(IF) \
     F(SWITCH) \
     F(CASE) \
     F(DEFAULT) \
     F(WHILE) \
     F(DOWHILE) \
     F(FOR) \
     F(BREAK) \
@@ -631,19 +633,20 @@ class ParseNode
         } binary;
         struct {                        /* one kid if unary */
             ParseNode*  kid;
             bool        prologue;       /* directive prologue member (as
                                            pn_prologue) */
         } unary;
         struct {                        /* name, labeled statement, etc. */
             union {
-                JSAtom*     atom;      /* lexical name or label atom */
-                ObjectBox*  objbox;    /* block or regexp object */
+                JSAtom*      atom;      /* lexical name or label atom */
+                ObjectBox*   objbox;    /* block or regexp object */
                 FunctionBox* funbox;    /* function object */
+                ModuleBox*   modulebox; /* module object */
             };
             union {
                 ParseNode*  expr;      /* module or function body, var
                                            initializer, argument default, or
                                            base object of PNK_DOT */
                 Definition* lexdef;    /* lexical definition for this use */
             };
             PackedScopeCoordinate scopeCoord;
@@ -657,16 +660,17 @@ class ParseNode
         } number;
         class {
             friend class LoopControlStatement;
             PropertyName*    label;    /* target of break/continue statement */
         } loopControl;
     } pn_u;
 
 #define pn_modulebox    pn_u.name.modulebox
+#define pn_objbox       pn_u.name.objbox
 #define pn_funbox       pn_u.name.funbox
 #define pn_body         pn_u.name.expr
 #define pn_scopecoord   pn_u.name.scopeCoord
 #define pn_dflags       pn_u.name.dflags
 #define pn_blockid      pn_u.name.blockid
 #define pn_head         pn_u.list.head
 #define pn_tail         pn_u.list.tail
 #define pn_count        pn_u.list.count
@@ -1067,25 +1071,27 @@ struct ListNode : public ParseNode
 
 #ifdef DEBUG
     void dump(int indent);
 #endif
 };
 
 struct CodeNode : public ParseNode
 {
-    explicit CodeNode(const TokenPos& pos)
-      : ParseNode(PNK_FUNCTION, JSOP_NOP, PN_CODE, pos)
+    CodeNode(ParseNodeKind kind, const TokenPos& pos)
+      : ParseNode(kind, JSOP_NOP, PN_CODE, pos)
     {
+        MOZ_ASSERT(kind == PNK_FUNCTION || kind == PNK_MODULE);
         MOZ_ASSERT(!pn_body);
-        MOZ_ASSERT(!pn_funbox);
+        MOZ_ASSERT(!pn_objbox);
         MOZ_ASSERT(pn_dflags == 0);
         pn_scopecoord.makeFree();
     }
 
+  public:
 #ifdef DEBUG
     void dump(int indent);
 #endif
 };
 
 struct NameNode : public ParseNode
 {
     NameNode(ParseNodeKind kind, JSOp op, JSAtom* atom, uint32_t blockid,
@@ -1682,16 +1688,18 @@ ParseNode::isConstant()
 class ObjectBox
 {
   public:
     JSObject* object;
 
     ObjectBox(JSObject* object, ObjectBox* traceLink);
     bool isFunctionBox() { return object->is<JSFunction>(); }
     FunctionBox* asFunctionBox();
+    bool isModuleBox() { return object->is<ModuleObject>(); }
+    ModuleBox* asModuleBox();
     void trace(JSTracer* trc);
 
   protected:
     friend struct CGObjectList;
 
     ObjectBox* traceLink;
     ObjectBox* emitLink;
 
--- a/js/src/frontend/Parser.cpp
+++ b/js/src/frontend/Parser.cpp
@@ -24,16 +24,17 @@
 #include "jscntxt.h"
 #include "jsfun.h"
 #include "jsobj.h"
 #include "jsopcode.h"
 #include "jsscript.h"
 #include "jstypes.h"
 
 #include "asmjs/AsmJSValidate.h"
+#include "builtin/ModuleObject.h"
 #include "frontend/BytecodeCompiler.h"
 #include "frontend/FoldConstants.h"
 #include "frontend/ParseMaps.h"
 #include "frontend/TokenStream.h"
 #include "vm/Shape.h"
 
 #include "jsatominlines.h"
 #include "jsscriptinlines.h"
@@ -404,22 +405,19 @@ AppendPackedBindings(const ParseContext<
         *dst = Binding(name, kind, aliased);
         if (!aliased && numUnaliased)
             ++*numUnaliased;
     }
 }
 
 template <typename ParseHandler>
 bool
-ParseContext<ParseHandler>::generateFunctionBindings(ExclusiveContext* cx, TokenStream& ts,
-                                                     LifoAlloc& alloc,
-                                                     MutableHandle<Bindings> bindings) const
-{
-    MOZ_ASSERT(sc->isFunctionBox());
-    MOZ_ASSERT(args_.length() < ARGNO_LIMIT);
+ParseContext<ParseHandler>::generateBindings(ExclusiveContext* cx, TokenStream& ts, LifoAlloc& alloc,
+                                             MutableHandle<Bindings> bindings) const
+{
     MOZ_ASSERT(vars_.length() + bodyLevelLexicals_.length() < LOCALNO_LIMIT);
 
     /*
      * Avoid pathological edge cases by explicitly limiting the total number of
      * bindings to what will fit in a uint32_t.
      */
     if (UINT32_MAX - args_.length() <= vars_.length() + bodyLevelLexicals_.length())
         return ts.reportError(JSMSG_TOO_MANY_LOCALS);
@@ -450,16 +448,38 @@ ParseContext<ParseHandler>::generateFunc
     return Bindings::initWithTemporaryStorage(cx, bindings, args_.length(), vars_.length(),
                                               bodyLevelLexicals_.length(), blockScopeDepth,
                                               numUnaliasedVars, numUnaliasedBodyLevelLexicals,
                                               packedBindings);
 }
 
 template <typename ParseHandler>
 bool
+ParseContext<ParseHandler>::generateFunctionBindings(ExclusiveContext* cx, TokenStream& ts,
+                                                     LifoAlloc& alloc,
+                                                     MutableHandle<Bindings> bindings) const
+{
+    MOZ_ASSERT(sc->isFunctionBox());
+    MOZ_ASSERT(args_.length() < ARGNO_LIMIT);
+    return generateBindings(cx, ts, alloc, bindings);
+}
+
+template <typename ParseHandler>
+bool
+ParseContext<ParseHandler>::generateModuleBindings(ExclusiveContext* cx, TokenStream& ts,
+                                                   LifoAlloc& alloc,
+                                                   MutableHandle<Bindings> bindings) const
+{
+    MOZ_ASSERT(sc->isModuleBox());
+    MOZ_ASSERT(args_.length() == 0);
+    return generateBindings(cx, ts, alloc, bindings);
+}
+
+template <typename ParseHandler>
+bool
 Parser<ParseHandler>::reportHelper(ParseReportKind kind, bool strict, uint32_t offset,
                                    unsigned errorNumber, va_list args)
 {
     bool result = false;
     switch (kind) {
       case ParseError:
         result = tokenStream.reportCompileErrorNumberVA(offset, JSREPORT_ERROR, errorNumber, args);
         break;
@@ -684,16 +704,59 @@ Parser<ParseHandler>::newFunctionBox(Nod
     traceListHead = funbox;
     if (fn)
         handler.setFunctionBox(fn, funbox);
 
     return funbox;
 }
 
 template <typename ParseHandler>
+ModuleBox::ModuleBox(ExclusiveContext* cx, ObjectBox* traceListHead, ModuleObject* module,
+                     ParseContext<ParseHandler>* outerpc)
+  : ObjectBox(module, traceListHead),
+    SharedContext(cx, Directives(true), extraWarnings),
+    bindings()
+{}
+
+template <typename ParseHandler>
+ModuleBox*
+Parser<ParseHandler>::newModuleBox(Node pn, HandleModuleObject module)
+{
+    MOZ_ASSERT(module);
+
+    /*
+     * We use JSContext.tempLifoAlloc to allocate parsed objects and place them
+     * on a list in this Parser to ensure GC safety. Thus the tempLifoAlloc
+     * arenas containing the entries must be alive until we are done with
+     * scanning, parsing and code generation for the whole module.
+     */
+    ParseContext<ParseHandler>* outerpc = nullptr;
+    ModuleBox* modbox =
+        alloc.new_<ModuleBox>(context, traceListHead, module, outerpc);
+    if (!modbox) {
+        ReportOutOfMemory(context);
+        return nullptr;
+    }
+
+    traceListHead = modbox;
+    if (pn)
+        handler.setModuleBox(pn, modbox);
+
+    return modbox;
+}
+
+template <>
+ModuleBox*
+Parser<SyntaxParseHandler>::newModuleBox(Node pn, HandleModuleObject module)
+{
+    MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+    return nullptr;
+}
+
+template <typename ParseHandler>
 void
 Parser<ParseHandler>::trace(JSTracer* trc)
 {
     traceListHead->trace(trc);
 }
 
 void
 MarkParser(JSTracer* trc, AutoGCRooter* parser)
@@ -780,16 +843,67 @@ Parser<ParseHandler>::checkStrictBinding
             return false;
         return report(ParseStrictError, pc->sc->strict(), pn,
                       JSMSG_BAD_BINDING, bytes.ptr());
     }
 
     return true;
 }
 
+template <typename ParseHandler>
+typename ParseHandler::Node
+Parser<ParseHandler>::standaloneModule(HandleModuleObject module)
+{
+    MOZ_ASSERT(checkOptionsCalled);
+
+    Node mn = handler.newModule();
+    if (!mn)
+        return null();
+
+    ModuleBox* modulebox = newModuleBox(mn, module);
+    if (!modulebox)
+        return null();
+    handler.setModuleBox(mn, modulebox);
+
+    ParseContext<FullParseHandler> modulepc(this, pc, mn, modulebox, nullptr, 0);
+    if (!modulepc.init(*this))
+        return null();
+
+    ParseNode* pn = statements(YieldIsKeyword);
+    if (!pn)
+        return null();
+
+    MOZ_ASSERT(pn->isKind(PNK_STATEMENTLIST));
+    mn->pn_body = pn;
+
+    TokenKind tt;
+    if (!tokenStream.getToken(&tt, TokenStream::Operand))
+        return null();
+    MOZ_ASSERT(tt == TOK_EOF);
+
+    if (!FoldConstants(context, &pn, this))
+        return null();
+
+    Rooted<Bindings> bindings(context, modulebox->bindings);
+    if (!modulepc.generateModuleBindings(context, tokenStream, alloc, &bindings))
+        return null();
+    modulebox->bindings = bindings;
+
+    MOZ_ASSERT(mn->pn_modulebox == modulebox);
+    return mn;
+}
+
+template <>
+SyntaxParseHandler::Node
+Parser<SyntaxParseHandler>::standaloneModule(HandleModuleObject module)
+{
+    MOZ_ALWAYS_FALSE(abortIfSyntaxParser());
+    return SyntaxParseHandler::NodeFailure;
+}
+
 template <>
 ParseNode*
 Parser<FullParseHandler>::standaloneFunctionBody(HandleFunction fun,
                                                  Handle<PropertyNameVector> formals,
                                                  GeneratorKind generatorKind,
                                                  Directives inheritedDirectives,
                                                  Directives* newDirectives,
                                                  HandleObject enclosingStaticScope)
--- a/js/src/frontend/Parser.h
+++ b/js/src/frontend/Parser.h
@@ -17,17 +17,19 @@
 #include "frontend/FullParseHandler.h"
 #include "frontend/ParseMaps.h"
 #include "frontend/ParseNode.h"
 #include "frontend/SharedContext.h"
 #include "frontend/SyntaxParseHandler.h"
 
 namespace js {
 
+class ModuleObject;
 class StaticFunctionBoxScopeObject;
+class StaticModuleBoxScopeObject;
 
 namespace frontend {
 
 struct StmtInfoPC : public StmtInfoBase
 {
     static const unsigned BlockIdLimit = 1 << ParseNode::NumBlockIdBits;
 
     StmtInfoPC*     enclosing;
@@ -192,33 +194,42 @@ struct MOZ_STACK_CLASS ParseContext : pu
 
     /* See the sad story in defineArg. */
     void prepareToAddDuplicateArg(HandlePropertyName name, DefinitionNode prevDecl);
 
     /* See the sad story in MakeDefIntoUse. */
     void updateDecl(JSAtom* atom, Node newDecl);
 
     /*
-     * After a function body has been parsed, the parser generates the
-     * function's "bindings". Bindings are a data-structure, ultimately stored
-     * in the compiled JSScript, that serve three purposes:
+     * After a function body or module has been parsed, the parser generates the
+     * code's "bindings". Bindings are a data-structure, ultimately stored in
+     * the compiled JSScript, that serve three purposes:
      *  - After parsing, the ParseContext is destroyed and 'decls' along with
      *    it. Mostly, the emitter just uses the binding information stored in
      *    the use/def nodes, but the emitter occasionally needs 'bindings' for
      *    various scope-related queries.
      *  - Bindings provide the initial js::Shape to use when creating a dynamic
-     *    scope object (js::CallObject) for the function. This shape is used
-     *    during dynamic name lookup.
+     *    scope object (js::CallObject). This shape is used during dynamic name
+     *    lookup.
      *  - Sometimes a script's bindings are accessed at runtime to retrieve the
      *    contents of the lexical scope (e.g., from the debugger).
      */
+  private:
+    bool generateBindings(ExclusiveContext* cx, TokenStream& ts, LifoAlloc& alloc,
+                          MutableHandle<Bindings> bindings) const;
+
+  public:
     bool generateFunctionBindings(ExclusiveContext* cx, TokenStream& ts,
                                   LifoAlloc& alloc,
                                   MutableHandle<Bindings> bindings) const;
 
+    bool generateModuleBindings(ExclusiveContext* cx, TokenStream& ts,
+                                LifoAlloc& alloc,
+                                MutableHandle<Bindings> bindings) const;
+
   private:
     ParseContext**  parserPC;     /* this points to the Parser's active pc
                                        and holds either |this| or one of
                                        |this|'s descendents */
 
     // Value for parserPC to restore at the end. Use 'parent' instead for
     // information about the parse chain, this may be nullptr if
     // parent != nullptr.
@@ -295,18 +306,23 @@ struct MOZ_STACK_CLASS ParseContext : pu
 
     // True if we are at the topmost level of a entire script or function body.
     // For example, while parsing this code we would encounter f1 and f2 at
     // body level, but we would not encounter f3 or f4 at body level:
     //
     //   function f1() { function f2() { } }
     //   if (cond) { function f3() { if (cond) { function f4() { } } } }
     //
-    bool atBodyLevel() { return !innermostStmt(); }
-    bool atGlobalLevel() { return atBodyLevel() && !sc->isFunctionBox() && !innermostScopeStmt(); }
+    bool atBodyLevel() {
+        return !innermostStmt();
+    }
+
+    bool atGlobalLevel() {
+        return atBodyLevel() && !sc->isFunctionBox() && !innermostScopeStmt();
+    }
 
     // True if this is the ParseContext for the body of a function created by
     // the Function constructor.
     bool isFunctionConstructorBody() const {
         return sc->isFunctionBox() && !parent && sc->asFunctionBox()->function()->isLambda();
     }
 
     inline bool useAsmOrInsideUseAsm() const {
@@ -485,16 +501,18 @@ class Parser : private JS::AutoGCRooter,
     // Use when the funbox should be linked to the outerpc's innermost scope.
     FunctionBox* newFunctionBox(Node fn, HandleFunction fun, ParseContext<ParseHandler>* outerpc,
                                 Directives directives, GeneratorKind generatorKind)
     {
         RootedObject enclosing(context, outerpc->innermostStaticScope());
         return newFunctionBox(fn, fun, outerpc, directives, generatorKind, enclosing);
     }
 
+    ModuleBox* newModuleBox(Node pn, HandleModuleObject module);
+
     /*
      * Create a new function object given a name (which is optional if this is
      * a function expression).
      */
     JSFunction* newFunction(HandleAtom atom, FunctionSyntaxKind kind, GeneratorKind generatorKind,
                             HandleObject proto);
 
     bool generateBlockId(JSObject* staticScope, uint32_t* blockIdOut) {
@@ -537,16 +555,18 @@ class Parser : private JS::AutoGCRooter,
     inline bool abortIfSyntaxParser();
 
   public:
     /* Public entry points for parsing. */
     Node statement(YieldHandling yieldHandling, bool canHaveDirectives = false);
 
     bool maybeParseDirective(Node list, Node pn, bool* cont);
 
+    Node standaloneModule(Handle<ModuleObject*> module);
+
     // Parse a function, given only its body. Used for the Function and
     // Generator constructors.
     Node standaloneFunctionBody(HandleFunction fun, Handle<PropertyNameVector> formals,
                                 GeneratorKind generatorKind,
                                 Directives inheritedDirectives, Directives* newDirectives,
                                 HandleObject enclosingStaticScope);
 
     // Parse a function, given only its arguments and body. Used for lazily
--- a/js/src/frontend/SharedContext.h
+++ b/js/src/frontend/SharedContext.h
@@ -8,16 +8,17 @@
 #define frontend_SharedContext_h
 
 #include "jsatom.h"
 #include "jsopcode.h"
 #include "jspubtd.h"
 #include "jsscript.h"
 #include "jstypes.h"
 
+#include "builtin/ModuleObject.h"
 #include "frontend/ParseMaps.h"
 #include "frontend/ParseNode.h"
 #include "frontend/TokenStream.h"
 #include "vm/ScopeObject.h"
 
 namespace js {
 namespace frontend {
 
@@ -210,18 +211,21 @@ class SharedContext
     // GlobalSharedContexts are stack allocated and thus may use RootedObject
     // for the static scope. FunctionBoxes are LifoAlloc'd and need to
     // manually trace their static scope.
     virtual JSObject* staticScope() const = 0;
     void computeAllowSyntax(JSObject* staticScope);
     void computeInWith(JSObject* staticScope);
 
     virtual ObjectBox* toObjectBox() { return nullptr; }
-    inline bool isFunctionBox() { return toObjectBox() && toObjectBox()->isFunctionBox(); }
+    bool isObjectBox() { return toObjectBox() != nullptr; }
+    bool isFunctionBox() { return isObjectBox() && toObjectBox()->isFunctionBox(); }
     inline FunctionBox* asFunctionBox();
+    bool isModuleBox() { return isObjectBox() && toObjectBox()->isModuleBox(); }
+    inline ModuleBox* asModuleBox();
 
     bool allowNewTarget()              const { return allowNewTarget_; }
     bool allowSuperProperty()          const { return allowSuperProperty_; }
     bool inWith()                      const { return inWith_; }
     void markSuperScopeNeedsHomeObject();
 
     bool hasExplicitUseStrict()        const { return anyCxFlags.hasExplicitUseStrict; }
     bool bindingsAccessedDynamically() const { return anyCxFlags.bindingsAccessedDynamically; }
@@ -363,23 +367,44 @@ class FunctionBox : public ObjectBox, pu
         return bindings.hasAnyAliasedBindings() ||
                hasExtensibleScope() ||
                needsDeclEnvObject() ||
                needsHomeObject()    ||
                isGenerator();
     }
 };
 
+class ModuleBox : public ObjectBox, public SharedContext
+{
+  public:
+    Bindings bindings;
+
+    template <typename ParseHandler>
+    ModuleBox(ExclusiveContext* cx, ObjectBox* traceListHead, ModuleObject* module,
+              ParseContext<ParseHandler>* pc);
+
+    ObjectBox* toObjectBox() override { return this; }
+    ModuleObject* module() const { return &object->as<ModuleObject>(); }
+    JSObject* staticScope() const override { return module(); }
+};
+
 inline FunctionBox*
 SharedContext::asFunctionBox()
 {
     MOZ_ASSERT(isFunctionBox());
     return static_cast<FunctionBox*>(this);
 }
 
+inline ModuleBox*
+SharedContext::asModuleBox()
+{
+    MOZ_ASSERT(isModuleBox());
+    return static_cast<ModuleBox*>(this);
+}
+
 
 // In generators, we treat all locals as aliased so that they get stored on the
 // heap.  This way there is less information to copy off the stack when
 // suspending, and back on when resuming.  It also avoids the need to create and
 // invalidate DebugScope proxies for unaliased locals in a generator frame, as
 // the generator frame will be copied out to the heap and released only by GC.
 inline bool
 SharedContext::allLocalsAliased()
--- a/js/src/gc/Marking.cpp
+++ b/js/src/gc/Marking.cpp
@@ -356,16 +356,17 @@ AssertRootMarkingPhase(JSTracer* trc)
     D(ArgumentsObject*) \
     D(ArrayBufferObject*) \
     D(ArrayBufferObjectMaybeShared*) \
     D(ArrayBufferViewObject*) \
     D(DebugScopeObject*) \
     D(GlobalObject*) \
     D(JSObject*) \
     D(JSFunction*) \
+    D(ModuleObject*)      \
     D(NestedScopeObject*) \
     D(PlainObject*) \
     D(SavedFrame*) \
     D(ScopeObject*) \
     D(ScriptSourceObject*) \
     D(SharedArrayBufferObject*) \
     D(SharedTypedArrayObject*) \
     D(JSScript*) \
new file mode 100644
--- /dev/null
+++ b/js/src/jit-test/tests/modules/shell-parse.js
@@ -0,0 +1,4 @@
+// Exercise shell parseModule function.
+
+parseModule("");
+parseModule("const foo = 1;");
--- a/js/src/jsscript.cpp
+++ b/js/src/jsscript.cpp
@@ -2647,16 +2647,35 @@ JSScript::linkToFunctionFromEmitter(js::
     script->setFunction(fun);
 
     if (fun->isInterpretedLazy())
         fun->setUnlazifiedScript(script);
     else
         fun->setScript(script);
 }
 
+/* static */ void
+JSScript::linkToModuleFromEmitter(js::ExclusiveContext* cx, JS::Handle<JSScript*> script,
+                                    js::frontend::ModuleBox* modulebox)
+{
+    script->funHasExtensibleScope_ = false;
+    script->funNeedsDeclEnvObject_ = false;
+    script->needsHomeObject_ = false;
+    script->isDerivedClassConstructor_ = false;
+    script->funLength_ = 0;
+
+    script->isGeneratorExp_ = false;
+    script->setGeneratorKind(NotGenerator);
+
+    // Link the module and the script to each other, so that StaticScopeIter
+    // may walk the scope chain of currently compiling scripts.
+    RootedModuleObject module(cx, modulebox->module());
+    script->setModule(module);
+}
+
 /* static */ bool
 JSScript::fullyInitFromEmitter(ExclusiveContext* cx, HandleScript script, BytecodeEmitter* bce)
 {
     /* The counts of indexed things must be checked during code generation. */
     MOZ_ASSERT(bce->atomIndices->count() <= INDEX_LIMIT);
     MOZ_ASSERT(bce->objectList.length <= INDEX_LIMIT);
     MOZ_ASSERT(bce->regexpList.length <= INDEX_LIMIT);
 
@@ -3590,16 +3609,19 @@ JSScript::traceChildren(JSTracer* trc)
     if (sourceObject()) {
         MOZ_ASSERT(MaybeForwarded(sourceObject())->compartment() == compartment());
         TraceEdge(trc, &sourceObject_, "sourceObject");
     }
 
     if (functionNonDelazifying())
         TraceEdge(trc, &function_, "function");
 
+    if (module_)
+        TraceEdge(trc, &module_, "module");
+
     if (enclosingStaticScope_)
         TraceEdge(trc, &enclosingStaticScope_, "enclosingStaticScope");
 
     if (maybeLazyScript())
         TraceManuallyBarrieredEdge(trc, &lazyScript, "lazyScript");
 
     if (trc->isMarkingTracer()) {
         compartment()->mark();
--- a/js/src/jsscript.h
+++ b/js/src/jsscript.h
@@ -13,16 +13,17 @@
 #include "mozilla/PodOperations.h"
 #include "mozilla/UniquePtr.h"
 
 #include "jsatom.h"
 #include "jslock.h"
 #include "jsopcode.h"
 #include "jstypes.h"
 
+#include "builtin/ModuleObject.h"
 #include "gc/Barrier.h"
 #include "gc/Rooting.h"
 #include "jit/IonCode.h"
 #include "js/UbiNode.h"
 #include "vm/NativeObject.h"
 #include "vm/Shape.h"
 
 namespace JS {
@@ -41,25 +42,26 @@ namespace jit {
 # define ION_PENDING_SCRIPT ((js::jit::IonScript*)0x3)
 
 # define BASELINE_DISABLED_SCRIPT ((js::jit::BaselineScript*)0x1)
 
 class BreakpointSite;
 class BindingIter;
 class Debugger;
 class LazyScript;
+class NestedScopeObject;
 class RegExpObject;
 struct SourceCompressionTask;
 class Shape;
-class NestedScopeObject;
 
 namespace frontend {
     struct BytecodeEmitter;
     class UpvarCookie;
     class FunctionBox;
+    class ModuleBox;
 } // namespace frontend
 
 namespace detail {
 
 // Do not call this directly! It is exposed for the friend declarations in
 // this file.
 bool
 CopyScript(JSContext* cx, HandleObject scriptStaticScope, HandleScript src, HandleScript dst);
@@ -944,16 +946,17 @@ class JSScript : public js::gc::TenuredC
 
     // This script's ScriptSourceObject, or a CCW thereof.
     //
     // (When we clone a JSScript into a new compartment, we don't clone its
     // source object. Instead, the clone refers to a wrapper.)
     js::HeapPtrObject sourceObject_;
 
     js::HeapPtrFunction function_;
+    js::HeapPtr<js::ModuleObject*> module_;
     js::HeapPtrObject   enclosingStaticScope_;
 
     /*
      * Information attached by Ion. Nexto a valid IonScript this could be
      * ION_DISABLED_SCRIPT, ION_COMPILING_SCRIPT or ION_PENDING_SCRIPT.
      * The later is a ion compilation that is ready, but hasn't been linked
      * yet.
      */
@@ -1134,17 +1137,17 @@ class JSScript : public js::gc::TenuredC
     bool needsHomeObject_:1;
 
     bool isDerivedClassConstructor_:1;
 
     // Add padding so JSScript is gc::Cell aligned. Make padding protected
     // instead of private to suppress -Wunused-private-field compiler warnings.
   protected:
 #if JS_BITS_PER_WORD == 32
-    uint32_t padding;
+    // No padding currently required.
 #endif
 
     //
     // End of fields.  Start methods.
     //
 
   public:
     static JSScript* Create(js::ExclusiveContext* cx,
@@ -1162,16 +1165,18 @@ class JSScript : public js::gc::TenuredC
     static bool partiallyInit(js::ExclusiveContext* cx, JS::Handle<JSScript*> script,
                               uint32_t nconsts, uint32_t nobjects, uint32_t nregexps,
                               uint32_t ntrynotes, uint32_t nblockscopes, uint32_t nyieldoffsets,
                               uint32_t nTypeSets);
     static bool fullyInitFromEmitter(js::ExclusiveContext* cx, JS::Handle<JSScript*> script,
                                      js::frontend::BytecodeEmitter* bce);
     static void linkToFunctionFromEmitter(js::ExclusiveContext* cx, JS::Handle<JSScript*> script,
                                           js::frontend::FunctionBox* funbox);
+    static void linkToModuleFromEmitter(js::ExclusiveContext* cx, JS::Handle<JSScript*> script,
+                                        js::frontend::ModuleBox* funbox);
     // Initialize a no-op script.
     static bool fullyInitTrivial(js::ExclusiveContext* cx, JS::Handle<JSScript*> script);
 
     inline JSPrincipals* principals();
 
     JSCompartment* compartment() const { return compartment_; }
     JSCompartment* maybeCompartment() const { return compartment(); }
 
@@ -1542,16 +1547,21 @@ class JSScript : public js::gc::TenuredC
     }
     inline void setFunction(JSFunction* fun);
     /*
      * De-lazifies the canonical function. Must be called before entering code
      * that expects the function to be non-lazy.
      */
     inline void ensureNonLazyCanonicalFunction(JSContext* cx);
 
+    js::ModuleObject* module() const {
+        return module_;
+    }
+    inline void setModule(js::ModuleObject* module);
+
     JSFlatString* sourceData(JSContext* cx);
 
     static bool loadSource(JSContext* cx, js::ScriptSource* ss, bool* worked);
 
     void setSourceObject(JSObject* object);
     JSObject* sourceObject() const {
         return sourceObject_;
     }
--- a/js/src/jsscriptinlines.h
+++ b/js/src/jsscriptinlines.h
@@ -62,21 +62,29 @@ JSScript::functionDelazifying() const
             lazyScript->initScript(const_cast<JSScript*>(this));
     }
     return function_;
 }
 
 inline void
 JSScript::setFunction(JSFunction* fun)
 {
+    MOZ_ASSERT(!function_ && !module_);
     MOZ_ASSERT(fun->isTenured());
     function_ = fun;
 }
 
 inline void
+JSScript::setModule(js::ModuleObject* module)
+{
+    MOZ_ASSERT(!function_ && !module_);
+    module_ = module;
+}
+
+inline void
 JSScript::ensureNonLazyCanonicalFunction(JSContext* cx)
 {
     // Infallibly delazify the canonical script.
     if (function_ && function_->isInterpretedLazy())
         functionDelazifying();
 }
 
 inline JSFunction*
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -142,16 +142,17 @@ UNIFIED_SOURCES += [
     'asmjs/AsmJSLink.cpp',
     'asmjs/AsmJSModule.cpp',
     'asmjs/AsmJSSignalHandlers.cpp',
     'asmjs/AsmJSValidate.cpp',
     'builtin/AtomicsObject.cpp',
     'builtin/Eval.cpp',
     'builtin/Intl.cpp',
     'builtin/MapObject.cpp',
+    'builtin/ModuleObject.cpp',
     'builtin/Object.cpp',
     'builtin/Profilers.cpp',
     'builtin/Reflect.cpp',
     'builtin/ReflectParse.cpp',
     'builtin/SIMD.cpp',
     'builtin/SymbolObject.cpp',
     'builtin/TestingFunctions.cpp',
     'builtin/TypedObject.cpp',
--- a/js/src/shell/js.cpp
+++ b/js/src/shell/js.cpp
@@ -51,16 +51,17 @@
 #include "jsscript.h"
 #include "jstypes.h"
 #include "jsutil.h"
 #ifdef XP_WIN
 # include "jswin.h"
 #endif
 #include "jswrapper.h"
 
+#include "builtin/ModuleObject.h"
 #include "builtin/TestingFunctions.h"
 #include "frontend/Parser.h"
 #include "gc/GCInternals.h"
 #include "jit/arm/Simulator-arm.h"
 #include "jit/Ion.h"
 #include "jit/JitcodeMap.h"
 #include "jit/OptimizationTracking.h"
 #include "js/Debug.h"
@@ -2062,17 +2063,22 @@ DisassembleToSprinter(JSContext* cx, uns
                 return false;
             SrcNotes(cx, script, sprinter);
             TryNotes(cx, script, sprinter);
             BlockNotes(cx, script, sprinter);
         }
     } else {
         for (unsigned i = 0; i < p.argc; i++) {
             RootedFunction fun(cx);
-            RootedScript script (cx, ValueToScript(cx, p.argv[i], fun.address()));
+            RootedScript script(cx);
+            RootedValue value(cx, p.argv[i]);
+            if (value.isObject() && value.toObject().is<ModuleObject>())
+                script = value.toObject().as<ModuleObject>().script();
+            else
+                script = ValueToScript(cx, value, fun.address());
             if (!script)
                 return false;
             if (!DisassembleScript(cx, script, fun, p.lines, p.recursive, sprinter))
                 return false;
         }
     }
     return true;
 }
@@ -3054,16 +3060,55 @@ Compile(JSContext* cx, unsigned argc, Va
     RootedScript script(cx);
     const char16_t* chars = stableChars.twoByteRange().start().get();
     bool ok = JS_CompileUCScript(cx, chars, scriptContents->length(), options, &script);
     args.rval().setUndefined();
     return ok;
 }
 
 static bool
+ParseModule(JSContext* cx, unsigned argc, Value* vp)
+{
+    CallArgs args = CallArgsFromVp(argc, vp);
+    if (args.length() < 1) {
+        JS_ReportErrorNumber(cx, GetErrorMessage, nullptr,
+                             JSMSG_MORE_ARGS_NEEDED, "parseModule", "0", "s");
+        return false;
+    }
+    if (!args[0].isString()) {
+        JS_ReportError(cx, "expected string to compile, got %s",
+                       JS_TypeOfValue(cx, args[0]));
+        return false;
+    }
+
+    RootedObject global(cx, JS::CurrentGlobalOrNull(cx));
+    JSFlatString* scriptContents = args[0].toString()->ensureFlat(cx);
+    if (!scriptContents)
+        return false;
+
+    CompileOptions options(cx);
+    options.setFileAndLine("<string>", 1);
+
+    AutoStableStringChars stableChars(cx);
+    if (!stableChars.initTwoByte(cx, scriptContents))
+        return false;
+
+    const char16_t* chars = stableChars.twoByteRange().start().get();
+    SourceBufferHolder srcBuf(chars, scriptContents->length(),
+                              SourceBufferHolder::NoOwnership);
+
+    RootedObject module(cx, frontend::CompileModule(cx, global, options, srcBuf));
+    if (!module)
+        return false;
+
+    args.rval().setObject(*module);
+    return true;
+}
+
+static bool
 Parse(JSContext* cx, unsigned argc, Value* vp)
 {
     using namespace js::frontend;
 
     CallArgs args = CallArgsFromVp(argc, vp);
 
     if (args.length() < 1) {
         JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_MORE_ARGS_NEEDED,
@@ -4601,16 +4646,20 @@ static const JSFunctionSpecWithHelp shel
     JS_FN_HELP("sleep", Sleep_fn, 1, 0,
 "sleep(dt)",
 "  Sleep for dt seconds."),
 
     JS_FN_HELP("compile", Compile, 1, 0,
 "compile(code)",
 "  Compiles a string to bytecode, potentially throwing."),
 
+    JS_FN_HELP("parseModule", ParseModule, 1, 0,
+"parseModule(code)",
+"  Parses source text as a module and returns a Module object."),
+
     JS_FN_HELP("parse", Parse, 1, 0,
 "parse(code)",
 "  Parses a string, potentially throwing."),
 
     JS_FN_HELP("syntaxParse", SyntaxParse, 1, 0,
 "syntaxParse(code)",
 "  Check the syntax of a string, returning success value"),
 
--- a/js/src/vm/ScopeObject.h
+++ b/js/src/vm/ScopeObject.h
@@ -16,16 +16,17 @@
 #include "vm/ProxyObject.h"
 #include "vm/WeakMapObject.h"
 
 namespace js {
 
 namespace frontend {
 struct Definition;
 class FunctionBox;
+class ModuleBox;
 }
 
 class StaticWithObject;
 class StaticEvalObject;
 class StaticNonSyntacticScopeObjects;
 
 /*****************************************************************************/
 
@@ -42,48 +43,52 @@ class StaticNonSyntacticScopeObjects;
  * The following are syntactic static scopes:
  *
  * StaticBlockObject
  *   Scope for non-function body blocks. e.g., |{ let x; }|
  *
  * JSFunction
  *   Scope for function bodies. e.g., |function f() { var x; let y; }|
  *
+ * ModuleObject
+ *   Scope for moddules.
+ *
  * StaticWithObject
  *   Scope for |with|. e.g., |with ({}) { ... }|
  *
  * StaticEvalObject
  *   Scope for |eval|. e.g., |eval(...)|
  *
  * The following are non-syntactic static scopes:
  *
  * StaticNonSyntacticScopeObjects
  *   Signals presence of "polluting" scope objects. Used by Gecko.
  *
  * There is an additional scope for named lambdas without a static scope
  * object. E.g., in:
  *
  *   (function f() { var x; function g() { } })
  *
- * All static scope objects are ScopeObjects with the exception of
- * JSFunction. A JSFunction keeps its enclosing scope link on
+ * All static scope objects are ScopeObjects with the exception of JSFunction
+ * and ModuleObject, which keeps their enclosing scope link on
  * |JSScript::enclosingStaticScope()|.
  */
 template <AllowGC allowGC>
 class StaticScopeIter
 {
     typename MaybeRooted<JSObject*, allowGC>::RootType obj;
     bool onNamedLambda;
 
     static bool IsStaticScope(JSObject* obj) {
         return obj->is<StaticBlockObject>() ||
                obj->is<StaticWithObject>() ||
                obj->is<StaticEvalObject>() ||
                obj->is<StaticNonSyntacticScopeObjects>() ||
-               obj->is<JSFunction>();
+               obj->is<JSFunction>() ||
+               obj->is<ModuleObject>();
     }
 
   public:
     StaticScopeIter(ExclusiveContext* cx, JSObject* obj)
       : obj(cx, obj), onNamedLambda(false)
     {
         static_assert(allowGC == CanGC,
                       "the context-accepting constructor should only be used "
--- a/js/src/vm/TraceLogging.cpp
+++ b/js/src/vm/TraceLogging.cpp
@@ -667,16 +667,17 @@ TraceLoggerThreadState::init()
         enabledTextIds[TraceLogger_Interpreter] = true;
         enabledTextIds[TraceLogger_IonCompilation] = true;
         enabledTextIds[TraceLogger_IonLinking] = true;
         enabledTextIds[TraceLogger_IonMonkey] = true;
         enabledTextIds[TraceLogger_MinorGC] = true;
         enabledTextIds[TraceLogger_ParserCompileFunction] = true;
         enabledTextIds[TraceLogger_ParserCompileLazy] = true;
         enabledTextIds[TraceLogger_ParserCompileScript] = true;
+        enabledTextIds[TraceLogger_ParserCompileModule] = true;
         enabledTextIds[TraceLogger_IrregexpCompile] = true;
         enabledTextIds[TraceLogger_IrregexpExecute] = true;
         enabledTextIds[TraceLogger_Scripts] = true;
         enabledTextIds[TraceLogger_Engine] = true;
     }
 
     if (ContainsFlag(env, "IonCompiler")) {
         enabledTextIds[TraceLogger_IonCompilation] = true;
--- a/js/src/vm/TraceLoggingTypes.h
+++ b/js/src/vm/TraceLoggingTypes.h
@@ -27,16 +27,17 @@
     _(IonLinking)                                     \
     _(IonMonkey)                                      \
     _(IrregexpCompile)                                \
     _(IrregexpExecute)                                \
     _(MinorGC)                                        \
     _(ParserCompileFunction)                          \
     _(ParserCompileLazy)                              \
     _(ParserCompileScript)                            \
+    _(ParserCompileModule)                            \
     _(Scripts)                                        \
     _(VM)                                             \
                                                       \
     /* Specific passes during ion compilation */      \
     _(FoldTests)                                      \
     _(SplitCriticalEdges)                             \
     _(RenumberBlocks)                                 \
     _(ScalarReplacement)                              \