Bug 1316635: Factor out DecodeImportsSection; r=luke
authorBenjamin Bouvier <benj@benj.me>
Tue, 08 Nov 2016 19:36:43 +0100
changeset 348812 5227163b9dad33c56cc657bc847cb534ed3141ef
parent 348811 e33f5dd90abe0fee15f6eeadc934dfaee043de5f
child 348813 aed62b2e0a8a14845fcc975ed70874dc6bd46f1c
push id10298
push userraliiev@mozilla.com
push dateMon, 14 Nov 2016 12:33:03 +0000
treeherdermozilla-aurora@7e29173b1641 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersluke
bugs1316635
milestone52.0a1
Bug 1316635: Factor out DecodeImportsSection; r=luke MozReview-Commit-ID: 586kaHWXJvu
js/src/wasm/WasmBinaryFormat.cpp
js/src/wasm/WasmBinaryFormat.h
js/src/wasm/WasmBinaryToAST.cpp
js/src/wasm/WasmCode.h
js/src/wasm/WasmCompile.cpp
js/src/wasm/WasmModule.h
js/src/wasm/WasmTypes.h
--- a/js/src/wasm/WasmBinaryFormat.cpp
+++ b/js/src/wasm/WasmBinaryFormat.cpp
@@ -17,16 +17,18 @@
  */
 
 #include "wasm/WasmBinaryFormat.h"
 
 #include "mozilla/CheckedInt.h"
 
 #include "jsprf.h"
 
+#include "jit/JitOptions.h"
+
 using namespace js;
 using namespace js::wasm;
 
 using mozilla::CheckedInt;
 
 bool
 wasm::DecodePreamble(Decoder& d)
 {
@@ -126,16 +128,178 @@ wasm::DecodeTypeSection(Decoder& d, SigW
     }
 
     if (!d.finishSection(sectionStart, sectionSize, "type"))
         return false;
 
     return true;
 }
 
+UniqueChars
+wasm::DecodeName(Decoder& d)
+{
+    uint32_t numBytes;
+    if (!d.readVarU32(&numBytes))
+        return nullptr;
+
+    const uint8_t* bytes;
+    if (!d.readBytes(numBytes, &bytes))
+        return nullptr;
+
+    UniqueChars name(js_pod_malloc<char>(numBytes + 1));
+    if (!name)
+        return nullptr;
+
+    memcpy(name.get(), bytes, numBytes);
+    name[numBytes] = '\0';
+
+    return name;
+}
+
+bool
+wasm::DecodeSignatureIndex(Decoder& d, const SigWithIdVector& sigs, uint32_t* sigIndex)
+{
+    if (!d.readVarU32(sigIndex))
+        return d.fail("expected signature index");
+
+    if (*sigIndex >= sigs.length())
+        return d.fail("signature index out of range");
+
+    return true;
+}
+
+bool
+wasm::DecodeTableLimits(Decoder& d, TableDescVector* tables)
+{
+    uint32_t elementType;
+    if (!d.readVarU32(&elementType))
+        return d.fail("expected table element type");
+
+    if (elementType != uint32_t(TypeCode::AnyFunc))
+        return d.fail("expected 'anyfunc' element type");
+
+    Limits limits;
+    if (!DecodeLimits(d, &limits))
+        return false;
+
+    if (tables->length())
+        return d.fail("already have default table");
+
+    return tables->emplaceBack(TableKind::AnyFunction, limits);
+}
+
+bool
+wasm::GlobalIsJSCompatible(Decoder& d, ValType type, bool isMutable)
+{
+    switch (type) {
+      case ValType::I32:
+      case ValType::F32:
+      case ValType::F64:
+        break;
+      case ValType::I64:
+        if (!jit::JitOptions.wasmTestMode)
+            return d.fail("can't import/export an Int64 global to JS");
+        break;
+      default:
+        return d.fail("unexpected variable type in global import/export");
+    }
+
+    if (isMutable)
+        return d.fail("can't import/export mutable globals in the MVP");
+
+    return true;
+}
+
+static bool
+DecodeImport(Decoder& d, const SigWithIdVector& sigs, Uint32Vector* funcSigIndices,
+             GlobalDescVector* globals, TableDescVector* tables, Maybe<Limits>* memory,
+             ImportVector* imports)
+{
+    UniqueChars moduleName = DecodeName(d);
+    if (!moduleName)
+        return d.fail("expected valid import module name");
+
+    UniqueChars funcName = DecodeName(d);
+    if (!funcName)
+        return d.fail("expected valid import func name");
+
+    uint32_t rawImportKind;
+    if (!d.readVarU32(&rawImportKind))
+        return d.fail("failed to read import kind");
+
+    DefinitionKind importKind = DefinitionKind(rawImportKind);
+
+    switch (importKind) {
+      case DefinitionKind::Function: {
+        uint32_t sigIndex;
+        if (!DecodeSignatureIndex(d, sigs, &sigIndex))
+            return false;
+        if (!funcSigIndices->append(sigIndex))
+            return false;
+        break;
+      }
+      case DefinitionKind::Table: {
+        if (!DecodeTableLimits(d, tables))
+            return false;
+        break;
+      }
+      case DefinitionKind::Memory: {
+        Limits limits;
+        if (!DecodeMemoryLimits(d, !!*memory, &limits))
+            return false;
+        memory->emplace(limits);
+        break;
+      }
+      case DefinitionKind::Global: {
+        ValType type;
+        bool isMutable;
+        if (!DecodeGlobalType(d, &type, &isMutable))
+            return false;
+        if (!GlobalIsJSCompatible(d, type, isMutable))
+            return false;
+        if (!globals->append(GlobalDesc(type, isMutable, globals->length())))
+            return false;
+        break;
+      }
+      default:
+        return d.fail("unsupported import kind");
+    }
+
+    return imports->emplaceBack(Move(moduleName), Move(funcName), importKind);
+}
+
+bool
+wasm::DecodeImportSection(Decoder& d, const SigWithIdVector& sigs, Uint32Vector* funcSigIndices,
+                          GlobalDescVector* globals, TableDescVector* tables, Maybe<Limits>* memory,
+                          ImportVector* imports)
+{
+    uint32_t sectionStart, sectionSize;
+    if (!d.startSection(SectionId::Import, &sectionStart, &sectionSize, "import"))
+        return false;
+    if (sectionStart == Decoder::NotStarted)
+        return true;
+
+    uint32_t numImports;
+    if (!d.readVarU32(&numImports))
+        return d.fail("failed to read number of imports");
+
+    if (numImports > MaxImports)
+        return d.fail("too many imports");
+
+    for (uint32_t i = 0; i < numImports; i++) {
+        if (!DecodeImport(d, sigs, funcSigIndices, globals, tables, memory, imports))
+            return false;
+    }
+
+    if (!d.finishSection(sectionStart, sectionSize, "import"))
+        return false;
+
+    return true;
+}
+
 bool
 wasm::EncodeLocalEntries(Encoder& e, const ValTypeVector& locals)
 {
     uint32_t numLocalEntries = 0;
     ValType prev = ValType(TypeCode::Limit);
     for (ValType t : locals) {
         if (t != prev) {
             numLocalEntries++;
--- a/js/src/wasm/WasmBinaryFormat.h
+++ b/js/src/wasm/WasmBinaryFormat.h
@@ -639,16 +639,33 @@ MOZ_MUST_USE bool
 DecodePreamble(Decoder& d);
 
 MOZ_MUST_USE bool
 CheckValType(Decoder& d, ValType type);
 
 MOZ_MUST_USE bool
 DecodeTypeSection(Decoder& d, SigWithIdVector* sigs);
 
+UniqueChars
+DecodeName(Decoder& d);
+
+MOZ_MUST_USE bool
+DecodeSignatureIndex(Decoder& d, const SigWithIdVector& sigs, uint32_t* sigIndex);
+
+MOZ_MUST_USE bool
+DecodeTableLimits(Decoder& d, TableDescVector* tables);
+
+MOZ_MUST_USE bool
+GlobalIsJSCompatible(Decoder& d, ValType type, bool isMutable);
+
+MOZ_MUST_USE bool
+DecodeImportSection(Decoder& d, const SigWithIdVector& sigs, Uint32Vector* funcSigIndices,
+                    GlobalDescVector* globals, TableDescVector* tables, Maybe<Limits>* memory,
+                    ImportVector* imports);
+
 MOZ_MUST_USE bool
 EncodeLocalEntries(Encoder& d, const ValTypeVector& locals);
 
 MOZ_MUST_USE bool
 DecodeLocalEntries(Decoder& d, ValTypeVector* locals);
 
 MOZ_MUST_USE bool
 DecodeGlobalType(Decoder& d, ValType* type, bool* isMutable);
--- a/js/src/wasm/WasmBinaryToAST.cpp
+++ b/js/src/wasm/WasmBinaryToAST.cpp
@@ -1427,24 +1427,23 @@ AstDecodeExpr(AstDecodeContext& c)
         lastExpr->setOffset(exprOffset);
     return true;
 }
 
 /*****************************************************************************/
 // wasm decoding and generation
 
 static bool
-AstDecodeTypeSection(AstDecodeContext& c)
+AstDecodeTypeSection(AstDecodeContext& c, SigWithIdVector* sigs)
 {
-    SigWithIdVector sigs;
-    if (!DecodeTypeSection(c.d, &sigs))
+    if (!DecodeTypeSection(c.d, sigs))
         return false;
 
-    for (size_t sigIndex = 0; sigIndex < sigs.length(); sigIndex++) {
-        const Sig& sig = sigs[sigIndex];
+    for (size_t sigIndex = 0; sigIndex < sigs->length(); sigIndex++) {
+        const Sig& sig = (*sigs)[sigIndex];
 
         AstValTypeVector args(c.lifo);
         if (!args.appendAll(sig.args()))
             return false;
 
         AstSig sigNoName(Move(args), sig.ret());
         AstName sigName;
         if (!AstDecodeGenerateName(c, AstName(u"type"), sigIndex, &sigName))
@@ -1453,16 +1452,113 @@ AstDecodeTypeSection(AstDecodeContext& c
         AstSig* astSig = new(c.lifo) AstSig(sigName, Move(sigNoName));
         if (!astSig || !c.module().append(astSig))
             return false;
     }
 
     return true;
 }
 
+static AstName
+ToAstName(AstDecodeContext& c, const UniqueChars& name)
+{
+    size_t len = strlen(name.get());
+    char16_t* buffer = static_cast<char16_t *>(c.lifo.alloc(len * sizeof(char16_t)));
+    if (!buffer)
+        return AstName();
+
+    for (size_t i = 0; i < len; i++)
+        buffer[i] = name.get()[i];
+
+    return AstName(buffer, len);
+}
+
+static bool
+AstDecodeImportSection(AstDecodeContext& c, const SigWithIdVector& sigs)
+{
+    Uint32Vector funcSigIndices;
+    GlobalDescVector globals;
+    TableDescVector tables;
+    Maybe<Limits> memory;
+    ImportVector imports;
+    if (!DecodeImportSection(c.d, sigs, &funcSigIndices, &globals, &tables, &memory, &imports))
+        return false;
+
+    size_t lastFunc = 0;
+    size_t lastGlobal = 0;
+    size_t lastTable = 0;
+    size_t lastMemory = 0;
+
+    for (size_t importIndex = 0; importIndex < imports.length(); importIndex++) {
+        const Import& import = imports[importIndex];
+
+        AstName moduleName = ToAstName(c, import.module);
+        AstName fieldName = ToAstName(c, import.field);
+
+        AstImport* ast = nullptr;
+        switch (import.kind) {
+          case DefinitionKind::Function: {
+            AstName importName;
+            if (!AstDecodeGenerateName(c, AstName(u"import"), lastFunc, &importName))
+                return false;
+
+            AstRef sigRef;
+            if (!AstDecodeGenerateRef(c, AstName(u"type"), funcSigIndices[lastFunc], &sigRef))
+                return false;
+
+            ast = new(c.lifo) AstImport(importName, moduleName, fieldName, sigRef);
+            lastFunc++;
+            break;
+          }
+          case DefinitionKind::Global: {
+            AstName importName;
+            if (!AstDecodeGenerateName(c, AstName(u"global"), lastGlobal, &importName))
+                return false;
+
+            const GlobalDesc& global = globals[lastGlobal];
+            ValType type = global.type();
+            bool isMutable = global.isMutable();
+
+            if (!c.addGlobalDesc(type, isMutable, /* import */ true))
+                return false;
+
+            ast = new(c.lifo) AstImport(importName, moduleName, fieldName,
+                                        AstGlobal(importName, type, isMutable));
+            lastGlobal++;
+            break;
+          }
+          case DefinitionKind::Table: {
+            AstName importName;
+            if (!AstDecodeGenerateName(c, AstName(u"table"), lastTable, &importName))
+                return false;
+
+            ast = new(c.lifo) AstImport(importName, moduleName, fieldName, DefinitionKind::Table,
+                                        tables[lastTable].limits);
+            lastTable++;
+            break;
+          }
+          case DefinitionKind::Memory: {
+            AstName importName;
+            if (!AstDecodeGenerateName(c, AstName(u"memory"), lastMemory, &importName))
+                return false;
+
+            ast = new(c.lifo) AstImport(importName, moduleName, fieldName, DefinitionKind::Memory,
+                                        *memory);
+            lastMemory++;
+            break;
+          }
+        }
+
+        if (!ast || !c.module().append(ast))
+            return false;
+    }
+
+    return true;
+}
+
 static bool
 AstDecodeSignatureIndex(AstDecodeContext& c, uint32_t* sigIndex)
 {
     if (!c.d.readVarU32(sigIndex))
         return c.d.fail("expected signature index");
 
     if (*sigIndex >= c.module().sigs().length())
         return c.d.fail("signature index out of range");
@@ -1561,153 +1657,16 @@ AstDecodeName(AstDecodeContext& c, AstNa
     for (size_t i = 0; i < length; i++)
         buffer[i] = bytes[i];
 
     *name = AstName(buffer, length);
     return true;
 }
 
 static bool
-AstDecodeLimitsTable(AstDecodeContext& c, Limits* limits)
-{
-    uint32_t kind;
-    if (!c.d.readVarU32(&kind))
-        return false;
-
-    if (kind != uint32_t(TypeCode::AnyFunc))
-        return c.d.fail("unknown type constructor kind");
-
-    if (!DecodeLimits(c.d, limits))
-        return false;
-
-    return true;
-}
-
-static bool
-AstDecodeImport(AstDecodeContext& c, uint32_t importIndex, AstImport** import)
-{
-    AstName moduleName;
-    if (!AstDecodeName(c, &moduleName))
-        return c.d.fail("expected import module name");
-
-    if (moduleName.empty())
-        return c.d.fail("module name cannot be empty");
-
-    AstName fieldName;
-    if (!AstDecodeName(c, &fieldName))
-        return c.d.fail("expected import field name");
-
-    uint32_t kind;
-    if (!c.d.readVarU32(&kind))
-        return c.d.fail("expected import kind");
-
-    switch (kind) {
-      case uint32_t(DefinitionKind::Function): {
-        AstName importName;
-        if (!AstDecodeGenerateName(c, AstName(u"import"), importIndex, &importName))
-            return false;
-
-        uint32_t sigIndex = AstNoIndex;
-        if (!AstDecodeSignatureIndex(c, &sigIndex))
-            return false;
-
-        AstRef sigRef;
-        if (!AstDecodeGenerateRef(c, AstName(u"type"), sigIndex, &sigRef))
-            return false;
-
-        *import = new(c.lifo) AstImport(importName, moduleName, fieldName, sigRef);
-        break;
-      }
-      case uint32_t(DefinitionKind::Global): {
-        AstName importName;
-        if (!AstDecodeGenerateName(c, AstName(u"global"), importIndex, &importName))
-            return false;
-
-        ValType type;
-        bool isMutable;
-        if (!DecodeGlobalType(c.d, &type, &isMutable))
-            return false;
-
-        if (!c.addGlobalDesc(type, isMutable, /* import */ true))
-            return false;
-
-        *import = new(c.lifo) AstImport(importName, moduleName, fieldName,
-                                        AstGlobal(importName, type, isMutable));
-        break;
-      }
-      case uint32_t(DefinitionKind::Table): {
-        if (c.module().hasTable())
-            return c.d.fail("already have default table");
-
-        AstName importName;
-        if (!AstDecodeGenerateName(c, AstName(u"table"), importIndex, &importName))
-            return false;
-
-        Limits table;
-        if (!AstDecodeLimitsTable(c, &table))
-            return false;
-
-        *import = new(c.lifo) AstImport(importName, moduleName, fieldName,
-                                        DefinitionKind::Table, table);
-        break;
-      }
-      case uint32_t(DefinitionKind::Memory): {
-        AstName importName;
-        if (!AstDecodeGenerateName(c, AstName(u"memory"), importIndex, &importName))
-            return false;
-
-        Limits memory;
-        if (!DecodeMemoryLimits(c.d, c.module().hasMemory(), &memory))
-            return false;
-
-        *import = new(c.lifo) AstImport(importName, moduleName, fieldName,
-                                        DefinitionKind::Memory, memory);
-        break;
-      }
-      default:
-        return c.d.fail("unknown import kind");
-    }
-
-    if (!*import)
-        return false;
-
-    return true;
-}
-
-static bool
-AstDecodeImportSection(AstDecodeContext& c)
-{
-    uint32_t sectionStart, sectionSize;
-    if (!c.d.startSection(SectionId::Import, &sectionStart, &sectionSize, "import"))
-        return false;
-    if (sectionStart == Decoder::NotStarted)
-        return true;
-
-    uint32_t numImports;
-    if (!c.d.readVarU32(&numImports))
-        return c.d.fail("failed to read number of imports");
-
-    if (numImports > MaxImports)
-        return c.d.fail( "too many imports");
-
-    for (uint32_t i = 0; i < numImports; i++) {
-        AstImport* import = nullptr;
-        if (!AstDecodeImport(c, i, &import))
-            return false;
-        if (!c.module().append(import))
-            return false;
-    }
-
-    if (!c.d.finishSection(sectionStart, sectionSize, "import"))
-        return false;
-
-    return true;
-}
-
-static bool
 AstDecodeMemorySection(AstDecodeContext& c)
 {
     bool present;
     Limits memory;
     if (!DecodeMemorySection(c.d, c.module().hasMemory(), &memory, &present))
         return false;
 
     if (present) {
@@ -2108,19 +2067,20 @@ wasm::BinaryToAst(JSContext* cx, const u
     AstModule* result = new(lifo) AstModule(lifo);
     if (!result->init())
         return false;
 
     UniqueChars error;
     Decoder d(bytes, bytes + length, &error);
     AstDecodeContext c(cx, lifo, d, *result, true);
 
+    SigWithIdVector sigs;
     if (!DecodePreamble(d) ||
-        !AstDecodeTypeSection(c) ||
-        !AstDecodeImportSection(c) ||
+        !AstDecodeTypeSection(c, &sigs) ||
+        !AstDecodeImportSection(c, sigs) ||
         !AstDecodeFunctionSection(c) ||
         !AstDecodeTableSection(c) ||
         !AstDecodeMemorySection(c) ||
         !AstDecodeGlobalSection(c) ||
         !AstDecodeExportSection(c) ||
         !AstDecodeStartSection(c) ||
         !AstDecodeElemSection(c) ||
         !AstDecodeCodeSection(c) ||
--- a/js/src/wasm/WasmCode.h
+++ b/js/src/wasm/WasmCode.h
@@ -368,28 +368,16 @@ struct CallThunk
     } u;
 
     CallThunk(uint32_t offset, uint32_t funcIndex) : offset(offset) { u.funcIndex = funcIndex; }
     CallThunk() = default;
 };
 
 WASM_DECLARE_POD_VECTOR(CallThunk, CallThunkVector)
 
-// CacheableChars is used to cacheably store UniqueChars.
-
-struct CacheableChars : UniqueChars
-{
-    CacheableChars() = default;
-    explicit CacheableChars(char* ptr) : UniqueChars(ptr) {}
-    MOZ_IMPLICIT CacheableChars(UniqueChars&& rhs) : UniqueChars(Move(rhs)) {}
-    WASM_DECLARE_SERIALIZABLE(CacheableChars)
-};
-
-typedef Vector<CacheableChars, 0, SystemAllocPolicy> CacheableCharsVector;
-
 // A wasm module can either use no memory, a unshared memory (ArrayBuffer) or
 // shared memory (SharedArrayBuffer).
 
 enum class MemoryUsage
 {
     None = false,
     Unshared = 1,
     Shared = 2
--- a/js/src/wasm/WasmCompile.cpp
+++ b/js/src/wasm/WasmCompile.cpp
@@ -406,26 +406,39 @@ DecodeFunctionBodyExprs(FunctionDecoder&
     }
 
     MOZ_CRASH("unreachable");
 
 #undef CHECK
 }
 
 static bool
-DecodeSignatureIndex(Decoder& d, const ModuleGeneratorData& init, const SigWithId** sig)
+DecodeImportSection(Decoder& d, ModuleGeneratorData* init, ImportVector* imports)
 {
-    uint32_t sigIndex;
-    if (!d.readVarU32(&sigIndex))
-        return d.fail("expected signature index");
+    Maybe<Limits> memory;
+    Uint32Vector funcSigIndices;
+    if (!DecodeImportSection(d, init->sigs, &funcSigIndices, &init->globals, &init->tables, &memory,
+                             imports))
+        return false;
+
+    for (uint32_t sigIndex : funcSigIndices) {
+        if (!init->funcSigs.append(&init->sigs[sigIndex]))
+            return false;
+    }
 
-    if (sigIndex >= init.sigs.length())
-        return d.fail("signature index out of range");
+    // The global data offsets will be filled in by ModuleGenerator::init.
+    if (!init->funcImportGlobalDataOffsets.resize(init->funcSigs.length()))
+        return false;
 
-    *sig = &init.sigs[sigIndex];
+    if (memory) {
+        init->memoryUsage = MemoryUsage::Unshared;
+        init->minMemoryLength = memory->initial;
+        init->maxMemoryLength = memory->maximum;
+    }
+
     return true;
 }
 
 static bool
 DecodeFunctionSection(Decoder& d, ModuleGeneratorData* init)
 {
     uint32_t sectionStart, sectionSize;
     if (!d.startSection(SectionId::Function, &sectionStart, &sectionSize, "function"))
@@ -440,205 +453,47 @@ DecodeFunctionSection(Decoder& d, Module
     uint32_t numFuncs = init->funcSigs.length() + numDefs;
     if (numFuncs > MaxFuncs)
         return d.fail("too many functions");
 
     if (!init->funcSigs.reserve(numFuncs))
         return false;
 
     for (uint32_t i = 0; i < numDefs; i++) {
-        const SigWithId* sig;
-        if (!DecodeSignatureIndex(d, *init, &sig))
+        uint32_t sigIndex;
+        if (!DecodeSignatureIndex(d, init->sigs, &sigIndex))
             return false;
 
+        const SigWithId* sig = &init->sigs[sigIndex];
         init->funcSigs.infallibleAppend(sig);
     }
 
     if (!d.finishSection(sectionStart, sectionSize, "function"))
         return false;
 
     return true;
 }
 
-static UniqueChars
-DecodeName(Decoder& d)
-{
-    uint32_t numBytes;
-    if (!d.readVarU32(&numBytes))
-        return nullptr;
-
-    const uint8_t* bytes;
-    if (!d.readBytes(numBytes, &bytes))
-        return nullptr;
-
-    UniqueChars name(js_pod_malloc<char>(numBytes + 1));
-    if (!name)
-        return nullptr;
-
-    memcpy(name.get(), bytes, numBytes);
-    name[numBytes] = '\0';
-
-    return name;
-}
-
-static bool
-DecodeMemoryLimits(Decoder& d, ModuleGeneratorData* init)
-{
-    Limits memory;
-    if (!DecodeMemoryLimits(d, UsesMemory(init->memoryUsage), &memory))
-        return false;
-
-    init->memoryUsage = MemoryUsage::Unshared;
-    init->minMemoryLength = memory.initial;
-    init->maxMemoryLength = memory.maximum;
-    return true;
-}
-
-static bool
-DecodeResizableTable(Decoder& d, ModuleGeneratorData* init)
-{
-    uint32_t elementType;
-    if (!d.readVarU32(&elementType))
-        return d.fail("expected table element type");
-
-    if (elementType != uint32_t(TypeCode::AnyFunc))
-        return d.fail("expected 'anyfunc' element type");
-
-    Limits limits;
-    if (!DecodeLimits(d, &limits))
-        return false;
-
-    if (!init->tables.empty())
-        return d.fail("already have default table");
-
-    return init->tables.emplaceBack(TableKind::AnyFunction, limits);
-}
-
-static bool
-GlobalIsJSCompatible(Decoder& d, ValType type, bool isMutable)
-{
-    switch (type) {
-      case ValType::I32:
-      case ValType::F32:
-      case ValType::F64:
-        break;
-      case ValType::I64:
-        if (!JitOptions.wasmTestMode)
-            return d.fail("can't import/export an Int64 global to JS");
-        break;
-      default:
-        return d.fail("unexpected variable type in global import/export");
-    }
-
-    if (isMutable)
-        return d.fail("can't import/export mutable globals in the MVP");
-
-    return true;
-}
-
-static bool
-DecodeImport(Decoder& d, ModuleGeneratorData* init, ImportVector* imports)
-{
-    UniqueChars moduleName = DecodeName(d);
-    if (!moduleName)
-        return d.fail("expected valid import module name");
-
-    UniqueChars funcName = DecodeName(d);
-    if (!funcName)
-        return d.fail("expected valid import func name");
-
-    uint32_t importKind;
-    if (!d.readVarU32(&importKind))
-        return d.fail("failed to read import kind");
-
-    switch (DefinitionKind(importKind)) {
-      case DefinitionKind::Function: {
-        const SigWithId* sig = nullptr;
-        if (!DecodeSignatureIndex(d, *init, &sig))
-            return false;
-        if (!init->funcSigs.emplaceBack(sig))
-            return false;
-        break;
-      }
-      case DefinitionKind::Table: {
-        if (!DecodeResizableTable(d, init))
-            return false;
-        break;
-      }
-      case DefinitionKind::Memory: {
-        if (!DecodeMemoryLimits(d, init))
-            return false;
-        break;
-      }
-      case DefinitionKind::Global: {
-        ValType type;
-        bool isMutable;
-        if (!DecodeGlobalType(d, &type, &isMutable))
-            return false;
-        if (!GlobalIsJSCompatible(d, type, isMutable))
-            return false;
-        if (!init->globals.append(GlobalDesc(type, isMutable, init->globals.length())))
-            return false;
-        break;
-      }
-      default:
-        return d.fail("unsupported import kind");
-    }
-
-    return imports->emplaceBack(Move(moduleName), Move(funcName), DefinitionKind(importKind));
-}
-
-static bool
-DecodeImportSection(Decoder& d, ModuleGeneratorData* init, ImportVector* imports)
-{
-    uint32_t sectionStart, sectionSize;
-    if (!d.startSection(SectionId::Import, &sectionStart, &sectionSize, "import"))
-        return false;
-    if (sectionStart == Decoder::NotStarted)
-        return true;
-
-    uint32_t numImports;
-    if (!d.readVarU32(&numImports))
-        return d.fail("failed to read number of imports");
-
-    if (numImports > MaxImports)
-        return d.fail("too many imports");
-
-    for (uint32_t i = 0; i < numImports; i++) {
-        if (!DecodeImport(d, init, imports))
-            return false;
-    }
-
-    // The global data offsets will be filled in by ModuleGenerator::init.
-    if (!init->funcImportGlobalDataOffsets.resize(init->funcSigs.length()))
-        return false;
-
-    if (!d.finishSection(sectionStart, sectionSize, "import"))
-        return false;
-
-    return true;
-}
-
 static bool
 DecodeTableSection(Decoder& d, ModuleGeneratorData* init, Uint32Vector* oldElems)
 {
     uint32_t sectionStart, sectionSize;
     if (!d.startSection(SectionId::Table, &sectionStart, &sectionSize, "table"))
         return false;
     if (sectionStart == Decoder::NotStarted)
         return true;
 
     uint32_t numTables;
     if (!d.readVarU32(&numTables))
         return d.fail("failed to read number of tables");
 
     if (numTables != 1)
         return d.fail("the number of tables must be exactly one");
 
-    if (!DecodeResizableTable(d, init))
+    if (!DecodeTableLimits(d, &init->tables))
         return false;
 
     if (!d.finishSection(sectionStart, sectionSize, "table"))
         return false;
 
     return true;
 }
 
@@ -1080,17 +935,17 @@ wasm::Compile(const ShareableBytes& byte
 
     if (!DecodePreamble(d))
         return nullptr;
 
     if (!DecodeTypeSection(d, &init->sigs))
         return nullptr;
 
     ImportVector imports;
-    if (!DecodeImportSection(d, init.get(), &imports))
+    if (!::DecodeImportSection(d, init.get(), &imports))
         return nullptr;
 
     if (!DecodeFunctionSection(d, init.get()))
         return nullptr;
 
     Uint32Vector oldElems;
     if (!DecodeTableSection(d, init.get(), &oldElems))
         return nullptr;
--- a/js/src/wasm/WasmModule.h
+++ b/js/src/wasm/WasmModule.h
@@ -72,38 +72,16 @@ struct LinkData : LinkDataCacheablePod
     SymbolicLinkArray   symbolicLinks;
 
     WASM_DECLARE_SERIALIZABLE(LinkData)
 };
 
 typedef UniquePtr<LinkData> UniqueLinkData;
 typedef UniquePtr<const LinkData> UniqueConstLinkData;
 
-// Import describes a single wasm import. An ImportVector describes all
-// of a single module's imports.
-//
-// ImportVector is built incrementally by ModuleGenerator and then stored
-// immutably by Module.
-
-struct Import
-{
-    CacheableChars module;
-    CacheableChars field;
-    DefinitionKind kind;
-
-    Import() = default;
-    Import(UniqueChars&& module, UniqueChars&& field, DefinitionKind kind)
-      : module(Move(module)), field(Move(field)), kind(kind)
-    {}
-
-    WASM_DECLARE_SERIALIZABLE(Import)
-};
-
-typedef Vector<Import, 0, SystemAllocPolicy> ImportVector;
-
 // Export describes the export of a definition in a Module to a field in the
 // export object. For functions, Export stores an index into the
 // FuncExportVector in Metadata. For memory and table exports, there is
 // at most one (default) memory/table so no index is needed. Note: a single
 // definition can be exported by multiple Exports in the ExportVector.
 //
 // ExportVector is built incrementally by ModuleGenerator and then stored
 // immutably by Module.
--- a/js/src/wasm/WasmTypes.h
+++ b/js/src/wasm/WasmTypes.h
@@ -546,16 +546,50 @@ class InitExpr
         switch (kind()) {
           case Kind::Constant: return u.val_.type();
           case Kind::GetGlobal: return u.global.type_;
         }
         MOZ_CRASH("unexpected initExpr type");
     }
 };
 
+// CacheableChars is used to cacheably store UniqueChars.
+
+struct CacheableChars : UniqueChars
+{
+    CacheableChars() = default;
+    explicit CacheableChars(char* ptr) : UniqueChars(ptr) {}
+    MOZ_IMPLICIT CacheableChars(UniqueChars&& rhs) : UniqueChars(Move(rhs)) {}
+    WASM_DECLARE_SERIALIZABLE(CacheableChars)
+};
+
+typedef Vector<CacheableChars, 0, SystemAllocPolicy> CacheableCharsVector;
+
+// Import describes a single wasm import. An ImportVector describes all
+// of a single module's imports.
+//
+// ImportVector is built incrementally by ModuleGenerator and then stored
+// immutably by Module.
+
+struct Import
+{
+    CacheableChars module;
+    CacheableChars field;
+    DefinitionKind kind;
+
+    Import() = default;
+    Import(UniqueChars&& module, UniqueChars&& field, DefinitionKind kind)
+      : module(Move(module)), field(Move(field)), kind(kind)
+    {}
+
+    WASM_DECLARE_SERIALIZABLE(Import)
+};
+
+typedef Vector<Import, 0, SystemAllocPolicy> ImportVector;
+
 // A GlobalDesc describes a single global variable. Currently, asm.js and wasm
 // exposes mutable and immutable private globals, but can't import nor export
 // mutable globals.
 
 enum class GlobalKind
 {
     Import,
     Constant,