Bug 1588861 - Part 3: Add support for xdr encoding modules into xdr cache. r=iain,tcampbell
authorcaroline <cullen.caroline@gmail.com>
Tue, 10 Dec 2019 02:52:37 +0000
changeset 506302 2a0aacbd11dee6907445c902aa088ab4b9492d6b
parent 506301 639290e9ff226088f9d3e1d5f710e6c4a1cde03e
child 506303 b622095c76ab52fdd32c1fa41a1541f3c8b49d01
push id102799
push useriireland@mozilla.com
push dateTue, 10 Dec 2019 18:22:40 +0000
treeherderautoland@815a452c73af [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersiain, tcampbell
bugs1588861
milestone73.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 1588861 - Part 3: Add support for xdr encoding modules into xdr cache. r=iain,tcampbell Differential Revision: https://phabricator.services.mozilla.com/D54158
js/src/builtin/ModuleObject.cpp
js/src/builtin/ModuleObject.h
js/src/frontend/BytecodeCompiler.cpp
js/src/gc/Rooting.h
js/src/vm/JSAtom.cpp
js/src/vm/JSAtom.h
js/src/vm/JSScript.cpp
js/src/vm/JSScript.h
js/src/vm/Scope.cpp
js/src/vm/Scope.h
js/src/vm/Xdr.cpp
js/src/vm/Xdr.h
--- a/js/src/builtin/ModuleObject.cpp
+++ b/js/src/builtin/ModuleObject.cpp
@@ -1092,16 +1092,28 @@ ModuleNamespaceObject* ModuleObject::cre
   if (!ns) {
     return nullptr;
   }
 
   self->initReservedSlot(NamespaceSlot, ObjectValue(*ns));
   return ns;
 }
 
+/* static */
+bool ModuleObject::createEnvironment(JSContext* cx, HandleModuleObject self) {
+  RootedModuleEnvironmentObject env(cx,
+                                    ModuleEnvironmentObject::create(cx, self));
+  if (!env) {
+    return false;
+  }
+
+  self->setInitialEnvironment(env);
+  return true;
+}
+
 static bool InvokeSelfHostedMethod(JSContext* cx, HandleModuleObject self,
                                    HandlePropertyName name) {
   RootedValue thisv(cx, ObjectValue(*self));
   FixedInvokeArgs<0> args(cx);
 
   RootedValue ignored(cx);
   return CallSelfHostedFunction(cx, name, thisv, args, &ignored);
 }
@@ -1796,8 +1808,303 @@ bool js::FinishDynamicModuleImport(JSCon
   RootedObject ns(cx, ModuleObject::GetOrCreateModuleNamespace(cx, module));
   if (!ns) {
     return RejectPromiseWithPendingError(cx, promise);
   }
 
   RootedValue value(cx, ObjectValue(*ns));
   return PromiseObject::resolve(cx, promise, value);
 }
+
+template <XDRMode mode>
+XDRResult js::XDRExportEntries(XDRState<mode>* xdr,
+                               MutableHandleArrayObject vec) {
+  JSContext* cx = xdr->cx();
+  Rooted<GCVector<ExportEntryObject*>> expVec(cx);
+  RootedExportEntryObject expObj(cx);
+  RootedAtom exportName(cx);
+  RootedAtom moduleRequest(cx);
+  RootedAtom importName(cx);
+  RootedAtom localName(cx);
+
+  uint32_t length = 0;
+  uint32_t lineNumber = 0;
+  uint32_t columnNumber = 0;
+
+  if (mode == XDR_ENCODE) {
+    length = vec->length();
+  }
+  MOZ_TRY(xdr->codeUint32(&length));
+  for (uint32_t i = 0; i < length; i++) {
+    if (mode == XDR_ENCODE) {
+      expObj = &vec->getDenseElement(i).toObject().as<ExportEntryObject>();
+
+      exportName = expObj->exportName();
+      moduleRequest = expObj->moduleRequest();
+      importName = expObj->importName();
+      localName = expObj->localName();
+      lineNumber = expObj->lineNumber();
+      columnNumber = expObj->columnNumber();
+    }
+
+    MOZ_TRY(XDRAtomOrNull(xdr, &exportName));
+    MOZ_TRY(XDRAtomOrNull(xdr, &moduleRequest));
+    MOZ_TRY(XDRAtomOrNull(xdr, &importName));
+    MOZ_TRY(XDRAtomOrNull(xdr, &localName));
+
+    MOZ_TRY(xdr->codeUint32(&lineNumber));
+    MOZ_TRY(xdr->codeUint32(&columnNumber));
+
+    if (mode == XDR_DECODE) {
+      expObj.set(ExportEntryObject::create(cx, exportName, moduleRequest,
+                                           importName, localName, lineNumber,
+                                           columnNumber));
+      if (!expObj) {
+        return xdr->fail(JS::TranscodeResult_Throw);
+      }
+      if (!expVec.append(expObj)) {
+        return xdr->fail(JS::TranscodeResult_Throw);
+      }
+    }
+  }
+
+  if (mode == XDR_DECODE) {
+    RootedArrayObject expArr(cx, js::CreateArray(cx, expVec));
+    if (!expArr) {
+      return xdr->fail(JS::TranscodeResult_Throw);
+    }
+    vec.set(expArr);
+  }
+
+  return Ok();
+}
+
+template <XDRMode mode>
+XDRResult js::XDRRequestedModuleObject(
+    XDRState<mode>* xdr, MutableHandleRequestedModuleObject reqObj) {
+  JSContext* cx = xdr->cx();
+  RootedAtom moduleSpecifier(cx);
+  uint32_t lineNumber = 0;
+  uint32_t columnNumber = 0;
+  if (mode == XDR_ENCODE) {
+    moduleSpecifier = reqObj->moduleSpecifier();
+    lineNumber = reqObj->lineNumber();
+    columnNumber = reqObj->columnNumber();
+  }
+
+  MOZ_TRY(XDRAtom(xdr, &moduleSpecifier));
+  MOZ_TRY(xdr->codeUint32(&lineNumber));
+  MOZ_TRY(xdr->codeUint32(&columnNumber));
+
+  if (mode == XDR_DECODE) {
+    reqObj.set(RequestedModuleObject::create(cx, moduleSpecifier, lineNumber,
+                                             columnNumber));
+    if (!reqObj) {
+      return xdr->fail(JS::TranscodeResult_Throw);
+    }
+  }
+
+  return Ok();
+}
+
+template <XDRMode mode>
+XDRResult js::XDRImportEntryObject(XDRState<mode>* xdr,
+                                   MutableHandleImportEntryObject impObj) {
+  JSContext* cx = xdr->cx();
+  RootedAtom moduleRequest(cx);
+  RootedAtom importName(cx);
+  RootedAtom localName(cx);
+  uint32_t lineNumber = 0;
+  uint32_t columnNumber = 0;
+  if (mode == XDR_ENCODE) {
+    moduleRequest = impObj->moduleRequest();
+    importName = impObj->importName();
+    localName = impObj->localName();
+    lineNumber = impObj->lineNumber();
+    columnNumber = impObj->columnNumber();
+  }
+
+  MOZ_TRY(XDRAtomOrNull(xdr, &moduleRequest));
+  MOZ_TRY(XDRAtomOrNull(xdr, &importName));
+  MOZ_TRY(XDRAtomOrNull(xdr, &localName));
+  MOZ_TRY(xdr->codeUint32(&lineNumber));
+  MOZ_TRY(xdr->codeUint32(&columnNumber));
+
+  if (mode == XDR_DECODE) {
+    impObj.set(ImportEntryObject::create(cx, moduleRequest, importName,
+                                         localName, lineNumber, columnNumber));
+    if (!impObj) {
+      return xdr->fail(JS::TranscodeResult_Throw);
+    }
+  }
+
+  return Ok();
+}
+
+template <XDRMode mode>
+XDRResult js::XDRModuleObject(XDRState<mode>* xdr,
+                              MutableHandleModuleObject modp) {
+  JSContext* cx = xdr->cx();
+  RootedModuleObject module(cx, modp);
+
+  RootedScope enclosingScope(cx);
+  RootedScript script(cx);
+
+  RootedArrayObject requestedModules(cx);
+  RootedArrayObject importEntries(cx);
+  RootedArrayObject localExportEntries(cx);
+  RootedArrayObject indirectExportEntries(cx);
+  RootedArrayObject starExportEntries(cx);
+  // funcDecls points to data traced by the ModuleObject,
+  // but is itself heap-allocated so we don't need to
+  // worry about rooting it again here.
+  FunctionDeclarationVector* funcDecls;
+
+  uint32_t requestedModulesLength = 0;
+  uint32_t importEntriesLength = 0;
+  uint32_t funcDeclLength = 0;
+
+  if (mode == XDR_ENCODE) {
+    module = modp.get();
+
+    script.set(module->script());
+    enclosingScope.set(module->enclosingScope());
+    MOZ_ASSERT(!enclosingScope->as<GlobalScope>().hasBindings());
+
+    requestedModules = &module->requestedModules();
+    importEntries = &module->importEntries();
+    localExportEntries = &module->localExportEntries();
+    indirectExportEntries = &module->indirectExportEntries();
+    starExportEntries = &module->starExportEntries();
+    funcDecls = module->functionDeclarations();
+
+    requestedModulesLength = requestedModules->length();
+    importEntriesLength = importEntries->length();
+    funcDeclLength = funcDecls->length();
+  }
+
+  /* ScriptSourceObject slot - ScriptSourceObject is created in XDRScript and is
+   * set when init is called. */
+  if (mode == XDR_DECODE) {
+    enclosingScope.set(&cx->global()->emptyGlobalScope());
+    module.set(ModuleObject::create(cx));
+    if (!module) {
+      return xdr->fail(JS::TranscodeResult_Throw);
+    }
+  }
+
+  /* Script slot */
+  MOZ_TRY(XDRScript(xdr, enclosingScope, nullptr, module, &script));
+
+  if (mode == XDR_DECODE) {
+    module->init(script);
+  }
+
+  /* Environment Slot */
+  if (mode == XDR_DECODE) {
+    if (!ModuleObject::createEnvironment(cx, module)) {
+      return xdr->fail(JS::TranscodeResult_Throw);
+    }
+  }
+
+  /* Namespace Slot, Status Slot, EvaluationErrorSlot, MetaObject - Initialized
+   * at instantiation */
+
+  /* RequestedModules slot */
+  RootedRequestedModuleVector reqVec(cx, GCVector<RequestedModuleObject*>(cx));
+  RootedRequestedModuleObject reqObj(cx);
+  MOZ_TRY(xdr->codeUint32(&requestedModulesLength));
+  for (uint32_t i = 0; i < requestedModulesLength; i++) {
+    if (mode == XDR_ENCODE) {
+      reqObj = &module->requestedModules()
+                    .getDenseElement(i)
+                    .toObject()
+                    .as<RequestedModuleObject>();
+    }
+    MOZ_TRY(XDRRequestedModuleObject(xdr, &reqObj));
+    if (mode == XDR_DECODE) {
+      if (!reqVec.append(reqObj)) {
+        return xdr->fail(JS::TranscodeResult_Throw);
+      }
+    }
+  }
+  if (mode == XDR_DECODE) {
+    RootedArrayObject reqArr(cx, js::CreateArray(cx, reqVec));
+    if (!reqArr) {
+      return xdr->fail(JS::TranscodeResult_Throw);
+    }
+    requestedModules.set(reqArr);
+  }
+
+  /* ImportEntries slot */
+  RootedImportEntryVector impVec(cx, GCVector<ImportEntryObject*>(cx));
+  RootedImportEntryObject impObj(cx);
+  MOZ_TRY(xdr->codeUint32(&importEntriesLength));
+  for (uint32_t i = 0; i < importEntriesLength; i++) {
+    if (mode == XDR_ENCODE) {
+      impObj = &module->importEntries()
+                    .getDenseElement(i)
+                    .toObject()
+                    .as<ImportEntryObject>();
+    }
+    MOZ_TRY(XDRImportEntryObject(xdr, &impObj));
+    if (mode == XDR_DECODE) {
+      if (!impVec.append(impObj)) {
+        return xdr->fail(JS::TranscodeResult_Throw);
+      }
+    }
+  }
+
+  if (mode == XDR_DECODE) {
+    RootedArrayObject impArr(cx, js::CreateArray(cx, impVec));
+    if (!impArr) {
+      return xdr->fail(JS::TranscodeResult_Throw);
+    }
+    importEntries.set(impArr);
+  }
+
+  /* LocalExportEntries slot */
+  MOZ_TRY(XDRExportEntries(xdr, &localExportEntries));
+  /* IndirectExportEntries slot */
+  MOZ_TRY(XDRExportEntries(xdr, &indirectExportEntries));
+  /* StarExportEntries slot */
+  MOZ_TRY(XDRExportEntries(xdr, &starExportEntries));
+
+  /* FunctionDeclarations slot */
+  RootedAtom funcName(cx);
+  uint32_t funIndex = 0;
+  MOZ_TRY(xdr->codeUint32(&funcDeclLength));
+  for (uint32_t i = 0; i < funcDeclLength; i++) {
+    if (mode == XDR_ENCODE) {
+      FunctionDeclaration fd = (*funcDecls)[i];
+      funcName = fd.name;
+      funIndex = fd.funIndex;
+    }
+
+    MOZ_TRY(XDRAtom(xdr, &funcName));
+    MOZ_TRY(xdr->codeUint32(&funIndex));
+
+    if (mode == XDR_DECODE) {
+      FunctionDeclaration funcDecl(funcName, funIndex);
+      if (!module->functionDeclarations()->append(funcDecl)) {
+        ReportOutOfMemory(cx);
+        return xdr->fail(JS::TranscodeResult_Throw);
+      }
+    }
+  }
+
+  /* ImportBindings slot, DFSIndex slot, DFSAncestorIndex slot -
+   * Initialized at instantiation */
+  if (mode == XDR_DECODE) {
+    module->initImportExportData(requestedModules, importEntries,
+                                 localExportEntries, indirectExportEntries,
+                                 starExportEntries);
+  }
+
+  modp.set(module);
+  return Ok();
+}
+
+template XDRResult js::XDRModuleObject(XDRState<XDR_ENCODE>* xdr,
+                                       MutableHandleModuleObject scriptp);
+
+template XDRResult js::XDRModuleObject(XDRState<XDR_DECODE>* xdr,
+                                       MutableHandleModuleObject scriptp);
--- a/js/src/builtin/ModuleObject.h
+++ b/js/src/builtin/ModuleObject.h
@@ -50,16 +50,22 @@ class ImportEntryObject : public NativeO
   JSAtom* importName() const;
   JSAtom* localName() const;
   uint32_t lineNumber() const;
   uint32_t columnNumber() const;
 };
 
 typedef Rooted<ImportEntryObject*> RootedImportEntryObject;
 typedef Handle<ImportEntryObject*> HandleImportEntryObject;
+typedef Rooted<GCVector<ImportEntryObject*>> RootedImportEntryVector;
+typedef MutableHandle<ImportEntryObject*> MutableHandleImportEntryObject;
+
+template <XDRMode mode>
+XDRResult XDRImportEntryObject(XDRState<mode>* xdr,
+                               MutableHandleImportEntryObject impObj);
 
 class ExportEntryObject : public NativeObject {
  public:
   enum {
     ExportNameSlot = 0,
     ModuleRequestSlot,
     ImportNameSlot,
     LocalNameSlot,
@@ -78,16 +84,19 @@ class ExportEntryObject : public NativeO
   JSAtom* exportName() const;
   JSAtom* moduleRequest() const;
   JSAtom* importName() const;
   JSAtom* localName() const;
   uint32_t lineNumber() const;
   uint32_t columnNumber() const;
 };
 
+template <XDRMode mode>
+XDRResult XDRExportEntries(XDRState<mode>* xdr, MutableHandleArrayObject vec);
+
 typedef Rooted<ExportEntryObject*> RootedExportEntryObject;
 typedef Handle<ExportEntryObject*> HandleExportEntryObject;
 
 class RequestedModuleObject : public NativeObject {
  public:
   enum { ModuleSpecifierSlot = 0, LineNumberSlot, ColumnNumberSlot, SlotCount };
 
   static const JSClass class_;
@@ -98,16 +107,23 @@ class RequestedModuleObject : public Nat
                                        uint32_t columnNumber);
   JSAtom* moduleSpecifier() const;
   uint32_t lineNumber() const;
   uint32_t columnNumber() const;
 };
 
 typedef Rooted<RequestedModuleObject*> RootedRequestedModuleObject;
 typedef Handle<RequestedModuleObject*> HandleRequestedModuleObject;
+typedef Rooted<GCVector<RequestedModuleObject*>> RootedRequestedModuleVector;
+typedef MutableHandle<RequestedModuleObject*>
+    MutableHandleRequestedModuleObject;
+
+template <XDRMode mode>
+XDRResult XDRRequestedModuleObject(XDRState<mode>* xdr,
+                                   MutableHandleRequestedModuleObject reqObj);
 
 class IndirectBindingMap {
  public:
   void trace(JSTracer* trc);
 
   bool put(JSContext* cx, HandleId name,
            HandleModuleEnvironmentObject environment, HandleId localName);
 
@@ -314,37 +330,43 @@ class ModuleObject : public NativeObject
   static bool execute(JSContext* cx, HandleModuleObject self,
                       MutableHandleValue rval);
 
   // For intrinsic_NewModuleNamespace.
   static ModuleNamespaceObject* createNamespace(JSContext* cx,
                                                 HandleModuleObject self,
                                                 HandleObject exports);
 
+  static bool createEnvironment(JSContext* cx, HandleModuleObject self);
+
+  FunctionDeclarationVector* functionDeclarations();
+
  private:
   static const JSClassOps classOps_;
 
   static void trace(JSTracer* trc, JSObject* obj);
   static void finalize(JSFreeOp* fop, JSObject* obj);
 
   bool hasImportBindings() const;
-  FunctionDeclarationVector* functionDeclarations();
 };
 
 JSObject* GetOrCreateModuleMetaObject(JSContext* cx, HandleObject module);
 
 JSObject* CallModuleResolveHook(JSContext* cx, HandleValue referencingPrivate,
                                 HandleString specifier);
 
 JSObject* StartDynamicModuleImport(JSContext* cx, HandleScript script,
                                    HandleValue specifier);
 
 bool FinishDynamicModuleImport(JSContext* cx, HandleValue referencingPrivate,
                                HandleString specifier, HandleObject promise);
 
+template <XDRMode mode>
+XDRResult XDRModuleObject(XDRState<mode>* xdr, MutableHandleModuleObject modp);
+
 }  // namespace js
 
 template <>
 inline bool JSObject::is<js::ModuleNamespaceObject>() const {
   return js::IsDerivedProxyObject(this,
                                   &js::ModuleNamespaceObject::proxyHandler);
 }
 
--- a/js/src/frontend/BytecodeCompiler.cpp
+++ b/js/src/frontend/BytecodeCompiler.cpp
@@ -610,24 +610,20 @@ ModuleObject* frontend::ModuleCompiler<U
   if (!emitter->emitScript(pn->as<ModuleNode>().body())) {
     return nullptr;
   }
 
   if (!builder.initModule()) {
     return nullptr;
   }
 
-  RootedModuleEnvironmentObject env(
-      cx, ModuleEnvironmentObject::create(cx, module));
-  if (!env) {
+  if (!ModuleObject::createEnvironment(cx, module)) {
     return nullptr;
   }
 
-  module->setInitialEnvironment(env);
-
   // Enqueue an off-thread source compression task after finishing parsing.
   if (!info.scriptSource->tryCompressOffThread(cx)) {
     return nullptr;
   }
 
   MOZ_ASSERT_IF(!cx->isHelperThreadContext(), !cx->isExceptionPending());
   return module;
 }
--- a/js/src/gc/Rooting.h
+++ b/js/src/gc/Rooting.h
@@ -64,16 +64,17 @@ typedef JS::MutableHandle<DebuggerArgume
 typedef JS::MutableHandle<DebuggerEnvironment*>
     MutableHandleDebuggerEnvironment;
 typedef JS::MutableHandle<DebuggerFrame*> MutableHandleDebuggerFrame;
 typedef JS::MutableHandle<DebuggerObject*> MutableHandleDebuggerObject;
 typedef JS::MutableHandle<DebuggerScript*> MutableHandleDebuggerScript;
 typedef JS::MutableHandle<DebuggerSource*> MutableHandleDebuggerSource;
 typedef JS::MutableHandle<Scope*> MutableHandleScope;
 typedef JS::MutableHandle<ModuleObject*> MutableHandleModuleObject;
+typedef JS::MutableHandle<ArrayObject*> MutableHandleArrayObject;
 
 typedef JS::Rooted<NativeObject*> RootedNativeObject;
 typedef JS::Rooted<Shape*> RootedShape;
 typedef JS::Rooted<ObjectGroup*> RootedObjectGroup;
 typedef JS::Rooted<JSAtom*> RootedAtom;
 typedef JS::Rooted<JSLinearString*> RootedLinearString;
 typedef JS::Rooted<PropertyName*> RootedPropertyName;
 typedef JS::Rooted<ArrayObject*> RootedArrayObject;
--- a/js/src/vm/JSAtom.cpp
+++ b/js/src/vm/JSAtom.cpp
@@ -1213,16 +1213,42 @@ static JSAtom* AtomizeLittleEndianTwoByt
   }
 
   AtomHasher::Lookup lookup(chars, length);
   return AtomizeAndCopyCharsFromLookup(cx, chars, length, lookup, DoNotPinAtom,
                                        Nothing());
 }
 
 template <XDRMode mode>
+XDRResult js::XDRAtomOrNull(XDRState<mode>* xdr, MutableHandleAtom atomp) {
+  uint8_t isNull = false;
+  if (mode == XDR_ENCODE) {
+    if (!atomp) {
+      isNull = true;
+    }
+  }
+
+  MOZ_TRY(xdr->codeUint8(&isNull));
+
+  if (!isNull) {
+    MOZ_TRY(XDRAtom(xdr, atomp));
+  } else if (mode == XDR_DECODE) {
+    atomp.set(nullptr);
+  }
+
+  return Ok();
+}
+
+template XDRResult js::XDRAtomOrNull(XDRState<XDR_DECODE>* xdr,
+                                     MutableHandleAtom atomp);
+
+template XDRResult js::XDRAtomOrNull(XDRState<XDR_ENCODE>* xdr,
+                                     MutableHandleAtom atomp);
+
+template <XDRMode mode>
 static XDRResult XDRAtomIndex(XDRState<mode>* xdr, uint32_t* index) {
   return xdr->codeUint32(index);
 }
 
 template <XDRMode mode>
 XDRResult js::XDRAtom(XDRState<mode>* xdr, MutableHandleAtom atomp) {
   if (!xdr->hasAtomMap() && !xdr->hasAtomTable()) {
     return XDRAtomData(xdr, atomp);
--- a/js/src/vm/JSAtom.h
+++ b/js/src/vm/JSAtom.h
@@ -73,16 +73,20 @@ extern JSAtom* ToAtom(JSContext* cx,
                       typename MaybeRooted<JS::Value, allowGC>::HandleType v);
 
 // These functions are declared in vm/Xdr.h
 //
 // template<XDRMode mode>
 // XDRResult
 // XDRAtom(XDRState<mode>* xdr, js::MutableHandleAtom atomp);
 
+// template<XDRMode mode>
+// XDRResult
+// XDRAtomOrNull(XDRState<mode>* xdr, js::MutableHandleAtom atomp);
+
 extern JS::Handle<PropertyName*> ClassName(JSProtoKey key, JSContext* cx);
 
 #ifdef DEBUG
 
 bool AtomIsMarked(JS::Zone* zone, JSAtom* atom);
 bool AtomIsMarked(JS::Zone* zone, jsid id);
 bool AtomIsMarked(JS::Zone* zone, const JS::Value& value);
 
--- a/js/src/vm/JSScript.cpp
+++ b/js/src/vm/JSScript.cpp
@@ -503,22 +503,25 @@ static XDRResult XDRInnerObject(XDRState
     }
   }
 
   return Ok();
 }
 
 template <XDRMode mode>
 static XDRResult XDRScope(XDRState<mode>* xdr, js::PrivateScriptData* data,
-                          HandleScope scriptEnclosingScope, HandleFunction fun,
-                          bool isFirstScope, MutableHandleScope scope) {
+                          HandleScope scriptEnclosingScope,
+                          HandleObject funOrMod, bool isFirstScope,
+                          MutableHandleScope scope) {
   JSContext* cx = xdr->cx();
 
   ScopeKind scopeKind;
   RootedScope enclosing(cx);
+  RootedFunction fun(cx);
+  RootedModuleObject module(cx);
   uint32_t enclosingIndex = 0;
 
   // The enclosingScope is encoded using an integer index into the scope array.
   // This means that scopes must be topologically sorted.
   if (mode == XDR_ENCODE) {
     scopeKind = scope->kind();
 
     if (isFirstScope) {
@@ -534,16 +537,22 @@ static XDRResult XDRScope(XDRState<mode>
 
   if (mode == XDR_DECODE) {
     if (isFirstScope) {
       MOZ_ASSERT(enclosingIndex == UINT32_MAX);
       enclosing = scriptEnclosingScope;
     } else {
       enclosing = &data->gcthings()[enclosingIndex].as<Scope>();
     }
+
+    if (funOrMod && funOrMod->is<ModuleObject>()) {
+      module.set(funOrMod.as<ModuleObject>());
+    } else if (funOrMod && funOrMod->is<JSFunction>()) {
+      fun.set(funOrMod.as<JSFunction>());
+    }
   }
 
   switch (scopeKind) {
     case ScopeKind::Function:
       MOZ_TRY(FunctionScope::XDR(xdr, fun, enclosing, scope));
       break;
     case ScopeKind::FunctionBodyVar:
     case ScopeKind::ParameterExpressionVar:
@@ -564,16 +573,18 @@ static XDRResult XDRScope(XDRState<mode>
     case ScopeKind::StrictEval:
       MOZ_TRY(EvalScope::XDR(xdr, scopeKind, enclosing, scope));
       break;
     case ScopeKind::Global:
     case ScopeKind::NonSyntactic:
       MOZ_TRY(GlobalScope::XDR(xdr, scopeKind, scope));
       break;
     case ScopeKind::Module:
+      MOZ_TRY(ModuleScope::XDR(xdr, module, enclosing, scope));
+      break;
     case ScopeKind::WasmInstance:
       MOZ_CRASH("NYI");
       break;
     case ScopeKind::WasmFunction:
       MOZ_CRASH("wasm functions cannot be nested in JSScripts");
       break;
     default:
       // Fail in debug, but only soft-fail in release
@@ -583,17 +594,17 @@ static XDRResult XDRScope(XDRState<mode>
 
   return Ok();
 }
 
 template <XDRMode mode>
 static XDRResult XDRScriptGCThing(XDRState<mode>* xdr, PrivateScriptData* data,
                                   HandleScriptSourceObject sourceObject,
                                   HandleScope scriptEnclosingScope,
-                                  HandleFunction fun, bool* isFirstScope,
+                                  HandleObject funOrMod, bool* isFirstScope,
                                   JS::GCCellPtr* thingp) {
   JSContext* cx = xdr->cx();
 
   enum class GCThingTag { Object, Scope, BigInt };
 
   JS::GCCellPtr thing;
 
   GCThingTag tag;
@@ -623,17 +634,17 @@ static XDRResult XDRScriptGCThing(XDRSta
       }
       break;
     }
     case GCThingTag::Scope: {
       RootedScope scope(cx);
       if (mode == XDR_ENCODE) {
         scope = &thing.as<Scope>();
       }
-      MOZ_TRY(XDRScope(xdr, data, scriptEnclosingScope, fun, *isFirstScope,
+      MOZ_TRY(XDRScope(xdr, data, scriptEnclosingScope, funOrMod, *isFirstScope,
                        &scope));
       if (mode == XDR_DECODE) {
         *thingp = JS::GCCellPtr(scope.get());
       }
       *isFirstScope = false;
       break;
     }
     case GCThingTag::BigInt: {
@@ -688,17 +699,17 @@ void js::BaseScript::finalize(JSFreeOp* 
   }
 }
 
 template <XDRMode mode>
 /* static */
 XDRResult js::PrivateScriptData::XDR(XDRState<mode>* xdr, HandleScript script,
                                      HandleScriptSourceObject sourceObject,
                                      HandleScope scriptEnclosingScope,
-                                     HandleFunction fun) {
+                                     HandleObject funOrMod) {
   uint32_t ngcthings = 0;
 
   JSContext* cx = xdr->cx();
   PrivateScriptData* data = nullptr;
 
   if (mode == XDR_ENCODE) {
     data = script->data_;
 
@@ -712,18 +723,18 @@ XDRResult js::PrivateScriptData::XDR(XDR
       return xdr->fail(JS::TranscodeResult_Throw);
     }
 
     data = script->data_;
   }
 
   bool isFirstScope = true;
   for (JS::GCCellPtr& gcThing : data->gcthings()) {
-    MOZ_TRY(XDRScriptGCThing(xdr, data, sourceObject, scriptEnclosingScope, fun,
-                             &isFirstScope, &gcThing));
+    MOZ_TRY(XDRScriptGCThing(xdr, data, sourceObject, scriptEnclosingScope,
+                             funOrMod, &isFirstScope, &gcThing));
   }
 
   // Verify marker to detect data corruption after decoding GC things. A
   // mismatch here indicates we will almost certainly crash in release.
   MOZ_TRY(xdr->codeMarker(0xF83B989A));
 
   return Ok();
 }
@@ -1039,17 +1050,17 @@ template
 template
     /* static */
     XDRResult
     RuntimeScriptData::XDR(XDRState<XDR_DECODE>* xdr, HandleScript script);
 
 template <XDRMode mode>
 XDRResult js::XDRScript(XDRState<mode>* xdr, HandleScope scriptEnclosingScope,
                         HandleScriptSourceObject sourceObjectArg,
-                        HandleFunction fun, MutableHandleScript scriptp) {
+                        HandleObject funOrMod, MutableHandleScript scriptp) {
   using ImmutableFlags = JSScript::ImmutableFlags;
 
   /* NB: Keep this in sync with CopyScript. */
 
   enum XDRScriptFlags {
     OwnSource,
     HasLazyScript,
   };
@@ -1067,29 +1078,35 @@ XDRResult js::XDRScript(XDRState<mode>* 
   uint32_t toStringStart = 0;
   uint32_t toStringEnd = 0;
   uint32_t immutableFlags = 0;
 
   // NOTE: |mutableFlags| are not preserved by XDR.
 
   JSContext* cx = xdr->cx();
   RootedScript script(cx);
+  bool isInnerFunction = funOrMod && funOrMod->is<JSFunction>();
 
   // Instrumented scripts cannot be encoded, as they have extra instructions
   // which are not normally present. Globals with instrumentation enabled must
   // compile scripts via the bytecode emitter, which will insert these
   // instructions.
   if (xdr->hasOptions() ? !!xdr->options().instrumentationKinds
                         : !!cx->global()->getInstrumentationHolder()) {
     return xdr->fail(JS::TranscodeResult_Failure);
   }
 
   if (mode == XDR_ENCODE) {
     script = scriptp.get();
-    MOZ_ASSERT(script->function() == fun);
+
+    RootedFunction fun(cx);
+    if (isInnerFunction) {
+      MOZ_ASSERT(script->function() == funOrMod);
+      fun.set(&funOrMod->as<JSFunction>());
+    }
 
     if (!fun && script->treatAsRunOnce() && script->hasRunOnce()) {
       // This is a toplevel or eval script that's runOnce.  We want to
       // make sure that we're not XDR-saving an object we emitted for
       // JSOP_OBJECT that then got modified.  So throw if we're not
       // cloning in JSOP_OBJECT or if we ever didn't clone in it in the
       // past.
       Realm* realm = cx->realm();
@@ -1192,19 +1209,20 @@ XDRResult js::XDRScript(XDRState<mode>* 
   } else {
     // While encoding, the ScriptSource passed in must match the ScriptSource
     // of the script.
     MOZ_ASSERT_IF(mode == XDR_ENCODE,
                   sourceObjectArg->source() == script->scriptSource());
   }
 
   if (mode == XDR_DECODE) {
-    RootedObject functionOrGlobal(cx,
-                                  fun ? static_cast<JSObject*>(fun)
-                                      : static_cast<JSObject*>(cx->global()));
+    RootedObject functionOrGlobal(
+        cx, isInnerFunction ? static_cast<JSObject*>(funOrMod)
+                            : static_cast<JSObject*>(cx->global()));
+
     script = JSScript::Create(cx, functionOrGlobal, *options, sourceObject,
                               sourceStart, sourceEnd, toStringStart,
                               toStringEnd, lineno, column);
     if (!script) {
       return xdr->fail(JS::TranscodeResult_Throw);
     }
     scriptp.set(script);
 
@@ -1213,36 +1231,41 @@ XDRResult js::XDRScript(XDRState<mode>* 
     if (script->argumentsHasVarBinding()) {
       // Call setArgumentsHasVarBinding to initialize the
       // NeedsArgsAnalysis flag.
       script->setArgumentsHasVarBinding();
     }
 
     // Set the script in its function now so that inner scripts to be
     // decoded may iterate the static scope chain.
-    if (fun) {
-      fun->initScript(script);
+    if (isInnerFunction) {
+      funOrMod->as<JSFunction>().initScript(script);
     }
   }
 
   // If XDR operation fails, we must call JSScript::freeScriptData in order
   // to neuter the script. Various things that iterate raw scripts in a GC
   // arena use the presense of this data to detect if initialization is
   // complete.
   auto scriptDataGuard = mozilla::MakeScopeExit([&] {
     if (mode == XDR_DECODE) {
       script->freeScriptData();
     }
   });
 
   // NOTE: The script data is rooted by the script.
   MOZ_TRY(PrivateScriptData::XDR<mode>(xdr, script, sourceObject,
-                                       scriptEnclosingScope, fun));
+                                       scriptEnclosingScope, funOrMod));
   MOZ_TRY(RuntimeScriptData::XDR<mode>(xdr, script));
 
+  RootedFunction fun(cx);
+  if (isInnerFunction) {
+    fun.set(&funOrMod->as<JSFunction>());
+  }
+
   if (mode == XDR_DECODE) {
     if (!script->shareScriptData(cx)) {
       return xdr->fail(JS::TranscodeResult_Throw);
     }
   }
 
   if (xdrScriptFlags & (1 << HasLazyScript)) {
     Rooted<LazyScript*> lazy(cx);
@@ -1272,21 +1295,21 @@ XDRResult js::XDRScript(XDRState<mode>* 
   }
 
   MOZ_ASSERT(script->code(), "Where's our bytecode?");
   scriptDataGuard.release();
   return Ok();
 }
 
 template XDRResult js::XDRScript(XDRState<XDR_ENCODE>*, HandleScope,
-                                 HandleScriptSourceObject, HandleFunction,
+                                 HandleScriptSourceObject, HandleObject,
                                  MutableHandleScript);
 
 template XDRResult js::XDRScript(XDRState<XDR_DECODE>*, HandleScope,
-                                 HandleScriptSourceObject, HandleFunction,
+                                 HandleScriptSourceObject, HandleObject,
                                  MutableHandleScript);
 
 template <XDRMode mode>
 XDRResult js::XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope,
                             HandleScriptSourceObject sourceObject,
                             HandleFunction fun,
                             MutableHandle<LazyScript*> lazy) {
   MOZ_ASSERT_IF(mode == XDR_DECODE, sourceObject);
--- a/js/src/vm/JSScript.h
+++ b/js/src/vm/JSScript.h
@@ -1587,17 +1587,17 @@ class alignas(uintptr_t) PrivateScriptDa
   // Allocate a new PrivateScriptData. Headers and GCPtrs are initialized.
   static PrivateScriptData* new_(JSContext* cx, uint32_t ngcthings);
 
   template <XDRMode mode>
   static MOZ_MUST_USE XDRResult XDR(js::XDRState<mode>* xdr,
                                     js::HandleScript script,
                                     js::HandleScriptSourceObject sourceObject,
                                     js::HandleScope scriptEnclosingScope,
-                                    js::HandleFunction fun);
+                                    js::HandleObject funOrMod);
 
   // Clone src script data into dst script.
   static bool Clone(JSContext* cx, js::HandleScript src, js::HandleScript dst,
                     js::MutableHandle<JS::GCVector<js::Scope*>> scopes);
 
   static bool InitFromEmitter(JSContext* cx, js::HandleScript script,
                               js::frontend::BytecodeEmitter* bce);
 
@@ -2153,18 +2153,18 @@ setterLevel:                            
 
 /*
  * NB: after a successful XDR_DECODE, XDRScript callers must do any required
  * subsequent set-up of owning function or script object and then call
  * CallNewScriptHook.
  */
 template <XDRMode mode>
 XDRResult XDRScript(XDRState<mode>* xdr, HandleScope enclosingScope,
-                    HandleScriptSourceObject sourceObject, HandleFunction fun,
-                    MutableHandleScript scriptp);
+                    HandleScriptSourceObject sourceObject,
+                    HandleObject funOrMod, MutableHandleScript scriptp);
 
 template <XDRMode mode>
 XDRResult XDRLazyScript(XDRState<mode>* xdr, HandleScope enclosingScope,
                         HandleScriptSourceObject sourceObject,
                         HandleFunction fun, MutableHandle<LazyScript*> lazy);
 
 /*
  * Code any constant value.
@@ -2571,17 +2571,17 @@ class JSScript : public js::BaseScript {
   // End of fields.  Start methods.
   //
 
  private:
   template <js::XDRMode mode>
   friend js::XDRResult js::XDRScript(js::XDRState<mode>* xdr,
                                      js::HandleScope enclosingScope,
                                      js::HandleScriptSourceObject sourceObject,
-                                     js::HandleFunction fun,
+                                     js::HandleObject funOrMod,
                                      js::MutableHandleScript scriptp);
 
   template <js::XDRMode mode>
   friend js::XDRResult js::RuntimeScriptData::XDR(js::XDRState<mode>* xdr,
                                                   js::HandleScript script);
 
   template <js::XDRMode mode>
   friend js::XDRResult js::ImmutableScriptData::XDR(js::XDRState<mode>* xdr,
@@ -2594,17 +2594,17 @@ class JSScript : public js::BaseScript {
   friend bool js::ImmutableScriptData::InitFromEmitter(
       JSContext* cx, js::HandleScript script,
       js::frontend::BytecodeEmitter* bce, uint32_t nslot);
 
   template <js::XDRMode mode>
   friend js::XDRResult js::PrivateScriptData::XDR(
       js::XDRState<mode>* xdr, js::HandleScript script,
       js::HandleScriptSourceObject sourceObject,
-      js::HandleScope scriptEnclosingScope, js::HandleFunction fun);
+      js::HandleScope scriptEnclosingScope, js::HandleObject funOrMod);
 
   friend bool js::PrivateScriptData::Clone(
       JSContext* cx, js::HandleScript src, js::HandleScript dst,
       js::MutableHandle<JS::GCVector<js::Scope*>> scopes);
 
   friend bool js::PrivateScriptData::InitFromEmitter(
       JSContext* cx, js::HandleScript script,
       js::frontend::BytecodeEmitter* bce);
--- a/js/src/vm/Scope.cpp
+++ b/js/src/vm/Scope.cpp
@@ -1348,16 +1348,75 @@ static JSAtom* GenerateWasmName(JSContex
   }
   if (!NumberValueToStringBuffer(cx, Int32Value(index), sb)) {
     return nullptr;
   }
 
   return sb.finishAtom();
 }
 
+template <XDRMode mode>
+/* static */
+XDRResult ModuleScope::XDR(XDRState<mode>* xdr, HandleModuleObject module,
+                           HandleScope enclosing, MutableHandleScope scope) {
+  JSContext* cx = xdr->cx();
+  Rooted<Data*> data(cx);
+  MOZ_TRY(
+      XDRSizedBindingNames<ModuleScope>(xdr, scope.as<ModuleScope>(), &data));
+
+  {
+    Maybe<Rooted<UniquePtr<Data>>> uniqueData;
+    if (mode == XDR_DECODE) {
+      uniqueData.emplace(cx, data);
+    }
+
+    uint32_t nextFrameSlot;
+    if (mode == XDR_ENCODE) {
+      nextFrameSlot = data->nextFrameSlot;
+    }
+
+    MOZ_TRY(xdr->codeUint32(&data->varStart));
+    MOZ_TRY(xdr->codeUint32(&data->letStart));
+    MOZ_TRY(xdr->codeUint32(&data->constStart));
+    MOZ_TRY(xdr->codeUint32(&nextFrameSlot));
+
+    if (mode == XDR_DECODE) {
+      if (!data->length) {
+        MOZ_ASSERT(!data->varStart);
+        MOZ_ASSERT(!data->letStart);
+        MOZ_ASSERT(!data->constStart);
+        MOZ_ASSERT(!data->nextFrameSlot);
+      }
+
+      scope.set(createWithData(cx, &uniqueData.ref(), module, enclosing));
+      if (!scope) {
+        return xdr->fail(JS::TranscodeResult_Throw);
+      }
+
+      // nextFrameSlot is used only for this correctness check.
+      MOZ_ASSERT(nextFrameSlot ==
+                 scope->as<ModuleScope>().data().nextFrameSlot);
+    }
+  }
+
+  return Ok();
+}
+
+template
+    /* static */
+    XDRResult
+    ModuleScope::XDR(XDRState<XDR_ENCODE>* xdr, HandleModuleObject module,
+                     HandleScope enclosing, MutableHandleScope scope);
+
+template
+    /* static */
+    XDRResult
+    ModuleScope::XDR(XDRState<XDR_DECODE>* xdr, HandleModuleObject module,
+                     HandleScope enclosing, MutableHandleScope scope);
+
 static void InitializeTrailingName(TrailingNamesArray& trailingNames, size_t i,
                                    JSAtom* name) {
   void* trailingName = &trailingNames[i];
   new (trailingName) BindingName(name, false);
 }
 
 template <class Data>
 static void InitializeNextTrailingName(const Rooted<UniquePtr<Data>>& data,
--- a/js/src/vm/Scope.h
+++ b/js/src/vm/Scope.h
@@ -914,16 +914,20 @@ class ModuleScope : public Scope {
     void trace(JSTracer* trc);
     Zone* zone() const;
   };
 
   static ModuleScope* create(JSContext* cx, Handle<Data*> data,
                              Handle<ModuleObject*> module,
                              HandleScope enclosing);
 
+  template <XDRMode mode>
+  static XDRResult XDR(XDRState<mode>* xdr, HandleModuleObject module,
+                       HandleScope enclosing, MutableHandleScope scope);
+
  private:
   static ModuleScope* createWithData(JSContext* cx,
                                      MutableHandle<UniquePtr<Data>> data,
                                      Handle<ModuleObject*> module,
                                      HandleScope enclosing);
 
   static bool prepareForScopeCreation(JSContext* cx,
                                       MutableHandle<UniquePtr<Data>> data,
--- a/js/src/vm/Xdr.cpp
+++ b/js/src/vm/Xdr.cpp
@@ -207,16 +207,30 @@ static XDRResult VersionCheck(XDRState<m
       return xdr->fail(JS::TranscodeResult_Failure_BadBuildId);
     }
   }
 
   return Ok();
 }
 
 template <XDRMode mode>
+XDRResult XDRState<mode>::codeModuleObject(MutableHandleModuleObject modp) {
+#ifdef DEBUG
+  auto sanityCheck = mozilla::MakeScopeExit(
+      [&] { MOZ_ASSERT(validateResultCode(cx(), resultCode())); });
+#endif
+  if (mode == XDR_DECODE) {
+    modp.set(nullptr);
+  }
+
+  MOZ_TRY(XDRModuleObject(this, modp));
+  return Ok();
+}
+
+template <XDRMode mode>
 static XDRResult XDRAtomCount(XDRState<mode>* xdr, uint32_t* atomCount) {
   return xdr->codeUint32(atomCount);
 }
 
 template <XDRMode mode>
 static XDRResult AtomTable(XDRState<mode>* xdr) {
   uint8_t atomHeader = false;
   if (mode == XDR_ENCODE) {
--- a/js/src/vm/Xdr.h
+++ b/js/src/vm/Xdr.h
@@ -417,16 +417,17 @@ class XDRState : public XDRCoderBase {
 
   // Transcode null-terminated strings. When decoding, a new buffer is
   // allocated and ownership is returned to caller.
   //
   // NOTE: Throws if string longer than JSString::MAX_LENGTH.
   XDRResult codeCharsZ(XDRTranscodeString<char>& buffer);
   XDRResult codeCharsZ(XDRTranscodeString<char16_t>& buffer);
 
+  XDRResult codeModuleObject(MutableHandleModuleObject modp);
   XDRResult codeFunction(JS::MutableHandleFunction objp,
                          HandleScriptSourceObject sourceObject = nullptr);
   XDRResult codeScript(MutableHandleScript scriptp);
 };
 
 using XDREncoder = XDRState<XDR_ENCODE>;
 using XDRDecoderBase = XDRState<XDR_DECODE>;
 
@@ -592,16 +593,19 @@ class XDRIncrementalEncoder : public XDR
   // Append the content collected during the incremental encoding into the
   // buffer given as argument.
   XDRResult linearize(JS::TranscodeBuffer& buffer);
 
   void trace(JSTracer* trc);
 };
 
 template <XDRMode mode>
+XDRResult XDRAtomOrNull(XDRState<mode>* xdr, js::MutableHandleAtom atomp);
+
+template <XDRMode mode>
 XDRResult XDRAtom(XDRState<mode>* xdr, js::MutableHandleAtom atomp);
 
 template <XDRMode mode>
 XDRResult XDRAtomData(XDRState<mode>* xdr, js::MutableHandleAtom atomp);
 
 } /* namespace js */
 
 #endif /* vm_Xdr_h */