Bug 1316634: Move function validation to WasmBinaryFormat.h/cpp && rename it WasmValidate; r=luke
authorBenjamin Bouvier <benj@benj.me>
Thu, 17 Nov 2016 18:51:44 +0100
changeset 323487 bad96b0dcb41d362dbe68473c59cfbc81b174f29
parent 323486 c0077e64b3fd71f279491ae8ffeea732b4d91778
child 323488 24995c8a348bc2a2c088a0f18682f95ce8cd2f12
push id21
push usermaklebus@msu.edu
push dateThu, 01 Dec 2016 06:22:08 +0000
reviewersluke
bugs1316634
milestone53.0a1
Bug 1316634: Move function validation to WasmBinaryFormat.h/cpp && rename it WasmValidate; r=luke MozReview-Commit-ID: KetNpyZd1J7
js/src/jsapi-tests/testWasmLEB128.cpp
js/src/moz.build
js/src/wasm/AsmJS.cpp
js/src/wasm/WasmBaselineCompile.cpp
js/src/wasm/WasmBinaryFormat.cpp
js/src/wasm/WasmBinaryFormat.h
js/src/wasm/WasmBinaryIterator.h
js/src/wasm/WasmBinaryToAST.cpp
js/src/wasm/WasmCompile.cpp
js/src/wasm/WasmGenerator.h
js/src/wasm/WasmIonCompile.cpp
js/src/wasm/WasmTextToBinary.cpp
js/src/wasm/WasmValidate.cpp
js/src/wasm/WasmValidate.h
--- a/js/src/jsapi-tests/testWasmLEB128.cpp
+++ b/js/src/jsapi-tests/testWasmLEB128.cpp
@@ -1,17 +1,17 @@
 /* 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 <stdlib.h>
 
 #include "jsapi-tests/tests.h"
 
-#include "wasm/WasmBinaryFormat.h"
+#include "wasm/WasmValidate.h"
 
 static bool WriteValidBytes(js::wasm::Encoder& encoder, bool* passed)
 {
     *passed = false;
     if (!encoder.empty())
         return true;
 
     // These remain the same under LEB128 unsigned encoding
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -355,17 +355,16 @@ UNIFIED_SOURCES += [
     'vm/UbiNodeShortestPaths.cpp',
     'vm/UnboxedObject.cpp',
     'vm/Unicode.cpp',
     'vm/Value.cpp',
     'vm/WeakMapPtr.cpp',
     'vm/Xdr.cpp',
     'wasm/AsmJS.cpp',
     'wasm/WasmBaselineCompile.cpp',
-    'wasm/WasmBinaryFormat.cpp',
     'wasm/WasmBinaryIterator.cpp',
     'wasm/WasmBinaryToAST.cpp',
     'wasm/WasmBinaryToExperimentalText.cpp',
     'wasm/WasmBinaryToText.cpp',
     'wasm/WasmCode.cpp',
     'wasm/WasmCompartment.cpp',
     'wasm/WasmCompile.cpp',
     'wasm/WasmFrameIterator.cpp',
@@ -374,17 +373,18 @@ UNIFIED_SOURCES += [
     'wasm/WasmIonCompile.cpp',
     'wasm/WasmJS.cpp',
     'wasm/WasmModule.cpp',
     'wasm/WasmSignalHandlers.cpp',
     'wasm/WasmStubs.cpp',
     'wasm/WasmTable.cpp',
     'wasm/WasmTextToBinary.cpp',
     'wasm/WasmTextUtils.cpp',
-    'wasm/WasmTypes.cpp'
+    'wasm/WasmTypes.cpp',
+    'wasm/WasmValidate.cpp'
 ]
 
 # jsarray.cpp and jsatom.cpp cannot be built in unified mode because
 # xpcshell is broken during packaging when compiled with gcc-4.8.2
 # builtin/RegExp.cpp cannot be built in unified mode because it is built
 # without PGO
 # frontend/Parser.cpp cannot be built in unified mode because of explicit
 # template instantiations.
--- a/js/src/wasm/AsmJS.cpp
+++ b/js/src/wasm/AsmJS.cpp
@@ -31,22 +31,22 @@
 
 #include "builtin/SIMD.h"
 #include "frontend/Parser.h"
 #include "gc/Policy.h"
 #include "js/MemoryMetrics.h"
 #include "vm/StringBuffer.h"
 #include "vm/Time.h"
 #include "vm/TypedArrayObject.h"
-#include "wasm/WasmBinaryFormat.h"
 #include "wasm/WasmCompile.h"
 #include "wasm/WasmGenerator.h"
 #include "wasm/WasmInstance.h"
 #include "wasm/WasmJS.h"
 #include "wasm/WasmSerialize.h"
+#include "wasm/WasmValidate.h"
 
 #include "jsobjinlines.h"
 
 #include "frontend/ParseNode-inl.h"
 #include "vm/ArrayBufferObject-inl.h"
 
 using namespace js;
 using namespace js::frontend;
--- a/js/src/wasm/WasmBaselineCompile.cpp
+++ b/js/src/wasm/WasmBaselineCompile.cpp
@@ -110,20 +110,20 @@
 #if defined(JS_CODEGEN_ARM)
 # include "jit/arm/Assembler-arm.h"
 #endif
 #if defined(JS_CODEGEN_X64) || defined(JS_CODEGEN_X86)
 # include "jit/x86-shared/Architecture-x86-shared.h"
 # include "jit/x86-shared/Assembler-x86-shared.h"
 #endif
 
-#include "wasm/WasmBinaryFormat.h"
 #include "wasm/WasmBinaryIterator.h"
 #include "wasm/WasmGenerator.h"
 #include "wasm/WasmSignalHandlers.h"
+#include "wasm/WasmValidate.h"
 
 #include "jit/MacroAssembler-inl.h"
 
 using mozilla::DebugOnly;
 using mozilla::FloatingPoint;
 using mozilla::IsPowerOfTwo;
 using mozilla::SpecificNaN;
 
deleted file mode 100644
--- a/js/src/wasm/WasmBinaryFormat.cpp
+++ /dev/null
@@ -1,966 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- *
- * Copyright 2016 Mozilla Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#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;
-
-// Decoder implementation.
-
-bool
-Decoder::fail(const char* msg, ...)
-{
-    va_list ap;
-    va_start(ap, msg);
-    UniqueChars str(JS_vsmprintf(msg, ap));
-    va_end(ap);
-    if (!str)
-        return false;
-
-    return fail(Move(str));
-}
-
-bool
-Decoder::fail(UniqueChars msg)
-{
-    MOZ_ASSERT(error_);
-    UniqueChars strWithOffset(JS_smprintf("at offset %" PRIuSIZE ": %s", currentOffset(), msg.get()));
-    if (!strWithOffset)
-        return false;
-
-    *error_ = Move(strWithOffset);
-    return false;
-}
-
-// Misc helpers.
-
-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++;
-            prev = t;
-        }
-    }
-
-    if (!e.writeVarU32(numLocalEntries))
-        return false;
-
-    if (numLocalEntries) {
-        prev = locals[0];
-        uint32_t count = 1;
-        for (uint32_t i = 1; i < locals.length(); i++, count++) {
-            if (prev != locals[i]) {
-                if (!e.writeVarU32(count))
-                    return false;
-                if (!e.writeValType(prev))
-                    return false;
-                prev = locals[i];
-                count = 0;
-            }
-        }
-        if (!e.writeVarU32(count))
-            return false;
-        if (!e.writeValType(prev))
-            return false;
-    }
-
-    return true;
-}
-
-static bool
-DecodeValType(Decoder& d, ModuleKind kind, ValType* type)
-{
-    uint8_t unchecked;
-    if (!d.readValType(&unchecked))
-        return false;
-
-    switch (unchecked) {
-      case uint8_t(ValType::I32):
-      case uint8_t(ValType::F32):
-      case uint8_t(ValType::F64):
-      case uint8_t(ValType::I64):
-        *type = ValType(unchecked);
-        return true;
-      case uint8_t(ValType::I8x16):
-      case uint8_t(ValType::I16x8):
-      case uint8_t(ValType::I32x4):
-      case uint8_t(ValType::F32x4):
-      case uint8_t(ValType::B8x16):
-      case uint8_t(ValType::B16x8):
-      case uint8_t(ValType::B32x4):
-        if (kind != ModuleKind::AsmJS)
-            return d.fail("bad type");
-        *type = ValType(unchecked);
-        return true;
-      default:
-        break;
-    }
-    return d.fail("bad type");
-}
-
-bool
-wasm::DecodeLocalEntries(Decoder& d, ModuleKind kind, ValTypeVector* locals)
-{
-    uint32_t numLocalEntries;
-    if (!d.readVarU32(&numLocalEntries))
-        return d.fail("failed to read number of local entries");
-
-    for (uint32_t i = 0; i < numLocalEntries; i++) {
-        uint32_t count;
-        if (!d.readVarU32(&count))
-            return d.fail("failed to read local entry count");
-
-        if (MaxLocals - locals->length() < count)
-            return d.fail("too many locals");
-
-        ValType type;
-        if (!DecodeValType(d, kind, &type))
-            return false;
-
-        if (!locals->appendN(type, count))
-            return false;
-    }
-
-    return true;
-}
-
-// Section macros.
-
-static bool
-DecodePreamble(Decoder& d)
-{
-    uint32_t u32;
-    if (!d.readFixedU32(&u32) || u32 != MagicNumber)
-        return d.fail("failed to match magic number");
-
-    if (!d.readFixedU32(&u32) || u32 != EncodingVersion)
-        return d.fail("binary version 0x%" PRIx32 " does not match expected version 0x%" PRIx32,
-                      u32, EncodingVersion);
-
-    return true;
-}
-
-static bool
-DecodeTypeSection(Decoder& d, SigWithIdVector* sigs)
-{
-    uint32_t sectionStart, sectionSize;
-    if (!d.startSection(SectionId::Type, &sectionStart, &sectionSize, "type"))
-        return false;
-    if (sectionStart == Decoder::NotStarted)
-        return true;
-
-    uint32_t numSigs;
-    if (!d.readVarU32(&numSigs))
-        return d.fail("expected number of signatures");
-
-    if (numSigs > MaxSigs)
-        return d.fail("too many signatures");
-
-    if (!sigs->resize(numSigs))
-        return false;
-
-    for (uint32_t sigIndex = 0; sigIndex < numSigs; sigIndex++) {
-        uint32_t form;
-        if (!d.readVarU32(&form) || form != uint32_t(TypeCode::Func))
-            return d.fail("expected function form");
-
-        uint32_t numArgs;
-        if (!d.readVarU32(&numArgs))
-            return d.fail("bad number of function args");
-
-        if (numArgs > MaxArgsPerFunc)
-            return d.fail("too many arguments in signature");
-
-        ValTypeVector args;
-        if (!args.resize(numArgs))
-            return false;
-
-        for (uint32_t i = 0; i < numArgs; i++) {
-            if (!DecodeValType(d, ModuleKind::Wasm, &args[i]))
-                return false;
-        }
-
-        uint32_t numRets;
-        if (!d.readVarU32(&numRets))
-            return d.fail("bad number of function returns");
-
-        if (numRets > 1)
-            return d.fail("too many returns in signature");
-
-        ExprType result = ExprType::Void;
-
-        if (numRets == 1) {
-            ValType type;
-            if (!DecodeValType(d, ModuleKind::Wasm, &type))
-                return false;
-
-            result = ToExprType(type);
-        }
-
-        (*sigs)[sigIndex] = Sig(Move(args), result);
-    }
-
-    if (!d.finishSection(sectionStart, sectionSize, "type"))
-        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
-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;
-}
-
-static bool
-DecodeLimits(Decoder& d, Limits* limits)
-{
-    uint32_t flags;
-    if (!d.readVarU32(&flags))
-        return d.fail("expected flags");
-
-    if (flags & ~uint32_t(0x1))
-        return d.fail("unexpected bits set in flags: %" PRIu32, (flags & ~uint32_t(0x1)));
-
-    if (!d.readVarU32(&limits->initial))
-        return d.fail("expected initial length");
-
-    if (flags & 0x1) {
-        uint32_t maximum;
-        if (!d.readVarU32(&maximum))
-            return d.fail("expected maximum length");
-
-        if (limits->initial > maximum) {
-            return d.fail("memory size minimum must not be greater than maximum; "
-                          "maximum length %" PRIu32 " is less than initial length %" PRIu32,
-                          maximum, limits->initial);
-        }
-
-        limits->maximum.emplace(maximum);
-    }
-
-    return true;
-}
-
-static bool
-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 (limits.initial > MaxTableElems)
-        return d.fail("too many table elements");
-
-    if (tables->length())
-        return d.fail("already have default table");
-
-    return 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 (!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
-DecodeGlobalType(Decoder& d, ValType* type, bool* isMutable)
-{
-    if (!DecodeValType(d, ModuleKind::Wasm, type))
-        return false;
-
-    uint32_t flags;
-    if (!d.readVarU32(&flags))
-        return d.fail("expected global flags");
-
-    if (flags & ~uint32_t(GlobalFlags::AllowedMask))
-        return d.fail("unexpected bits set in global flags");
-
-    *isMutable = flags & uint32_t(GlobalFlags::IsMutable);
-    return true;
-}
-
-static bool
-DecodeMemoryLimits(Decoder& d, ModuleEnvironment* env)
-{
-    if (env->usesMemory())
-        return d.fail("already have default memory");
-
-    Limits memory;
-    if (!DecodeLimits(d, &memory))
-        return false;
-
-    CheckedInt<uint32_t> initialBytes = memory.initial;
-    initialBytes *= PageSize;
-    if (!initialBytes.isValid() || initialBytes.value() > uint32_t(INT32_MAX))
-        return d.fail("initial memory size too big");
-
-    memory.initial = initialBytes.value();
-
-    if (memory.maximum) {
-        CheckedInt<uint32_t> maximumBytes = *memory.maximum;
-        maximumBytes *= PageSize;
-        if (!maximumBytes.isValid())
-            return d.fail("maximum memory size too big");
-
-        memory.maximum = Some(maximumBytes.value());
-    }
-
-    env->memoryUsage = MemoryUsage::Unshared;
-    env->minMemoryLength = memory.initial;
-    env->maxMemoryLength = memory.maximum;
-    return true;
-}
-
-static bool
-DecodeImport(Decoder& d, ModuleEnvironment* env)
-{
-    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, env->sigs, &sigIndex))
-            return false;
-        if (!env->funcSigs.append(&env->sigs[sigIndex]))
-            return false;
-        break;
-      }
-      case DefinitionKind::Table: {
-        if (!DecodeTableLimits(d, &env->tables))
-            return false;
-        env->tables.back().external = true;
-        break;
-      }
-      case DefinitionKind::Memory: {
-        if (!DecodeMemoryLimits(d, env))
-            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 (!env->globals.append(GlobalDesc(type, isMutable, env->globals.length())))
-            return false;
-        break;
-      }
-      default:
-        return d.fail("unsupported import kind");
-    }
-
-    return env->imports.emplaceBack(Move(moduleName), Move(funcName), importKind);
-}
-
-static bool
-DecodeImportSection(Decoder& d, ModuleEnvironment* env)
-{
-    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, env))
-            return false;
-    }
-
-    if (!d.finishSection(sectionStart, sectionSize, "import"))
-        return false;
-
-    // The global data offsets will be filled in by ModuleGenerator::init.
-    if (!env->funcImportGlobalDataOffsets.resize(env->funcSigs.length()))
-        return false;
-
-    return true;
-}
-
-static bool
-DecodeFunctionSection(Decoder& d, ModuleEnvironment* env)
-{
-    uint32_t sectionStart, sectionSize;
-    if (!d.startSection(SectionId::Function, &sectionStart, &sectionSize, "function"))
-        return false;
-    if (sectionStart == Decoder::NotStarted)
-        return true;
-
-    uint32_t numDefs;
-    if (!d.readVarU32(&numDefs))
-        return d.fail("expected number of function definitions");
-
-    uint32_t numFuncs = env->funcSigs.length() + numDefs;
-    if (numFuncs > MaxFuncs)
-        return d.fail("too many functions");
-
-    if (!env->funcSigs.reserve(numFuncs))
-        return false;
-
-    for (uint32_t i = 0; i < numDefs; i++) {
-        uint32_t sigIndex;
-        if (!DecodeSignatureIndex(d, env->sigs, &sigIndex))
-            return false;
-        env->funcSigs.infallibleAppend(&env->sigs[sigIndex]);
-    }
-
-    if (!d.finishSection(sectionStart, sectionSize, "function"))
-        return false;
-
-    return true;
-}
-
-static bool
-DecodeTableSection(Decoder& d, TableDescVector* tables)
-{
-    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 (!DecodeTableLimits(d, tables))
-        return false;
-
-    if (!d.finishSection(sectionStart, sectionSize, "table"))
-        return false;
-
-    return true;
-}
-
-static bool
-DecodeMemorySection(Decoder& d, ModuleEnvironment* env)
-{
-    uint32_t sectionStart, sectionSize;
-    if (!d.startSection(SectionId::Memory, &sectionStart, &sectionSize, "memory"))
-        return false;
-    if (sectionStart == Decoder::NotStarted)
-        return true;
-
-    uint32_t numMemories;
-    if (!d.readVarU32(&numMemories))
-        return d.fail("failed to read number of memories");
-
-    if (numMemories != 1)
-        return d.fail("the number of memories must be exactly one");
-
-    if (!DecodeMemoryLimits(d, env))
-        return false;
-
-    if (!d.finishSection(sectionStart, sectionSize, "memory"))
-        return false;
-
-    return true;
-}
-
-static bool
-DecodeInitializerExpression(Decoder& d, const GlobalDescVector& globals, ValType expected,
-                            InitExpr* init)
-{
-    uint16_t op;
-    if (!d.readOp(&op))
-        return d.fail("failed to read initializer type");
-
-    switch (op) {
-      case uint16_t(Op::I32Const): {
-        int32_t i32;
-        if (!d.readVarS32(&i32))
-            return d.fail("failed to read initializer i32 expression");
-        *init = InitExpr(Val(uint32_t(i32)));
-        break;
-      }
-      case uint16_t(Op::I64Const): {
-        int64_t i64;
-        if (!d.readVarS64(&i64))
-            return d.fail("failed to read initializer i64 expression");
-        *init = InitExpr(Val(uint64_t(i64)));
-        break;
-      }
-      case uint16_t(Op::F32Const): {
-        RawF32 f32;
-        if (!d.readFixedF32(&f32))
-            return d.fail("failed to read initializer f32 expression");
-        *init = InitExpr(Val(f32));
-        break;
-      }
-      case uint16_t(Op::F64Const): {
-        RawF64 f64;
-        if (!d.readFixedF64(&f64))
-            return d.fail("failed to read initializer f64 expression");
-        *init = InitExpr(Val(f64));
-        break;
-      }
-      case uint16_t(Op::GetGlobal): {
-        uint32_t i;
-        if (!d.readVarU32(&i))
-            return d.fail("failed to read get_global index in initializer expression");
-        if (i >= globals.length())
-            return d.fail("global index out of range in initializer expression");
-        if (!globals[i].isImport() || globals[i].isMutable())
-            return d.fail("initializer expression must reference a global immutable import");
-        *init = InitExpr(i, globals[i].type());
-        break;
-      }
-      default: {
-        return d.fail("unexpected initializer expression");
-      }
-    }
-
-    if (expected != init->type())
-        return d.fail("type mismatch: initializer type and expected type don't match");
-
-    uint16_t end;
-    if (!d.readOp(&end) || end != uint16_t(Op::End))
-        return d.fail("failed to read end of initializer expression");
-
-    return true;
-}
-
-static bool
-DecodeGlobalSection(Decoder& d, GlobalDescVector* globals)
-{
-    uint32_t sectionStart, sectionSize;
-    if (!d.startSection(SectionId::Global, &sectionStart, &sectionSize, "global"))
-        return false;
-    if (sectionStart == Decoder::NotStarted)
-        return true;
-
-    uint32_t numDefs;
-    if (!d.readVarU32(&numDefs))
-        return d.fail("expected number of globals");
-
-    uint32_t numGlobals = globals->length() + numDefs;
-    if (numGlobals > MaxGlobals)
-        return d.fail("too many globals");
-
-    if (!globals->reserve(numGlobals))
-        return false;
-
-    for (uint32_t i = 0; i < numDefs; i++) {
-        ValType type;
-        bool isMutable;
-        if (!DecodeGlobalType(d, &type, &isMutable))
-            return false;
-
-        InitExpr initializer;
-        if (!DecodeInitializerExpression(d, *globals, type, &initializer))
-            return false;
-
-        globals->infallibleAppend(GlobalDesc(initializer, isMutable));
-    }
-
-    if (!d.finishSection(sectionStart, sectionSize, "global"))
-        return false;
-
-    return true;
-}
-
-typedef HashSet<const char*, CStringHasher, SystemAllocPolicy> CStringSet;
-
-static UniqueChars
-DecodeExportName(Decoder& d, CStringSet* dupSet)
-{
-    UniqueChars exportName = DecodeName(d);
-    if (!exportName) {
-        d.fail("expected valid export name");
-        return nullptr;
-    }
-
-    CStringSet::AddPtr p = dupSet->lookupForAdd(exportName.get());
-    if (p) {
-        d.fail("duplicate export");
-        return nullptr;
-    }
-
-    if (!dupSet->add(p, exportName.get()))
-        return nullptr;
-
-    return Move(exportName);
-}
-
-static bool
-DecodeExport(Decoder& d, ModuleEnvironment* env, CStringSet* dupSet)
-{
-    UniqueChars fieldName = DecodeExportName(d, dupSet);
-    if (!fieldName)
-        return false;
-
-    uint32_t exportKind;
-    if (!d.readVarU32(&exportKind))
-        return d.fail("failed to read export kind");
-
-    switch (DefinitionKind(exportKind)) {
-      case DefinitionKind::Function: {
-        uint32_t funcIndex;
-        if (!d.readVarU32(&funcIndex))
-            return d.fail("expected function index");
-
-        if (funcIndex >= env->numFuncs())
-            return d.fail("exported function index out of bounds");
-
-        return env->exports.emplaceBack(Move(fieldName), funcIndex, DefinitionKind::Function);
-      }
-      case DefinitionKind::Table: {
-        uint32_t tableIndex;
-        if (!d.readVarU32(&tableIndex))
-            return d.fail("expected table index");
-
-        if (tableIndex >= env->tables.length())
-            return d.fail("exported table index out of bounds");
-
-        MOZ_ASSERT(env->tables.length() == 1);
-        env->tables[0].external = true;
-
-        return env->exports.emplaceBack(Move(fieldName), DefinitionKind::Table);
-      }
-      case DefinitionKind::Memory: {
-        uint32_t memoryIndex;
-        if (!d.readVarU32(&memoryIndex))
-            return d.fail("expected memory index");
-
-        if (memoryIndex > 0 || !env->usesMemory())
-            return d.fail("exported memory index out of bounds");
-
-        return env->exports.emplaceBack(Move(fieldName), DefinitionKind::Memory);
-      }
-      case DefinitionKind::Global: {
-        uint32_t globalIndex;
-        if (!d.readVarU32(&globalIndex))
-            return d.fail("expected global index");
-
-        if (globalIndex >= env->globals.length())
-            return d.fail("exported global index out of bounds");
-
-        const GlobalDesc& global = env->globals[globalIndex];
-        if (!GlobalIsJSCompatible(d, global.type(), global.isMutable()))
-            return false;
-
-        return env->exports.emplaceBack(Move(fieldName), globalIndex, DefinitionKind::Global);
-      }
-      default:
-        return d.fail("unexpected export kind");
-    }
-
-    MOZ_CRASH("unreachable");
-}
-
-static bool
-DecodeExportSection(Decoder& d, ModuleEnvironment* env)
-{
-    uint32_t sectionStart, sectionSize;
-    if (!d.startSection(SectionId::Export, &sectionStart, &sectionSize, "export"))
-        return false;
-    if (sectionStart == Decoder::NotStarted)
-        return true;
-
-    CStringSet dupSet;
-    if (!dupSet.init())
-        return false;
-
-    uint32_t numExports;
-    if (!d.readVarU32(&numExports))
-        return d.fail("failed to read number of exports");
-
-    if (numExports > MaxExports)
-        return d.fail("too many exports");
-
-    for (uint32_t i = 0; i < numExports; i++) {
-        if (!DecodeExport(d, env, &dupSet))
-            return false;
-    }
-
-    if (!d.finishSection(sectionStart, sectionSize, "export"))
-        return false;
-
-    return true;
-}
-
-static bool
-DecodeStartSection(Decoder& d, ModuleEnvironment* env)
-{
-    uint32_t sectionStart, sectionSize;
-    if (!d.startSection(SectionId::Start, &sectionStart, &sectionSize, "start"))
-        return false;
-    if (sectionStart == Decoder::NotStarted)
-        return true;
-
-    uint32_t funcIndex;
-    if (!d.readVarU32(&funcIndex))
-        return d.fail("failed to read start func index");
-
-    if (funcIndex >= env->numFuncs())
-        return d.fail("unknown start function");
-
-    const Sig& sig = *env->funcSigs[funcIndex];
-    if (!IsVoid(sig.ret()))
-        return d.fail("start function must not return anything");
-
-    if (sig.args().length())
-        return d.fail("start function must be nullary");
-
-    env->startFuncIndex = Some(funcIndex);
-
-    if (!d.finishSection(sectionStart, sectionSize, "start"))
-        return false;
-
-    return true;
-}
-
-static bool
-DecodeElemSection(Decoder& d, ModuleEnvironment* env)
-{
-    uint32_t sectionStart, sectionSize;
-    if (!d.startSection(SectionId::Elem, &sectionStart, &sectionSize, "elem"))
-        return false;
-    if (sectionStart == Decoder::NotStarted)
-        return true;
-
-    uint32_t numSegments;
-    if (!d.readVarU32(&numSegments))
-        return d.fail("failed to read number of elem segments");
-
-    if (numSegments > MaxElemSegments)
-        return d.fail("too many elem segments");
-
-    for (uint32_t i = 0; i < numSegments; i++) {
-        uint32_t tableIndex;
-        if (!d.readVarU32(&tableIndex))
-            return d.fail("expected table index");
-
-        MOZ_ASSERT(env->tables.length() <= 1);
-        if (tableIndex >= env->tables.length())
-            return d.fail("table index out of range");
-
-        InitExpr offset;
-        if (!DecodeInitializerExpression(d, env->globals, ValType::I32, &offset))
-            return false;
-
-        uint32_t numElems;
-        if (!d.readVarU32(&numElems))
-            return d.fail("expected segment size");
-
-        Uint32Vector elemFuncIndices;
-        if (!elemFuncIndices.resize(numElems))
-            return false;
-
-        for (uint32_t i = 0; i < numElems; i++) {
-            if (!d.readVarU32(&elemFuncIndices[i]))
-                return d.fail("failed to read element function index");
-            if (elemFuncIndices[i] >= env->numFuncs())
-                return d.fail("table element out of range");
-        }
-
-        if (!env->elemSegments.emplaceBack(0, offset, Move(elemFuncIndices)))
-            return false;
-
-        env->tables[env->elemSegments.back().tableIndex].external = true;
-    }
-
-    if (!d.finishSection(sectionStart, sectionSize, "elem"))
-        return false;
-
-    return true;
-}
-
-bool
-wasm::DecodeModuleEnvironment(Decoder& d, ModuleEnvironment* env)
-{
-    if (!DecodePreamble(d))
-        return false;
-
-    if (!DecodeTypeSection(d, &env->sigs))
-        return false;
-
-    if (!DecodeImportSection(d, env))
-        return false;
-
-    if (!DecodeFunctionSection(d, env))
-        return false;
-
-    if (!DecodeTableSection(d, &env->tables))
-        return false;
-
-    if (!DecodeMemorySection(d, env))
-        return false;
-
-    if (!DecodeGlobalSection(d, &env->globals))
-        return false;
-
-    if (!DecodeExportSection(d, env))
-        return false;
-
-    if (!DecodeStartSection(d, env))
-        return false;
-
-    if (!DecodeElemSection(d, env))
-        return false;
-
-    return true;
-}
-
-bool
-wasm::DecodeDataSection(Decoder& d, const ModuleEnvironment& env, DataSegmentVector* segments)
-{
-    uint32_t sectionStart, sectionSize;
-    if (!d.startSection(SectionId::Data, &sectionStart, &sectionSize, "data"))
-        return false;
-    if (sectionStart == Decoder::NotStarted)
-        return true;
-
-    if (!env.usesMemory())
-        return d.fail("data section requires a memory section");
-
-    uint32_t numSegments;
-    if (!d.readVarU32(&numSegments))
-        return d.fail("failed to read number of data segments");
-
-    if (numSegments > MaxDataSegments)
-        return d.fail("too many data segments");
-
-    for (uint32_t i = 0; i < numSegments; i++) {
-        uint32_t linearMemoryIndex;
-        if (!d.readVarU32(&linearMemoryIndex))
-            return d.fail("expected linear memory index");
-
-        if (linearMemoryIndex != 0)
-            return d.fail("linear memory index must currently be 0");
-
-        DataSegment seg;
-        if (!DecodeInitializerExpression(d, env.globals, ValType::I32, &seg.offset))
-            return false;
-
-        if (!d.readVarU32(&seg.length))
-            return d.fail("expected segment size");
-
-        seg.bytecodeOffset = d.currentOffset();
-
-        if (!d.readBytes(seg.length))
-            return d.fail("data segment shorter than declared");
-
-        if (!segments->append(seg))
-            return false;
-    }
-
-    if (!d.finishSection(sectionStart, sectionSize, "data"))
-        return false;
-
-    return true;
-}
-
-bool
-wasm::DecodeUnknownSections(Decoder& d)
-{
-    while (!d.done()) {
-        if (!d.skipUserDefinedSection())
-            return false;
-    }
-
-    return true;
-}
deleted file mode 100644
--- a/js/src/wasm/WasmBinaryFormat.h
+++ /dev/null
@@ -1,716 +0,0 @@
-/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
- * vim: set ts=8 sts=4 et sw=4 tw=99:
- *
- * Copyright 2016 Mozilla Foundation
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef wasm_binary_format_h
-#define wasm_binary_format_h
-
-#include "wasm/WasmCode.h"
-
-namespace js {
-namespace wasm {
-
-// The Encoder class appends bytes to the Bytes object it is given during
-// construction. The client is responsible for the Bytes's lifetime and must
-// keep the Bytes alive as long as the Encoder is used.
-
-class Encoder
-{
-    Bytes& bytes_;
-
-    template <class T>
-    MOZ_MUST_USE bool write(const T& v) {
-        return bytes_.append(reinterpret_cast<const uint8_t*>(&v), sizeof(T));
-    }
-
-    template <typename UInt>
-    MOZ_MUST_USE bool writeVarU(UInt i) {
-        do {
-            uint8_t byte = i & 0x7f;
-            i >>= 7;
-            if (i != 0)
-                byte |= 0x80;
-            if (!bytes_.append(byte))
-                return false;
-        } while (i != 0);
-        return true;
-    }
-
-    template <typename SInt>
-    MOZ_MUST_USE bool writeVarS(SInt i) {
-        bool done;
-        do {
-            uint8_t byte = i & 0x7f;
-            i >>= 7;
-            done = ((i == 0) && !(byte & 0x40)) || ((i == -1) && (byte & 0x40));
-            if (!done)
-                byte |= 0x80;
-            if (!bytes_.append(byte))
-                return false;
-        } while (!done);
-        return true;
-    }
-
-    void patchVarU32(size_t offset, uint32_t patchBits, uint32_t assertBits) {
-        do {
-            uint8_t assertByte = assertBits & 0x7f;
-            uint8_t patchByte = patchBits & 0x7f;
-            assertBits >>= 7;
-            patchBits >>= 7;
-            if (assertBits != 0) {
-                assertByte |= 0x80;
-                patchByte |= 0x80;
-            }
-            MOZ_ASSERT(assertByte == bytes_[offset]);
-            bytes_[offset] = patchByte;
-            offset++;
-        } while(assertBits != 0);
-    }
-
-    void patchFixedU7(size_t offset, uint8_t patchBits, uint8_t assertBits) {
-        MOZ_ASSERT(patchBits <= uint8_t(INT8_MAX));
-        patchFixedU8(offset, patchBits, assertBits);
-    }
-
-    void patchFixedU8(size_t offset, uint8_t patchBits, uint8_t assertBits) {
-        MOZ_ASSERT(bytes_[offset] == assertBits);
-        bytes_[offset] = patchBits;
-    }
-
-    uint32_t varU32ByteLength(size_t offset) const {
-        size_t start = offset;
-        while (bytes_[offset] & 0x80)
-            offset++;
-        return offset - start + 1;
-    }
-
-  public:
-    explicit Encoder(Bytes& bytes)
-      : bytes_(bytes)
-    {
-        MOZ_ASSERT(empty());
-    }
-
-    size_t currentOffset() const { return bytes_.length(); }
-    bool empty() const { return currentOffset() == 0; }
-
-    // Fixed-size encoding operations simply copy the literal bytes (without
-    // attempting to align).
-
-    MOZ_MUST_USE bool writeFixedU7(uint8_t i) {
-        MOZ_ASSERT(i <= uint8_t(INT8_MAX));
-        return writeFixedU8(i);
-    }
-    MOZ_MUST_USE bool writeFixedU8(uint8_t i) {
-        return write<uint8_t>(i);
-    }
-    MOZ_MUST_USE bool writeFixedU32(uint32_t i) {
-        return write<uint32_t>(i);
-    }
-    MOZ_MUST_USE bool writeFixedF32(RawF32 f) {
-        return write<uint32_t>(f.bits());
-    }
-    MOZ_MUST_USE bool writeFixedF64(RawF64 d) {
-        return write<uint64_t>(d.bits());
-    }
-    MOZ_MUST_USE bool writeFixedI8x16(const I8x16& i8x16) {
-        return write<I8x16>(i8x16);
-    }
-    MOZ_MUST_USE bool writeFixedI16x8(const I16x8& i16x8) {
-        return write<I16x8>(i16x8);
-    }
-    MOZ_MUST_USE bool writeFixedI32x4(const I32x4& i32x4) {
-        return write<I32x4>(i32x4);
-    }
-    MOZ_MUST_USE bool writeFixedF32x4(const F32x4& f32x4) {
-        return write<F32x4>(f32x4);
-    }
-
-    // Variable-length encodings that all use LEB128.
-
-    MOZ_MUST_USE bool writeVarU32(uint32_t i) {
-        return writeVarU<uint32_t>(i);
-    }
-    MOZ_MUST_USE bool writeVarS32(int32_t i) {
-        return writeVarS<int32_t>(i);
-    }
-    MOZ_MUST_USE bool writeVarU64(uint64_t i) {
-        return writeVarU<uint64_t>(i);
-    }
-    MOZ_MUST_USE bool writeVarS64(int64_t i) {
-        return writeVarS<int64_t>(i);
-    }
-    MOZ_MUST_USE bool writeValType(ValType type) {
-        static_assert(size_t(TypeCode::Limit) <= UINT8_MAX, "fits");
-        MOZ_ASSERT(size_t(type) < size_t(TypeCode::Limit));
-        return writeFixedU8(uint8_t(type));
-    }
-    MOZ_MUST_USE bool writeBlockType(ExprType type) {
-        static_assert(size_t(TypeCode::Limit) <= UINT8_MAX, "fits");
-        MOZ_ASSERT(size_t(type) < size_t(TypeCode::Limit));
-        return writeFixedU8(uint8_t(type));
-    }
-    MOZ_MUST_USE bool writeOp(Op op) {
-        static_assert(size_t(Op::Limit) <= 2 * UINT8_MAX, "fits");
-        MOZ_ASSERT(size_t(op) < size_t(Op::Limit));
-        if (size_t(op) < UINT8_MAX)
-            return writeFixedU8(uint8_t(op));
-        return writeFixedU8(UINT8_MAX) &&
-               writeFixedU8(size_t(op) - UINT8_MAX);
-    }
-
-    // Fixed-length encodings that allow back-patching.
-
-    MOZ_MUST_USE bool writePatchableFixedU7(size_t* offset) {
-        *offset = bytes_.length();
-        return writeFixedU8(UINT8_MAX);
-    }
-    void patchFixedU7(size_t offset, uint8_t patchBits) {
-        return patchFixedU7(offset, patchBits, UINT8_MAX);
-    }
-
-    // Variable-length encodings that allow back-patching.
-
-    MOZ_MUST_USE bool writePatchableVarU32(size_t* offset) {
-        *offset = bytes_.length();
-        return writeVarU32(UINT32_MAX);
-    }
-    void patchVarU32(size_t offset, uint32_t patchBits) {
-        return patchVarU32(offset, patchBits, UINT32_MAX);
-    }
-
-    // Byte ranges start with an LEB128 length followed by an arbitrary sequence
-    // of bytes. When used for strings, bytes are to be interpreted as utf8.
-
-    MOZ_MUST_USE bool writeBytes(const void* bytes, uint32_t numBytes) {
-        return writeVarU32(numBytes) &&
-               bytes_.append(reinterpret_cast<const uint8_t*>(bytes), numBytes);
-    }
-
-    // A "section" is a contiguous range of bytes that stores its own size so
-    // that it may be trivially skipped without examining the contents. Sections
-    // require backpatching since the size of the section is only known at the
-    // end while the size's varU32 must be stored at the beginning. Immediately
-    // after the section length is the string id of the section.
-
-    MOZ_MUST_USE bool startSection(SectionId id, size_t* offset) {
-        MOZ_ASSERT(id != SectionId::UserDefined); // not supported yet
-
-        return writeVarU32(uint32_t(id)) &&
-               writePatchableVarU32(offset);
-    }
-    void finishSection(size_t offset) {
-        return patchVarU32(offset, bytes_.length() - offset - varU32ByteLength(offset));
-    }
-};
-
-// The Decoder class decodes the bytes in the range it is given during
-// construction. The client is responsible for keeping the byte range alive as
-// long as the Decoder is used.
-
-class Decoder
-{
-    const uint8_t* const beg_;
-    const uint8_t* const end_;
-    const uint8_t* cur_;
-    UniqueChars* error_;
-
-    template <class T>
-    MOZ_MUST_USE bool read(T* out) {
-        if (bytesRemain() < sizeof(T))
-            return false;
-        memcpy((void*)out, cur_, sizeof(T));
-        cur_ += sizeof(T);
-        return true;
-    }
-
-    template <class T>
-    T uncheckedRead() {
-        MOZ_ASSERT(bytesRemain() >= sizeof(T));
-        T ret;
-        memcpy(&ret, cur_, sizeof(T));
-        cur_ += sizeof(T);
-        return ret;
-    }
-
-    template <class T>
-    void uncheckedRead(T* ret) {
-        MOZ_ASSERT(bytesRemain() >= sizeof(T));
-        memcpy(ret, cur_, sizeof(T));
-        cur_ += sizeof(T);
-    }
-
-    template <typename UInt>
-    MOZ_MUST_USE bool readVarU(UInt* out) {
-        const unsigned numBits = sizeof(UInt) * CHAR_BIT;
-        const unsigned remainderBits = numBits % 7;
-        const unsigned numBitsInSevens = numBits - remainderBits;
-        UInt u = 0;
-        uint8_t byte;
-        UInt shift = 0;
-        do {
-            if (!readFixedU8(&byte))
-                return false;
-            if (!(byte & 0x80)) {
-                *out = u | UInt(byte) << shift;
-                return true;
-            }
-            u |= UInt(byte & 0x7F) << shift;
-            shift += 7;
-        } while (shift != numBitsInSevens);
-        if (!readFixedU8(&byte) || (byte & (unsigned(-1) << remainderBits)))
-            return false;
-        *out = u | (UInt(byte) << numBitsInSevens);
-        return true;
-    }
-
-    template <typename SInt>
-    MOZ_MUST_USE bool readVarS(SInt* out) {
-        const unsigned numBits = sizeof(SInt) * CHAR_BIT;
-        const unsigned remainderBits = numBits % 7;
-        const unsigned numBitsInSevens = numBits - remainderBits;
-        SInt s = 0;
-        uint8_t byte;
-        unsigned shift = 0;
-        do {
-            if (!readFixedU8(&byte))
-                return false;
-            s |= SInt(byte & 0x7f) << shift;
-            shift += 7;
-            if (!(byte & 0x80)) {
-                if (byte & 0x40)
-                    s |= SInt(-1) << shift;
-                *out = s;
-                return true;
-            }
-        } while (shift < numBitsInSevens);
-        if (!remainderBits || !readFixedU8(&byte) || (byte & 0x80))
-            return false;
-        uint8_t mask = 0x7f & (uint8_t(-1) << remainderBits);
-        if ((byte & mask) != ((byte & (1 << (remainderBits - 1))) ? mask : 0))
-            return false;
-        *out = s | SInt(byte) << shift;
-        return true;
-    }
-
-  public:
-    Decoder(const uint8_t* begin, const uint8_t* end, UniqueChars* error)
-      : beg_(begin),
-        end_(end),
-        cur_(begin),
-        error_(error)
-    {
-        MOZ_ASSERT(begin <= end);
-    }
-    explicit Decoder(const Bytes& bytes, UniqueChars* error = nullptr)
-      : beg_(bytes.begin()),
-        end_(bytes.end()),
-        cur_(bytes.begin()),
-        error_(error)
-    {}
-
-    bool fail(const char* msg, ...) MOZ_FORMAT_PRINTF(2, 3);
-    bool fail(UniqueChars msg);
-    void clearError() {
-        if (error_)
-            error_->reset();
-    }
-
-    bool done() const {
-        MOZ_ASSERT(cur_ <= end_);
-        return cur_ == end_;
-    }
-
-    size_t bytesRemain() const {
-        MOZ_ASSERT(end_ >= cur_);
-        return size_t(end_ - cur_);
-    }
-    const uint8_t* currentPosition() const {
-        return cur_;
-    }
-    size_t currentOffset() const {
-        return cur_ - beg_;
-    }
-    const uint8_t* begin() const {
-        return beg_;
-    }
-
-    // Fixed-size encoding operations simply copy the literal bytes (without
-    // attempting to align).
-
-    MOZ_MUST_USE bool readFixedU8(uint8_t* i) {
-        return read<uint8_t>(i);
-    }
-    MOZ_MUST_USE bool readFixedU32(uint32_t* u) {
-        return read<uint32_t>(u);
-    }
-    MOZ_MUST_USE bool readFixedF32(RawF32* f) {
-        uint32_t u;
-        if (!read<uint32_t>(&u))
-            return false;
-        *f = RawF32::fromBits(u);
-        return true;
-    }
-    MOZ_MUST_USE bool readFixedF64(RawF64* d) {
-        uint64_t u;
-        if (!read<uint64_t>(&u))
-            return false;
-        *d = RawF64::fromBits(u);
-        return true;
-    }
-    MOZ_MUST_USE bool readFixedI8x16(I8x16* i8x16) {
-        return read<I8x16>(i8x16);
-    }
-    MOZ_MUST_USE bool readFixedI16x8(I16x8* i16x8) {
-        return read<I16x8>(i16x8);
-    }
-    MOZ_MUST_USE bool readFixedI32x4(I32x4* i32x4) {
-        return read<I32x4>(i32x4);
-    }
-    MOZ_MUST_USE bool readFixedF32x4(F32x4* f32x4) {
-        return read<F32x4>(f32x4);
-    }
-
-    // Variable-length encodings that all use LEB128.
-
-    MOZ_MUST_USE bool readVarU32(uint32_t* out) {
-        return readVarU<uint32_t>(out);
-    }
-    MOZ_MUST_USE bool readVarS32(int32_t* out) {
-        return readVarS<int32_t>(out);
-    }
-    MOZ_MUST_USE bool readVarU64(uint64_t* out) {
-        return readVarU<uint64_t>(out);
-    }
-    MOZ_MUST_USE bool readVarS64(int64_t* out) {
-        return readVarS<int64_t>(out);
-    }
-    MOZ_MUST_USE bool readValType(uint8_t* type) {
-        static_assert(uint8_t(TypeCode::Limit) <= UINT8_MAX, "fits");
-        return readFixedU8(type);
-    }
-    MOZ_MUST_USE bool readBlockType(uint8_t* type) {
-        static_assert(size_t(TypeCode::Limit) <= UINT8_MAX, "fits");
-        return readFixedU8(type);
-    }
-    MOZ_MUST_USE bool readOp(uint16_t* op) {
-        static_assert(size_t(Op::Limit) <= 2 * UINT8_MAX, "fits");
-        uint8_t u8;
-        if (!readFixedU8(&u8))
-            return false;
-        if (MOZ_LIKELY(u8 != UINT8_MAX)) {
-            *op = u8;
-            return true;
-        }
-        if (!readFixedU8(&u8))
-            return false;
-        *op = uint16_t(u8) + UINT8_MAX;
-        return true;
-    }
-
-    // See writeBytes comment.
-
-    MOZ_MUST_USE bool readBytes(uint32_t numBytes, const uint8_t** bytes = nullptr) {
-        if (bytes)
-            *bytes = cur_;
-        if (bytesRemain() < numBytes)
-            return false;
-        cur_ += numBytes;
-        return true;
-    }
-
-    // See "section" description in Encoder.
-
-    static const uint32_t NotStarted = UINT32_MAX;
-
-    MOZ_MUST_USE bool startSection(SectionId id,
-                                   uint32_t* startOffset,
-                                   uint32_t* size,
-                                   const char* sectionName)
-    {
-        const uint8_t* const before = cur_;
-        const uint8_t* beforeId = before;
-        uint32_t idValue;
-        if (!readVarU32(&idValue))
-            goto backup;
-        while (idValue != uint32_t(id)) {
-            if (idValue != uint32_t(SectionId::UserDefined))
-                goto backup;
-            // Rewind to the section id since skipUserDefinedSection expects it.
-            cur_ = beforeId;
-            if (!skipUserDefinedSection())
-                return false;
-            beforeId = cur_;
-            if (!readVarU32(&idValue))
-                goto backup;
-        }
-        if (!readVarU32(size))
-            goto fail;
-        if (bytesRemain() < *size)
-            goto fail;
-        *startOffset = cur_ - beg_;
-        return true;
-      backup:
-        cur_ = before;
-        *startOffset = NotStarted;
-        return true;
-      fail:
-        return fail("failed to start %s section", sectionName);
-    }
-    MOZ_MUST_USE bool finishSection(uint32_t startOffset, uint32_t size,
-                                    const char* sectionName)
-    {
-        if (size != (cur_ - beg_) - startOffset)
-            return fail("byte size mismatch in %s section", sectionName);
-        return true;
-    }
-
-    // "User sections" do not cause validation errors unless the error is in
-    // the user-defined section header itself.
-
-    MOZ_MUST_USE bool startUserDefinedSection(const char* expectedId,
-                                              size_t expectedIdSize,
-                                              uint32_t* sectionStart,
-                                              uint32_t* sectionSize)
-    {
-        const uint8_t* const before = cur_;
-        while (true) {
-            if (!startSection(SectionId::UserDefined, sectionStart, sectionSize, "user-defined"))
-                return false;
-            if (*sectionStart == NotStarted) {
-                cur_ = before;
-                return true;
-            }
-            uint32_t idSize;
-            if (!readVarU32(&idSize))
-                goto fail;
-            if (idSize > bytesRemain() || currentOffset() + idSize > *sectionStart + *sectionSize)
-                goto fail;
-            if (expectedId && (expectedIdSize != idSize || !!memcmp(cur_, expectedId, idSize))) {
-                finishUserDefinedSection(*sectionStart, *sectionSize);
-                continue;
-            }
-            cur_ += idSize;
-            return true;
-        }
-        MOZ_CRASH("unreachable");
-      fail:
-        return fail("failed to start user-defined section");
-    }
-    template <size_t IdSizeWith0>
-    MOZ_MUST_USE bool startUserDefinedSection(const char (&id)[IdSizeWith0],
-                                              uint32_t* sectionStart,
-                                              uint32_t* sectionSize)
-    {
-        MOZ_ASSERT(id[IdSizeWith0 - 1] == '\0');
-        return startUserDefinedSection(id, IdSizeWith0 - 1, sectionStart, sectionSize);
-    }
-    void finishUserDefinedSection(uint32_t sectionStart, uint32_t sectionSize) {
-        MOZ_ASSERT(cur_ >= beg_);
-        MOZ_ASSERT(cur_ <= end_);
-        cur_ = (beg_ + sectionStart) + sectionSize;
-        MOZ_ASSERT(cur_ <= end_);
-        clearError();
-    }
-    MOZ_MUST_USE bool skipUserDefinedSection() {
-        uint32_t sectionStart, sectionSize;
-        if (!startUserDefinedSection(nullptr, 0, &sectionStart, &sectionSize))
-            return false;
-        if (sectionStart == NotStarted)
-            return fail("expected user-defined section");
-        finishUserDefinedSection(sectionStart, sectionSize);
-        return true;
-    }
-
-    // The infallible "unchecked" decoding functions can be used when we are
-    // sure that the bytes are well-formed (by construction or due to previous
-    // validation).
-
-    uint8_t uncheckedReadFixedU8() {
-        return uncheckedRead<uint8_t>();
-    }
-    uint32_t uncheckedReadFixedU32() {
-        return uncheckedRead<uint32_t>();
-    }
-    RawF32 uncheckedReadFixedF32() {
-        return RawF32::fromBits(uncheckedRead<uint32_t>());
-    }
-    RawF64 uncheckedReadFixedF64() {
-        return RawF64::fromBits(uncheckedRead<uint64_t>());
-    }
-    template <typename UInt>
-    UInt uncheckedReadVarU() {
-        static const unsigned numBits = sizeof(UInt) * CHAR_BIT;
-        static const unsigned remainderBits = numBits % 7;
-        static const unsigned numBitsInSevens = numBits - remainderBits;
-        UInt decoded = 0;
-        uint32_t shift = 0;
-        do {
-            uint8_t byte = *cur_++;
-            if (!(byte & 0x80))
-                return decoded | (UInt(byte) << shift);
-            decoded |= UInt(byte & 0x7f) << shift;
-            shift += 7;
-        } while (shift != numBitsInSevens);
-        uint8_t byte = *cur_++;
-        MOZ_ASSERT(!(byte & 0xf0));
-        return decoded | (UInt(byte) << numBitsInSevens);
-    }
-    uint32_t uncheckedReadVarU32() {
-        return uncheckedReadVarU<uint32_t>();
-    }
-    int32_t uncheckedReadVarS32() {
-        int32_t i32 = 0;
-        MOZ_ALWAYS_TRUE(readVarS32(&i32));
-        return i32;
-    }
-    uint64_t uncheckedReadVarU64() {
-        return uncheckedReadVarU<uint64_t>();
-    }
-    int64_t uncheckedReadVarS64() {
-        int64_t i64 = 0;
-        MOZ_ALWAYS_TRUE(readVarS64(&i64));
-        return i64;
-    }
-    ValType uncheckedReadValType() {
-        return (ValType)uncheckedReadFixedU8();
-    }
-    Op uncheckedReadOp() {
-        static_assert(size_t(Op::Limit) <= 2 * UINT8_MAX, "fits");
-        uint8_t u8 = uncheckedReadFixedU8();
-        return u8 != UINT8_MAX
-               ? Op(u8)
-               : Op(uncheckedReadFixedU8() + UINT8_MAX);
-    }
-    void uncheckedReadFixedI8x16(I8x16* i8x16) {
-        struct T { I8x16 v; };
-        T t = uncheckedRead<T>();
-        memcpy(i8x16, &t, sizeof(t));
-    }
-    void uncheckedReadFixedI16x8(I16x8* i16x8) {
-        struct T { I16x8 v; };
-        T t = uncheckedRead<T>();
-        memcpy(i16x8, &t, sizeof(t));
-    }
-    void uncheckedReadFixedI32x4(I32x4* i32x4) {
-        struct T { I32x4 v; };
-        T t = uncheckedRead<T>();
-        memcpy(i32x4, &t, sizeof(t));
-    }
-    void uncheckedReadFixedF32x4(F32x4* f32x4) {
-        struct T { F32x4 v; };
-        T t = uncheckedRead<T>();
-        memcpy(f32x4, &t, sizeof(t));
-    }
-};
-
-// Reusable macro encoding/decoding functions reused by both the two
-// encoders (AsmJS/WasmTextToBinary) and all the decoders
-// (WasmCompile/WasmIonCompile/WasmBaselineCompile/WasmBinaryToText).
-
-// Misc helpers.
-
-MOZ_MUST_USE bool
-EncodeLocalEntries(Encoder& d, const ValTypeVector& locals);
-
-MOZ_MUST_USE bool
-DecodeLocalEntries(Decoder& d, ModuleKind kind, ValTypeVector* locals);
-
-// ModuleEnvironment contains all the state necessary to validate, process or
-// render functions. It is created by decoding all the sections before the wasm
-// code section and then used immutably during. When compiling a module using a
-// ModuleGenerator, the ModuleEnvironment holds state shared between the
-// ModuleGenerator thread and background compile threads. All the threads
-// are given a read-only view of the ModuleEnvironment, thus preventing race
-// conditions.
-
-struct ModuleEnvironment
-{
-    ModuleKind                kind;
-    MemoryUsage               memoryUsage;
-    mozilla::Atomic<uint32_t> minMemoryLength;
-    Maybe<uint32_t>           maxMemoryLength;
-
-    SigWithIdVector           sigs;
-    SigWithIdPtrVector        funcSigs;
-    Uint32Vector              funcImportGlobalDataOffsets;
-    GlobalDescVector          globals;
-    TableDescVector           tables;
-    Uint32Vector              asmJSSigToTableIndex;
-    ImportVector              imports;
-    ExportVector              exports;
-    Maybe<uint32_t>           startFuncIndex;
-    ElemSegmentVector         elemSegments;
-
-    explicit ModuleEnvironment(ModuleKind kind = ModuleKind::Wasm)
-      : kind(kind),
-        memoryUsage(MemoryUsage::None),
-        minMemoryLength(0)
-    {}
-
-    size_t numTables() const {
-        return tables.length();
-    }
-    size_t numSigs() const {
-        return sigs.length();
-    }
-    size_t numFuncs() const {
-        // asm.js pre-reserves a bunch of function index space which is
-        // incrementally filled in during function-body validation. Thus, there
-        // are a few possible interpretations of numFuncs() (total index space
-        // size vs.  exact number of imports/definitions encountered so far) and
-        // to simplify things we simply only define this quantity for wasm.
-        MOZ_ASSERT(!isAsmJS());
-        return funcSigs.length();
-    }
-    size_t numFuncDefs() const {
-        // asm.js overallocates the length of funcSigs and in general does not
-        // know the number of function definitions until it's done compiling.
-        MOZ_ASSERT(!isAsmJS());
-        return funcSigs.length() - funcImportGlobalDataOffsets.length();
-    }
-    bool usesMemory() const {
-        return UsesMemory(memoryUsage);
-    }
-    bool isAsmJS() const {
-        return kind == ModuleKind::AsmJS;
-    }
-    bool funcIsImport(uint32_t funcIndex) const {
-        return funcIndex < funcImportGlobalDataOffsets.length();
-    }
-    uint32_t funcIndexToSigIndex(uint32_t funcIndex) const {
-        return funcSigs[funcIndex] - sigs.begin();
-    }
-};
-
-typedef UniquePtr<ModuleEnvironment> UniqueModuleEnvironment;
-
-// Section macros.
-
-MOZ_MUST_USE bool
-DecodeModuleEnvironment(Decoder& d, ModuleEnvironment* env);
-
-MOZ_MUST_USE bool
-DecodeDataSection(Decoder& d, const ModuleEnvironment& env, DataSegmentVector* segments);
-
-MOZ_MUST_USE bool
-DecodeUnknownSections(Decoder& d);
-
-} // namespace wasm
-} // namespace js
-
-#endif // wasm_binary_format_h
--- a/js/src/wasm/WasmBinaryIterator.h
+++ b/js/src/wasm/WasmBinaryIterator.h
@@ -19,17 +19,17 @@
 #ifndef wasm_binary_iterator_h
 #define wasm_binary_iterator_h
 
 #include "mozilla/Poison.h"
 
 #include "jsprf.h"
 
 #include "jit/AtomicOp.h"
-#include "wasm/WasmBinaryFormat.h"
+#include "wasm/WasmValidate.h"
 
 namespace js {
 namespace wasm {
 
 // The kind of a control-flow stack item.
 enum class LabelKind : uint8_t {
     Block,
     Loop,
--- a/js/src/wasm/WasmBinaryToAST.cpp
+++ b/js/src/wasm/WasmBinaryToAST.cpp
@@ -19,18 +19,18 @@
 #include "wasm/WasmBinaryToAST.h"
 
 #include "mozilla/CheckedInt.h"
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/Sprintf.h"
 
 #include "jscntxt.h"
 
-#include "wasm/WasmBinaryFormat.h"
 #include "wasm/WasmBinaryIterator.h"
+#include "wasm/WasmValidate.h"
 
 using namespace js;
 using namespace js::wasm;
 
 using mozilla::CheckedInt;
 using mozilla::FloorLog2;
 
 enum AstDecodeTerminationKind
--- a/js/src/wasm/WasmCompile.cpp
+++ b/js/src/wasm/WasmCompile.cpp
@@ -15,407 +15,25 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #include "wasm/WasmCompile.h"
 
 #include "jsprf.h"
 
-#include "wasm/WasmBinaryFormat.h"
 #include "wasm/WasmBinaryIterator.h"
 #include "wasm/WasmGenerator.h"
 #include "wasm/WasmSignalHandlers.h"
+#include "wasm/WasmValidate.h"
 
 using namespace js;
 using namespace js::jit;
 using namespace js::wasm;
 
-using mozilla::IsNaN;
-
-namespace {
-
-struct ValidatingPolicy : OpIterPolicy
-{
-    // Validation is what we're all about here.
-    static const bool Validate = true;
-};
-
-typedef OpIter<ValidatingPolicy> ValidatingOpIter;
-
-class FunctionDecoder
-{
-    const ModuleEnvironment& env_;
-    const ValTypeVector& locals_;
-    ValidatingOpIter iter_;
-
-  public:
-    FunctionDecoder(const ModuleEnvironment& env, const ValTypeVector& locals, Decoder& d)
-      : env_(env), locals_(locals), iter_(d)
-    {}
-
-    const ModuleEnvironment& env() const { return env_; }
-    ValidatingOpIter& iter() { return iter_; }
-    const ValTypeVector& locals() const { return locals_; }
-
-    bool checkHasMemory() {
-        if (!env().usesMemory())
-            return iter().fail("can't touch memory without memory");
-        return true;
-    }
-};
-
-} // end anonymous namespace
-
-static bool
-DecodeCallArgs(FunctionDecoder& f, const Sig& sig)
-{
-    const ValTypeVector& args = sig.args();
-    uint32_t numArgs = args.length();
-    for (size_t i = 0; i < numArgs; ++i) {
-        ValType argType = args[i];
-        if (!f.iter().readCallArg(argType, numArgs, i, nullptr))
-            return false;
-    }
-
-    return f.iter().readCallArgsEnd(numArgs);
-}
-
-static bool
-DecodeCallReturn(FunctionDecoder& f, const Sig& sig)
-{
-    return f.iter().readCallReturn(sig.ret());
-}
-
-static bool
-DecodeCall(FunctionDecoder& f)
-{
-    uint32_t funcIndex;
-    if (!f.iter().readCall(&funcIndex))
-        return false;
-
-    if (funcIndex >= f.env().numFuncs())
-        return f.iter().fail("callee index out of range");
-
-    if (!f.iter().inReachableCode())
-        return true;
-
-    const Sig* sig = f.env().funcSigs[funcIndex];
-
-    return DecodeCallArgs(f, *sig) &&
-           DecodeCallReturn(f, *sig);
-}
-
-static bool
-DecodeCallIndirect(FunctionDecoder& f)
-{
-    if (!f.env().numTables())
-        return f.iter().fail("can't call_indirect without a table");
-
-    uint32_t sigIndex;
-    if (!f.iter().readCallIndirect(&sigIndex, nullptr))
-        return false;
-
-    if (sigIndex >= f.env().numSigs())
-        return f.iter().fail("signature index out of range");
-
-    if (!f.iter().inReachableCode())
-        return true;
-
-    const Sig& sig = f.env().sigs[sigIndex];
-    if (!DecodeCallArgs(f, sig))
-        return false;
-
-    return DecodeCallReturn(f, sig);
-}
-
-static bool
-DecodeBrTable(FunctionDecoder& f)
-{
-    uint32_t tableLength;
-    ExprType type = ExprType::Limit;
-    if (!f.iter().readBrTable(&tableLength, &type, nullptr, nullptr))
-        return false;
-
-    uint32_t depth;
-    for (size_t i = 0, e = tableLength; i < e; ++i) {
-        if (!f.iter().readBrTableEntry(&type, nullptr, &depth))
-            return false;
-    }
-
-    // Read the default label.
-    return f.iter().readBrTableDefault(&type, nullptr, &depth);
-}
-
-static bool
-DecodeFunctionBodyExprs(FunctionDecoder& f)
-{
-#define CHECK(c) if (!(c)) return false; break
-
-    while (true) {
-        uint16_t op;
-        if (!f.iter().readOp(&op))
-            return false;
-
-        switch (op) {
-          case uint16_t(Op::End):
-            if (!f.iter().readEnd(nullptr, nullptr, nullptr))
-                return false;
-            if (f.iter().controlStackEmpty())
-                return true;
-            break;
-          case uint16_t(Op::Nop):
-            CHECK(f.iter().readNop());
-          case uint16_t(Op::Drop):
-            CHECK(f.iter().readDrop());
-          case uint16_t(Op::Call):
-            CHECK(DecodeCall(f));
-          case uint16_t(Op::CallIndirect):
-            CHECK(DecodeCallIndirect(f));
-          case uint16_t(Op::I32Const):
-            CHECK(f.iter().readI32Const(nullptr));
-          case uint16_t(Op::I64Const):
-            CHECK(f.iter().readI64Const(nullptr));
-          case uint16_t(Op::F32Const):
-            CHECK(f.iter().readF32Const(nullptr));
-          case uint16_t(Op::F64Const):
-            CHECK(f.iter().readF64Const(nullptr));
-          case uint16_t(Op::GetLocal):
-            CHECK(f.iter().readGetLocal(f.locals(), nullptr));
-          case uint16_t(Op::SetLocal):
-            CHECK(f.iter().readSetLocal(f.locals(), nullptr, nullptr));
-          case uint16_t(Op::TeeLocal):
-            CHECK(f.iter().readTeeLocal(f.locals(), nullptr, nullptr));
-          case uint16_t(Op::GetGlobal):
-            CHECK(f.iter().readGetGlobal(f.env().globals, nullptr));
-          case uint16_t(Op::SetGlobal):
-            CHECK(f.iter().readSetGlobal(f.env().globals, nullptr, nullptr));
-          case uint16_t(Op::Select):
-            CHECK(f.iter().readSelect(nullptr, nullptr, nullptr, nullptr));
-          case uint16_t(Op::Block):
-            CHECK(f.iter().readBlock());
-          case uint16_t(Op::Loop):
-            CHECK(f.iter().readLoop());
-          case uint16_t(Op::If):
-            CHECK(f.iter().readIf(nullptr));
-          case uint16_t(Op::Else):
-            CHECK(f.iter().readElse(nullptr, nullptr));
-          case uint16_t(Op::I32Clz):
-          case uint16_t(Op::I32Ctz):
-          case uint16_t(Op::I32Popcnt):
-            CHECK(f.iter().readUnary(ValType::I32, nullptr));
-          case uint16_t(Op::I64Clz):
-          case uint16_t(Op::I64Ctz):
-          case uint16_t(Op::I64Popcnt):
-            CHECK(f.iter().readUnary(ValType::I64, nullptr));
-          case uint16_t(Op::F32Abs):
-          case uint16_t(Op::F32Neg):
-          case uint16_t(Op::F32Ceil):
-          case uint16_t(Op::F32Floor):
-          case uint16_t(Op::F32Sqrt):
-          case uint16_t(Op::F32Trunc):
-          case uint16_t(Op::F32Nearest):
-            CHECK(f.iter().readUnary(ValType::F32, nullptr));
-          case uint16_t(Op::F64Abs):
-          case uint16_t(Op::F64Neg):
-          case uint16_t(Op::F64Ceil):
-          case uint16_t(Op::F64Floor):
-          case uint16_t(Op::F64Sqrt):
-          case uint16_t(Op::F64Trunc):
-          case uint16_t(Op::F64Nearest):
-            CHECK(f.iter().readUnary(ValType::F64, nullptr));
-          case uint16_t(Op::I32Add):
-          case uint16_t(Op::I32Sub):
-          case uint16_t(Op::I32Mul):
-          case uint16_t(Op::I32DivS):
-          case uint16_t(Op::I32DivU):
-          case uint16_t(Op::I32RemS):
-          case uint16_t(Op::I32RemU):
-          case uint16_t(Op::I32And):
-          case uint16_t(Op::I32Or):
-          case uint16_t(Op::I32Xor):
-          case uint16_t(Op::I32Shl):
-          case uint16_t(Op::I32ShrS):
-          case uint16_t(Op::I32ShrU):
-          case uint16_t(Op::I32Rotl):
-          case uint16_t(Op::I32Rotr):
-            CHECK(f.iter().readBinary(ValType::I32, nullptr, nullptr));
-          case uint16_t(Op::I64Add):
-          case uint16_t(Op::I64Sub):
-          case uint16_t(Op::I64Mul):
-          case uint16_t(Op::I64DivS):
-          case uint16_t(Op::I64DivU):
-          case uint16_t(Op::I64RemS):
-          case uint16_t(Op::I64RemU):
-          case uint16_t(Op::I64And):
-          case uint16_t(Op::I64Or):
-          case uint16_t(Op::I64Xor):
-          case uint16_t(Op::I64Shl):
-          case uint16_t(Op::I64ShrS):
-          case uint16_t(Op::I64ShrU):
-          case uint16_t(Op::I64Rotl):
-          case uint16_t(Op::I64Rotr):
-            CHECK(f.iter().readBinary(ValType::I64, nullptr, nullptr));
-          case uint16_t(Op::F32Add):
-          case uint16_t(Op::F32Sub):
-          case uint16_t(Op::F32Mul):
-          case uint16_t(Op::F32Div):
-          case uint16_t(Op::F32Min):
-          case uint16_t(Op::F32Max):
-          case uint16_t(Op::F32CopySign):
-            CHECK(f.iter().readBinary(ValType::F32, nullptr, nullptr));
-          case uint16_t(Op::F64Add):
-          case uint16_t(Op::F64Sub):
-          case uint16_t(Op::F64Mul):
-          case uint16_t(Op::F64Div):
-          case uint16_t(Op::F64Min):
-          case uint16_t(Op::F64Max):
-          case uint16_t(Op::F64CopySign):
-            CHECK(f.iter().readBinary(ValType::F64, nullptr, nullptr));
-          case uint16_t(Op::I32Eq):
-          case uint16_t(Op::I32Ne):
-          case uint16_t(Op::I32LtS):
-          case uint16_t(Op::I32LtU):
-          case uint16_t(Op::I32LeS):
-          case uint16_t(Op::I32LeU):
-          case uint16_t(Op::I32GtS):
-          case uint16_t(Op::I32GtU):
-          case uint16_t(Op::I32GeS):
-          case uint16_t(Op::I32GeU):
-            CHECK(f.iter().readComparison(ValType::I32, nullptr, nullptr));
-          case uint16_t(Op::I64Eq):
-          case uint16_t(Op::I64Ne):
-          case uint16_t(Op::I64LtS):
-          case uint16_t(Op::I64LtU):
-          case uint16_t(Op::I64LeS):
-          case uint16_t(Op::I64LeU):
-          case uint16_t(Op::I64GtS):
-          case uint16_t(Op::I64GtU):
-          case uint16_t(Op::I64GeS):
-          case uint16_t(Op::I64GeU):
-            CHECK(f.iter().readComparison(ValType::I64, nullptr, nullptr));
-          case uint16_t(Op::F32Eq):
-          case uint16_t(Op::F32Ne):
-          case uint16_t(Op::F32Lt):
-          case uint16_t(Op::F32Le):
-          case uint16_t(Op::F32Gt):
-          case uint16_t(Op::F32Ge):
-            CHECK(f.iter().readComparison(ValType::F32, nullptr, nullptr));
-          case uint16_t(Op::F64Eq):
-          case uint16_t(Op::F64Ne):
-          case uint16_t(Op::F64Lt):
-          case uint16_t(Op::F64Le):
-          case uint16_t(Op::F64Gt):
-          case uint16_t(Op::F64Ge):
-            CHECK(f.iter().readComparison(ValType::F64, nullptr, nullptr));
-          case uint16_t(Op::I32Eqz):
-            CHECK(f.iter().readConversion(ValType::I32, ValType::I32, nullptr));
-          case uint16_t(Op::I64Eqz):
-          case uint16_t(Op::I32WrapI64):
-            CHECK(f.iter().readConversion(ValType::I64, ValType::I32, nullptr));
-          case uint16_t(Op::I32TruncSF32):
-          case uint16_t(Op::I32TruncUF32):
-          case uint16_t(Op::I32ReinterpretF32):
-            CHECK(f.iter().readConversion(ValType::F32, ValType::I32, nullptr));
-          case uint16_t(Op::I32TruncSF64):
-          case uint16_t(Op::I32TruncUF64):
-            CHECK(f.iter().readConversion(ValType::F64, ValType::I32, nullptr));
-          case uint16_t(Op::I64ExtendSI32):
-          case uint16_t(Op::I64ExtendUI32):
-            CHECK(f.iter().readConversion(ValType::I32, ValType::I64, nullptr));
-          case uint16_t(Op::I64TruncSF32):
-          case uint16_t(Op::I64TruncUF32):
-            CHECK(f.iter().readConversion(ValType::F32, ValType::I64, nullptr));
-          case uint16_t(Op::I64TruncSF64):
-          case uint16_t(Op::I64TruncUF64):
-          case uint16_t(Op::I64ReinterpretF64):
-            CHECK(f.iter().readConversion(ValType::F64, ValType::I64, nullptr));
-          case uint16_t(Op::F32ConvertSI32):
-          case uint16_t(Op::F32ConvertUI32):
-          case uint16_t(Op::F32ReinterpretI32):
-            CHECK(f.iter().readConversion(ValType::I32, ValType::F32, nullptr));
-          case uint16_t(Op::F32ConvertSI64):
-          case uint16_t(Op::F32ConvertUI64):
-            CHECK(f.iter().readConversion(ValType::I64, ValType::F32, nullptr));
-          case uint16_t(Op::F32DemoteF64):
-            CHECK(f.iter().readConversion(ValType::F64, ValType::F32, nullptr));
-          case uint16_t(Op::F64ConvertSI32):
-          case uint16_t(Op::F64ConvertUI32):
-            CHECK(f.iter().readConversion(ValType::I32, ValType::F64, nullptr));
-          case uint16_t(Op::F64ConvertSI64):
-          case uint16_t(Op::F64ConvertUI64):
-          case uint16_t(Op::F64ReinterpretI64):
-            CHECK(f.iter().readConversion(ValType::I64, ValType::F64, nullptr));
-          case uint16_t(Op::F64PromoteF32):
-            CHECK(f.iter().readConversion(ValType::F32, ValType::F64, nullptr));
-          case uint16_t(Op::I32Load8S):
-          case uint16_t(Op::I32Load8U):
-            CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I32, 1, nullptr));
-          case uint16_t(Op::I32Load16S):
-          case uint16_t(Op::I32Load16U):
-            CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I32, 2, nullptr));
-          case uint16_t(Op::I32Load):
-            CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I32, 4, nullptr));
-          case uint16_t(Op::I64Load8S):
-          case uint16_t(Op::I64Load8U):
-            CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I64, 1, nullptr));
-          case uint16_t(Op::I64Load16S):
-          case uint16_t(Op::I64Load16U):
-            CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I64, 2, nullptr));
-          case uint16_t(Op::I64Load32S):
-          case uint16_t(Op::I64Load32U):
-            CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I64, 4, nullptr));
-          case uint16_t(Op::I64Load):
-            CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I64, 8, nullptr));
-          case uint16_t(Op::F32Load):
-            CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::F32, 4, nullptr));
-          case uint16_t(Op::F64Load):
-            CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::F64, 8, nullptr));
-          case uint16_t(Op::I32Store8):
-            CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I32, 1, nullptr, nullptr));
-          case uint16_t(Op::I32Store16):
-            CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I32, 2, nullptr, nullptr));
-          case uint16_t(Op::I32Store):
-            CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I32, 4, nullptr, nullptr));
-          case uint16_t(Op::I64Store8):
-            CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I64, 1, nullptr, nullptr));
-          case uint16_t(Op::I64Store16):
-            CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I64, 2, nullptr, nullptr));
-          case uint16_t(Op::I64Store32):
-            CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I64, 4, nullptr, nullptr));
-          case uint16_t(Op::I64Store):
-            CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I64, 8, nullptr, nullptr));
-          case uint16_t(Op::F32Store):
-            CHECK(f.checkHasMemory() && f.iter().readStore(ValType::F32, 4, nullptr, nullptr));
-          case uint16_t(Op::F64Store):
-            CHECK(f.checkHasMemory() && f.iter().readStore(ValType::F64, 8, nullptr, nullptr));
-          case uint16_t(Op::GrowMemory):
-            CHECK(f.checkHasMemory() && f.iter().readGrowMemory(nullptr));
-          case uint16_t(Op::CurrentMemory):
-            CHECK(f.checkHasMemory() && f.iter().readCurrentMemory());
-          case uint16_t(Op::Br):
-            CHECK(f.iter().readBr(nullptr, nullptr, nullptr));
-          case uint16_t(Op::BrIf):
-            CHECK(f.iter().readBrIf(nullptr, nullptr, nullptr, nullptr));
-          case uint16_t(Op::BrTable):
-            CHECK(DecodeBrTable(f));
-          case uint16_t(Op::Return):
-            CHECK(f.iter().readReturn(nullptr));
-          case uint16_t(Op::Unreachable):
-            CHECK(f.iter().readUnreachable());
-          default:
-            return f.iter().unrecognizedOpcode(op);
-        }
-    }
-
-    MOZ_CRASH("unreachable");
-
-#undef CHECK
-}
-
 static bool
 DecodeFunctionBody(Decoder& d, ModuleGenerator& mg, uint32_t funcIndex)
 {
     uint32_t bodySize;
     if (!d.readVarU32(&bodySize))
         return d.fail("expected number of function body bytes");
 
     if (d.bytesRemain() < bodySize)
@@ -423,33 +41,17 @@ DecodeFunctionBody(Decoder& d, ModuleGen
 
     const uint8_t* bodyBegin = d.currentPosition();
     const size_t offsetInModule = d.currentOffset();
 
     FunctionGenerator fg;
     if (!mg.startFuncDef(offsetInModule, &fg))
         return false;
 
-    ValTypeVector locals;
-    const Sig& sig = mg.funcSig(funcIndex);
-    if (!locals.appendAll(sig.args()))
-        return false;
-
-    if (!DecodeLocalEntries(d, ModuleKind::Wasm, &locals))
-        return false;
-
-    FunctionDecoder f(mg.env(), locals, d);
-
-    if (!f.iter().readFunctionStart(sig.ret()))
-        return false;
-
-    if (!DecodeFunctionBodyExprs(f))
-        return false;
-
-    if (!f.iter().readFunctionEnd())
+    if (!ValidateFunctionBody(mg.env(), funcIndex, d))
         return false;
 
     if (d.currentPosition() != bodyBegin + bodySize)
         return d.fail("function body length mismatch");
 
     if (!fg.bytes().resize(bodySize))
         return false;
 
--- a/js/src/wasm/WasmGenerator.h
+++ b/js/src/wasm/WasmGenerator.h
@@ -15,18 +15,18 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
 
 #ifndef wasm_generator_h
 #define wasm_generator_h
 
 #include "jit/MacroAssembler.h"
-#include "wasm/WasmBinaryFormat.h"
 #include "wasm/WasmModule.h"
+#include "wasm/WasmValidate.h"
 
 namespace js {
 namespace wasm {
 
 struct CompileArgs;
 
 class FunctionGenerator;
 
--- a/js/src/wasm/WasmIonCompile.cpp
+++ b/js/src/wasm/WasmIonCompile.cpp
@@ -18,20 +18,20 @@
 
 #include "wasm/WasmIonCompile.h"
 
 #include "mozilla/MathAlgorithms.h"
 
 #include "jit/CodeGenerator.h"
 
 #include "wasm/WasmBaselineCompile.h"
-#include "wasm/WasmBinaryFormat.h"
 #include "wasm/WasmBinaryIterator.h"
 #include "wasm/WasmGenerator.h"
 #include "wasm/WasmSignalHandlers.h"
+#include "wasm/WasmValidate.h"
 
 using namespace js;
 using namespace js::jit;
 using namespace js::wasm;
 
 using mozilla::DebugOnly;
 using mozilla::IsPowerOfTwo;
 using mozilla::Maybe;
--- a/js/src/wasm/WasmTextToBinary.cpp
+++ b/js/src/wasm/WasmTextToBinary.cpp
@@ -26,18 +26,18 @@
 #include "jsnum.h"
 #include "jsprf.h"
 #include "jsstr.h"
 
 #include "ds/LifoAlloc.h"
 #include "js/CharacterEncoding.h"
 #include "js/HashTable.h"
 #include "wasm/WasmAST.h"
-#include "wasm/WasmBinaryFormat.h"
 #include "wasm/WasmTypes.h"
+#include "wasm/WasmValidate.h"
 
 using namespace js;
 using namespace js::wasm;
 
 using mozilla::BitwiseCast;
 using mozilla::CeilingLog2;
 using mozilla::CountLeadingZeroes32;
 using mozilla::CheckedInt;
new file mode 100644
--- /dev/null
+++ b/js/src/wasm/WasmValidate.cpp
@@ -0,0 +1,1368 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ *
+ * Copyright 2016 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wasm/WasmValidate.h"
+
+#include "mozilla/CheckedInt.h"
+
+#include "jsprf.h"
+
+#include "jit/JitOptions.h"
+#include "wasm/WasmBinaryIterator.h"
+
+using namespace js;
+using namespace js::jit;
+using namespace js::wasm;
+
+using mozilla::CheckedInt;
+
+// Decoder implementation.
+
+bool
+Decoder::fail(const char* msg, ...)
+{
+    va_list ap;
+    va_start(ap, msg);
+    UniqueChars str(JS_vsmprintf(msg, ap));
+    va_end(ap);
+    if (!str)
+        return false;
+
+    return fail(Move(str));
+}
+
+bool
+Decoder::fail(UniqueChars msg)
+{
+    MOZ_ASSERT(error_);
+    UniqueChars strWithOffset(JS_smprintf("at offset %" PRIuSIZE ": %s", currentOffset(), msg.get()));
+    if (!strWithOffset)
+        return false;
+
+    *error_ = Move(strWithOffset);
+    return false;
+}
+
+// Misc helpers.
+
+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++;
+            prev = t;
+        }
+    }
+
+    if (!e.writeVarU32(numLocalEntries))
+        return false;
+
+    if (numLocalEntries) {
+        prev = locals[0];
+        uint32_t count = 1;
+        for (uint32_t i = 1; i < locals.length(); i++, count++) {
+            if (prev != locals[i]) {
+                if (!e.writeVarU32(count))
+                    return false;
+                if (!e.writeValType(prev))
+                    return false;
+                prev = locals[i];
+                count = 0;
+            }
+        }
+        if (!e.writeVarU32(count))
+            return false;
+        if (!e.writeValType(prev))
+            return false;
+    }
+
+    return true;
+}
+
+static bool
+DecodeValType(Decoder& d, ModuleKind kind, ValType* type)
+{
+    uint8_t unchecked;
+    if (!d.readValType(&unchecked))
+        return false;
+
+    switch (unchecked) {
+      case uint8_t(ValType::I32):
+      case uint8_t(ValType::F32):
+      case uint8_t(ValType::F64):
+      case uint8_t(ValType::I64):
+        *type = ValType(unchecked);
+        return true;
+      case uint8_t(ValType::I8x16):
+      case uint8_t(ValType::I16x8):
+      case uint8_t(ValType::I32x4):
+      case uint8_t(ValType::F32x4):
+      case uint8_t(ValType::B8x16):
+      case uint8_t(ValType::B16x8):
+      case uint8_t(ValType::B32x4):
+        if (kind != ModuleKind::AsmJS)
+            return d.fail("bad type");
+        *type = ValType(unchecked);
+        return true;
+      default:
+        break;
+    }
+    return d.fail("bad type");
+}
+
+bool
+wasm::DecodeLocalEntries(Decoder& d, ModuleKind kind, ValTypeVector* locals)
+{
+    uint32_t numLocalEntries;
+    if (!d.readVarU32(&numLocalEntries))
+        return d.fail("failed to read number of local entries");
+
+    for (uint32_t i = 0; i < numLocalEntries; i++) {
+        uint32_t count;
+        if (!d.readVarU32(&count))
+            return d.fail("failed to read local entry count");
+
+        if (MaxLocals - locals->length() < count)
+            return d.fail("too many locals");
+
+        ValType type;
+        if (!DecodeValType(d, kind, &type))
+            return false;
+
+        if (!locals->appendN(type, count))
+            return false;
+    }
+
+    return true;
+}
+
+// Function body validation.
+
+struct ValidatingPolicy : OpIterPolicy
+{
+    // Validation is what we're all about here.
+    static const bool Validate = true;
+};
+
+typedef OpIter<ValidatingPolicy> ValidatingOpIter;
+
+class FunctionDecoder
+{
+    const ModuleEnvironment& env_;
+    const ValTypeVector& locals_;
+    ValidatingOpIter iter_;
+
+  public:
+    FunctionDecoder(const ModuleEnvironment& env, const ValTypeVector& locals, Decoder& d)
+      : env_(env), locals_(locals), iter_(d)
+    {}
+
+    const ModuleEnvironment& env() const { return env_; }
+    ValidatingOpIter& iter() { return iter_; }
+    const ValTypeVector& locals() const { return locals_; }
+
+    bool checkHasMemory() {
+        if (!env().usesMemory())
+            return iter().fail("can't touch memory without memory");
+        return true;
+    }
+};
+
+static bool
+DecodeCallArgs(FunctionDecoder& f, const Sig& sig)
+{
+    const ValTypeVector& args = sig.args();
+    uint32_t numArgs = args.length();
+    for (size_t i = 0; i < numArgs; ++i) {
+        ValType argType = args[i];
+        if (!f.iter().readCallArg(argType, numArgs, i, nullptr))
+            return false;
+    }
+
+    return f.iter().readCallArgsEnd(numArgs);
+}
+
+static bool
+DecodeCallReturn(FunctionDecoder& f, const Sig& sig)
+{
+    return f.iter().readCallReturn(sig.ret());
+}
+
+static bool
+DecodeCall(FunctionDecoder& f)
+{
+    uint32_t funcIndex;
+    if (!f.iter().readCall(&funcIndex))
+        return false;
+
+    if (funcIndex >= f.env().numFuncs())
+        return f.iter().fail("callee index out of range");
+
+    if (!f.iter().inReachableCode())
+        return true;
+
+    const Sig* sig = f.env().funcSigs[funcIndex];
+
+    return DecodeCallArgs(f, *sig) &&
+           DecodeCallReturn(f, *sig);
+}
+
+static bool
+DecodeCallIndirect(FunctionDecoder& f)
+{
+    if (!f.env().numTables())
+        return f.iter().fail("can't call_indirect without a table");
+
+    uint32_t sigIndex;
+    if (!f.iter().readCallIndirect(&sigIndex, nullptr))
+        return false;
+
+    if (sigIndex >= f.env().numSigs())
+        return f.iter().fail("signature index out of range");
+
+    if (!f.iter().inReachableCode())
+        return true;
+
+    const Sig& sig = f.env().sigs[sigIndex];
+    if (!DecodeCallArgs(f, sig))
+        return false;
+
+    return DecodeCallReturn(f, sig);
+}
+
+static bool
+DecodeBrTable(FunctionDecoder& f)
+{
+    uint32_t tableLength;
+    ExprType type = ExprType::Limit;
+    if (!f.iter().readBrTable(&tableLength, &type, nullptr, nullptr))
+        return false;
+
+    uint32_t depth;
+    for (size_t i = 0, e = tableLength; i < e; ++i) {
+        if (!f.iter().readBrTableEntry(&type, nullptr, &depth))
+            return false;
+    }
+
+    // Read the default label.
+    return f.iter().readBrTableDefault(&type, nullptr, &depth);
+}
+
+static bool
+DecodeFunctionBodyExprs(FunctionDecoder& f)
+{
+#define CHECK(c) if (!(c)) return false; break
+
+    while (true) {
+        uint16_t op;
+        if (!f.iter().readOp(&op))
+            return false;
+
+        switch (op) {
+          case uint16_t(Op::End):
+            if (!f.iter().readEnd(nullptr, nullptr, nullptr))
+                return false;
+            if (f.iter().controlStackEmpty())
+                return true;
+            break;
+          case uint16_t(Op::Nop):
+            CHECK(f.iter().readNop());
+          case uint16_t(Op::Drop):
+            CHECK(f.iter().readDrop());
+          case uint16_t(Op::Call):
+            CHECK(DecodeCall(f));
+          case uint16_t(Op::CallIndirect):
+            CHECK(DecodeCallIndirect(f));
+          case uint16_t(Op::I32Const):
+            CHECK(f.iter().readI32Const(nullptr));
+          case uint16_t(Op::I64Const):
+            CHECK(f.iter().readI64Const(nullptr));
+          case uint16_t(Op::F32Const):
+            CHECK(f.iter().readF32Const(nullptr));
+          case uint16_t(Op::F64Const):
+            CHECK(f.iter().readF64Const(nullptr));
+          case uint16_t(Op::GetLocal):
+            CHECK(f.iter().readGetLocal(f.locals(), nullptr));
+          case uint16_t(Op::SetLocal):
+            CHECK(f.iter().readSetLocal(f.locals(), nullptr, nullptr));
+          case uint16_t(Op::TeeLocal):
+            CHECK(f.iter().readTeeLocal(f.locals(), nullptr, nullptr));
+          case uint16_t(Op::GetGlobal):
+            CHECK(f.iter().readGetGlobal(f.env().globals, nullptr));
+          case uint16_t(Op::SetGlobal):
+            CHECK(f.iter().readSetGlobal(f.env().globals, nullptr, nullptr));
+          case uint16_t(Op::Select):
+            CHECK(f.iter().readSelect(nullptr, nullptr, nullptr, nullptr));
+          case uint16_t(Op::Block):
+            CHECK(f.iter().readBlock());
+          case uint16_t(Op::Loop):
+            CHECK(f.iter().readLoop());
+          case uint16_t(Op::If):
+            CHECK(f.iter().readIf(nullptr));
+          case uint16_t(Op::Else):
+            CHECK(f.iter().readElse(nullptr, nullptr));
+          case uint16_t(Op::I32Clz):
+          case uint16_t(Op::I32Ctz):
+          case uint16_t(Op::I32Popcnt):
+            CHECK(f.iter().readUnary(ValType::I32, nullptr));
+          case uint16_t(Op::I64Clz):
+          case uint16_t(Op::I64Ctz):
+          case uint16_t(Op::I64Popcnt):
+            CHECK(f.iter().readUnary(ValType::I64, nullptr));
+          case uint16_t(Op::F32Abs):
+          case uint16_t(Op::F32Neg):
+          case uint16_t(Op::F32Ceil):
+          case uint16_t(Op::F32Floor):
+          case uint16_t(Op::F32Sqrt):
+          case uint16_t(Op::F32Trunc):
+          case uint16_t(Op::F32Nearest):
+            CHECK(f.iter().readUnary(ValType::F32, nullptr));
+          case uint16_t(Op::F64Abs):
+          case uint16_t(Op::F64Neg):
+          case uint16_t(Op::F64Ceil):
+          case uint16_t(Op::F64Floor):
+          case uint16_t(Op::F64Sqrt):
+          case uint16_t(Op::F64Trunc):
+          case uint16_t(Op::F64Nearest):
+            CHECK(f.iter().readUnary(ValType::F64, nullptr));
+          case uint16_t(Op::I32Add):
+          case uint16_t(Op::I32Sub):
+          case uint16_t(Op::I32Mul):
+          case uint16_t(Op::I32DivS):
+          case uint16_t(Op::I32DivU):
+          case uint16_t(Op::I32RemS):
+          case uint16_t(Op::I32RemU):
+          case uint16_t(Op::I32And):
+          case uint16_t(Op::I32Or):
+          case uint16_t(Op::I32Xor):
+          case uint16_t(Op::I32Shl):
+          case uint16_t(Op::I32ShrS):
+          case uint16_t(Op::I32ShrU):
+          case uint16_t(Op::I32Rotl):
+          case uint16_t(Op::I32Rotr):
+            CHECK(f.iter().readBinary(ValType::I32, nullptr, nullptr));
+          case uint16_t(Op::I64Add):
+          case uint16_t(Op::I64Sub):
+          case uint16_t(Op::I64Mul):
+          case uint16_t(Op::I64DivS):
+          case uint16_t(Op::I64DivU):
+          case uint16_t(Op::I64RemS):
+          case uint16_t(Op::I64RemU):
+          case uint16_t(Op::I64And):
+          case uint16_t(Op::I64Or):
+          case uint16_t(Op::I64Xor):
+          case uint16_t(Op::I64Shl):
+          case uint16_t(Op::I64ShrS):
+          case uint16_t(Op::I64ShrU):
+          case uint16_t(Op::I64Rotl):
+          case uint16_t(Op::I64Rotr):
+            CHECK(f.iter().readBinary(ValType::I64, nullptr, nullptr));
+          case uint16_t(Op::F32Add):
+          case uint16_t(Op::F32Sub):
+          case uint16_t(Op::F32Mul):
+          case uint16_t(Op::F32Div):
+          case uint16_t(Op::F32Min):
+          case uint16_t(Op::F32Max):
+          case uint16_t(Op::F32CopySign):
+            CHECK(f.iter().readBinary(ValType::F32, nullptr, nullptr));
+          case uint16_t(Op::F64Add):
+          case uint16_t(Op::F64Sub):
+          case uint16_t(Op::F64Mul):
+          case uint16_t(Op::F64Div):
+          case uint16_t(Op::F64Min):
+          case uint16_t(Op::F64Max):
+          case uint16_t(Op::F64CopySign):
+            CHECK(f.iter().readBinary(ValType::F64, nullptr, nullptr));
+          case uint16_t(Op::I32Eq):
+          case uint16_t(Op::I32Ne):
+          case uint16_t(Op::I32LtS):
+          case uint16_t(Op::I32LtU):
+          case uint16_t(Op::I32LeS):
+          case uint16_t(Op::I32LeU):
+          case uint16_t(Op::I32GtS):
+          case uint16_t(Op::I32GtU):
+          case uint16_t(Op::I32GeS):
+          case uint16_t(Op::I32GeU):
+            CHECK(f.iter().readComparison(ValType::I32, nullptr, nullptr));
+          case uint16_t(Op::I64Eq):
+          case uint16_t(Op::I64Ne):
+          case uint16_t(Op::I64LtS):
+          case uint16_t(Op::I64LtU):
+          case uint16_t(Op::I64LeS):
+          case uint16_t(Op::I64LeU):
+          case uint16_t(Op::I64GtS):
+          case uint16_t(Op::I64GtU):
+          case uint16_t(Op::I64GeS):
+          case uint16_t(Op::I64GeU):
+            CHECK(f.iter().readComparison(ValType::I64, nullptr, nullptr));
+          case uint16_t(Op::F32Eq):
+          case uint16_t(Op::F32Ne):
+          case uint16_t(Op::F32Lt):
+          case uint16_t(Op::F32Le):
+          case uint16_t(Op::F32Gt):
+          case uint16_t(Op::F32Ge):
+            CHECK(f.iter().readComparison(ValType::F32, nullptr, nullptr));
+          case uint16_t(Op::F64Eq):
+          case uint16_t(Op::F64Ne):
+          case uint16_t(Op::F64Lt):
+          case uint16_t(Op::F64Le):
+          case uint16_t(Op::F64Gt):
+          case uint16_t(Op::F64Ge):
+            CHECK(f.iter().readComparison(ValType::F64, nullptr, nullptr));
+          case uint16_t(Op::I32Eqz):
+            CHECK(f.iter().readConversion(ValType::I32, ValType::I32, nullptr));
+          case uint16_t(Op::I64Eqz):
+          case uint16_t(Op::I32WrapI64):
+            CHECK(f.iter().readConversion(ValType::I64, ValType::I32, nullptr));
+          case uint16_t(Op::I32TruncSF32):
+          case uint16_t(Op::I32TruncUF32):
+          case uint16_t(Op::I32ReinterpretF32):
+            CHECK(f.iter().readConversion(ValType::F32, ValType::I32, nullptr));
+          case uint16_t(Op::I32TruncSF64):
+          case uint16_t(Op::I32TruncUF64):
+            CHECK(f.iter().readConversion(ValType::F64, ValType::I32, nullptr));
+          case uint16_t(Op::I64ExtendSI32):
+          case uint16_t(Op::I64ExtendUI32):
+            CHECK(f.iter().readConversion(ValType::I32, ValType::I64, nullptr));
+          case uint16_t(Op::I64TruncSF32):
+          case uint16_t(Op::I64TruncUF32):
+            CHECK(f.iter().readConversion(ValType::F32, ValType::I64, nullptr));
+          case uint16_t(Op::I64TruncSF64):
+          case uint16_t(Op::I64TruncUF64):
+          case uint16_t(Op::I64ReinterpretF64):
+            CHECK(f.iter().readConversion(ValType::F64, ValType::I64, nullptr));
+          case uint16_t(Op::F32ConvertSI32):
+          case uint16_t(Op::F32ConvertUI32):
+          case uint16_t(Op::F32ReinterpretI32):
+            CHECK(f.iter().readConversion(ValType::I32, ValType::F32, nullptr));
+          case uint16_t(Op::F32ConvertSI64):
+          case uint16_t(Op::F32ConvertUI64):
+            CHECK(f.iter().readConversion(ValType::I64, ValType::F32, nullptr));
+          case uint16_t(Op::F32DemoteF64):
+            CHECK(f.iter().readConversion(ValType::F64, ValType::F32, nullptr));
+          case uint16_t(Op::F64ConvertSI32):
+          case uint16_t(Op::F64ConvertUI32):
+            CHECK(f.iter().readConversion(ValType::I32, ValType::F64, nullptr));
+          case uint16_t(Op::F64ConvertSI64):
+          case uint16_t(Op::F64ConvertUI64):
+          case uint16_t(Op::F64ReinterpretI64):
+            CHECK(f.iter().readConversion(ValType::I64, ValType::F64, nullptr));
+          case uint16_t(Op::F64PromoteF32):
+            CHECK(f.iter().readConversion(ValType::F32, ValType::F64, nullptr));
+          case uint16_t(Op::I32Load8S):
+          case uint16_t(Op::I32Load8U):
+            CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I32, 1, nullptr));
+          case uint16_t(Op::I32Load16S):
+          case uint16_t(Op::I32Load16U):
+            CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I32, 2, nullptr));
+          case uint16_t(Op::I32Load):
+            CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I32, 4, nullptr));
+          case uint16_t(Op::I64Load8S):
+          case uint16_t(Op::I64Load8U):
+            CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I64, 1, nullptr));
+          case uint16_t(Op::I64Load16S):
+          case uint16_t(Op::I64Load16U):
+            CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I64, 2, nullptr));
+          case uint16_t(Op::I64Load32S):
+          case uint16_t(Op::I64Load32U):
+            CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I64, 4, nullptr));
+          case uint16_t(Op::I64Load):
+            CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I64, 8, nullptr));
+          case uint16_t(Op::F32Load):
+            CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::F32, 4, nullptr));
+          case uint16_t(Op::F64Load):
+            CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::F64, 8, nullptr));
+          case uint16_t(Op::I32Store8):
+            CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I32, 1, nullptr, nullptr));
+          case uint16_t(Op::I32Store16):
+            CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I32, 2, nullptr, nullptr));
+          case uint16_t(Op::I32Store):
+            CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I32, 4, nullptr, nullptr));
+          case uint16_t(Op::I64Store8):
+            CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I64, 1, nullptr, nullptr));
+          case uint16_t(Op::I64Store16):
+            CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I64, 2, nullptr, nullptr));
+          case uint16_t(Op::I64Store32):
+            CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I64, 4, nullptr, nullptr));
+          case uint16_t(Op::I64Store):
+            CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I64, 8, nullptr, nullptr));
+          case uint16_t(Op::F32Store):
+            CHECK(f.checkHasMemory() && f.iter().readStore(ValType::F32, 4, nullptr, nullptr));
+          case uint16_t(Op::F64Store):
+            CHECK(f.checkHasMemory() && f.iter().readStore(ValType::F64, 8, nullptr, nullptr));
+          case uint16_t(Op::GrowMemory):
+            CHECK(f.checkHasMemory() && f.iter().readGrowMemory(nullptr));
+          case uint16_t(Op::CurrentMemory):
+            CHECK(f.checkHasMemory() && f.iter().readCurrentMemory());
+          case uint16_t(Op::Br):
+            CHECK(f.iter().readBr(nullptr, nullptr, nullptr));
+          case uint16_t(Op::BrIf):
+            CHECK(f.iter().readBrIf(nullptr, nullptr, nullptr, nullptr));
+          case uint16_t(Op::BrTable):
+            CHECK(DecodeBrTable(f));
+          case uint16_t(Op::Return):
+            CHECK(f.iter().readReturn(nullptr));
+          case uint16_t(Op::Unreachable):
+            CHECK(f.iter().readUnreachable());
+          default:
+            return f.iter().unrecognizedOpcode(op);
+        }
+    }
+
+    MOZ_CRASH("unreachable");
+
+#undef CHECK
+}
+
+bool
+wasm::ValidateFunctionBody(const ModuleEnvironment& env, uint32_t funcIndex, Decoder& d)
+{
+    ValTypeVector locals;
+    const Sig& sig = *env.funcSigs[funcIndex];
+    if (!locals.appendAll(sig.args()))
+        return false;
+
+    if (!DecodeLocalEntries(d, ModuleKind::Wasm, &locals))
+        return false;
+
+    FunctionDecoder f(env, locals, d);
+
+    if (!f.iter().readFunctionStart(sig.ret()))
+        return false;
+
+    if (!DecodeFunctionBodyExprs(f))
+        return false;
+
+    return f.iter().readFunctionEnd();
+}
+
+// Section macros.
+
+static bool
+DecodePreamble(Decoder& d)
+{
+    uint32_t u32;
+    if (!d.readFixedU32(&u32) || u32 != MagicNumber)
+        return d.fail("failed to match magic number");
+
+    if (!d.readFixedU32(&u32) || u32 != EncodingVersion)
+        return d.fail("binary version 0x%" PRIx32 " does not match expected version 0x%" PRIx32,
+                      u32, EncodingVersion);
+
+    return true;
+}
+
+static bool
+DecodeTypeSection(Decoder& d, SigWithIdVector* sigs)
+{
+    uint32_t sectionStart, sectionSize;
+    if (!d.startSection(SectionId::Type, &sectionStart, &sectionSize, "type"))
+        return false;
+    if (sectionStart == Decoder::NotStarted)
+        return true;
+
+    uint32_t numSigs;
+    if (!d.readVarU32(&numSigs))
+        return d.fail("expected number of signatures");
+
+    if (numSigs > MaxSigs)
+        return d.fail("too many signatures");
+
+    if (!sigs->resize(numSigs))
+        return false;
+
+    for (uint32_t sigIndex = 0; sigIndex < numSigs; sigIndex++) {
+        uint32_t form;
+        if (!d.readVarU32(&form) || form != uint32_t(TypeCode::Func))
+            return d.fail("expected function form");
+
+        uint32_t numArgs;
+        if (!d.readVarU32(&numArgs))
+            return d.fail("bad number of function args");
+
+        if (numArgs > MaxArgsPerFunc)
+            return d.fail("too many arguments in signature");
+
+        ValTypeVector args;
+        if (!args.resize(numArgs))
+            return false;
+
+        for (uint32_t i = 0; i < numArgs; i++) {
+            if (!DecodeValType(d, ModuleKind::Wasm, &args[i]))
+                return false;
+        }
+
+        uint32_t numRets;
+        if (!d.readVarU32(&numRets))
+            return d.fail("bad number of function returns");
+
+        if (numRets > 1)
+            return d.fail("too many returns in signature");
+
+        ExprType result = ExprType::Void;
+
+        if (numRets == 1) {
+            ValType type;
+            if (!DecodeValType(d, ModuleKind::Wasm, &type))
+                return false;
+
+            result = ToExprType(type);
+        }
+
+        (*sigs)[sigIndex] = Sig(Move(args), result);
+    }
+
+    if (!d.finishSection(sectionStart, sectionSize, "type"))
+        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
+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;
+}
+
+static bool
+DecodeLimits(Decoder& d, Limits* limits)
+{
+    uint32_t flags;
+    if (!d.readVarU32(&flags))
+        return d.fail("expected flags");
+
+    if (flags & ~uint32_t(0x1))
+        return d.fail("unexpected bits set in flags: %" PRIu32, (flags & ~uint32_t(0x1)));
+
+    if (!d.readVarU32(&limits->initial))
+        return d.fail("expected initial length");
+
+    if (flags & 0x1) {
+        uint32_t maximum;
+        if (!d.readVarU32(&maximum))
+            return d.fail("expected maximum length");
+
+        if (limits->initial > maximum) {
+            return d.fail("memory size minimum must not be greater than maximum; "
+                          "maximum length %" PRIu32 " is less than initial length %" PRIu32,
+                          maximum, limits->initial);
+        }
+
+        limits->maximum.emplace(maximum);
+    }
+
+    return true;
+}
+
+static bool
+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 (limits.initial > MaxTableElems)
+        return d.fail("too many table elements");
+
+    if (tables->length())
+        return d.fail("already have default table");
+
+    return 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 (!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
+DecodeGlobalType(Decoder& d, ValType* type, bool* isMutable)
+{
+    if (!DecodeValType(d, ModuleKind::Wasm, type))
+        return false;
+
+    uint32_t flags;
+    if (!d.readVarU32(&flags))
+        return d.fail("expected global flags");
+
+    if (flags & ~uint32_t(GlobalFlags::AllowedMask))
+        return d.fail("unexpected bits set in global flags");
+
+    *isMutable = flags & uint32_t(GlobalFlags::IsMutable);
+    return true;
+}
+
+static bool
+DecodeMemoryLimits(Decoder& d, ModuleEnvironment* env)
+{
+    if (env->usesMemory())
+        return d.fail("already have default memory");
+
+    Limits memory;
+    if (!DecodeLimits(d, &memory))
+        return false;
+
+    CheckedInt<uint32_t> initialBytes = memory.initial;
+    initialBytes *= PageSize;
+    if (!initialBytes.isValid() || initialBytes.value() > uint32_t(INT32_MAX))
+        return d.fail("initial memory size too big");
+
+    memory.initial = initialBytes.value();
+
+    if (memory.maximum) {
+        CheckedInt<uint32_t> maximumBytes = *memory.maximum;
+        maximumBytes *= PageSize;
+        if (!maximumBytes.isValid())
+            return d.fail("maximum memory size too big");
+
+        memory.maximum = Some(maximumBytes.value());
+    }
+
+    env->memoryUsage = MemoryUsage::Unshared;
+    env->minMemoryLength = memory.initial;
+    env->maxMemoryLength = memory.maximum;
+    return true;
+}
+
+static bool
+DecodeImport(Decoder& d, ModuleEnvironment* env)
+{
+    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, env->sigs, &sigIndex))
+            return false;
+        if (!env->funcSigs.append(&env->sigs[sigIndex]))
+            return false;
+        break;
+      }
+      case DefinitionKind::Table: {
+        if (!DecodeTableLimits(d, &env->tables))
+            return false;
+        env->tables.back().external = true;
+        break;
+      }
+      case DefinitionKind::Memory: {
+        if (!DecodeMemoryLimits(d, env))
+            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 (!env->globals.append(GlobalDesc(type, isMutable, env->globals.length())))
+            return false;
+        break;
+      }
+      default:
+        return d.fail("unsupported import kind");
+    }
+
+    return env->imports.emplaceBack(Move(moduleName), Move(funcName), importKind);
+}
+
+static bool
+DecodeImportSection(Decoder& d, ModuleEnvironment* env)
+{
+    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, env))
+            return false;
+    }
+
+    if (!d.finishSection(sectionStart, sectionSize, "import"))
+        return false;
+
+    // The global data offsets will be filled in by ModuleGenerator::init.
+    if (!env->funcImportGlobalDataOffsets.resize(env->funcSigs.length()))
+        return false;
+
+    return true;
+}
+
+static bool
+DecodeFunctionSection(Decoder& d, ModuleEnvironment* env)
+{
+    uint32_t sectionStart, sectionSize;
+    if (!d.startSection(SectionId::Function, &sectionStart, &sectionSize, "function"))
+        return false;
+    if (sectionStart == Decoder::NotStarted)
+        return true;
+
+    uint32_t numDefs;
+    if (!d.readVarU32(&numDefs))
+        return d.fail("expected number of function definitions");
+
+    uint32_t numFuncs = env->funcSigs.length() + numDefs;
+    if (numFuncs > MaxFuncs)
+        return d.fail("too many functions");
+
+    if (!env->funcSigs.reserve(numFuncs))
+        return false;
+
+    for (uint32_t i = 0; i < numDefs; i++) {
+        uint32_t sigIndex;
+        if (!DecodeSignatureIndex(d, env->sigs, &sigIndex))
+            return false;
+        env->funcSigs.infallibleAppend(&env->sigs[sigIndex]);
+    }
+
+    if (!d.finishSection(sectionStart, sectionSize, "function"))
+        return false;
+
+    return true;
+}
+
+static bool
+DecodeTableSection(Decoder& d, TableDescVector* tables)
+{
+    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 (!DecodeTableLimits(d, tables))
+        return false;
+
+    if (!d.finishSection(sectionStart, sectionSize, "table"))
+        return false;
+
+    return true;
+}
+
+static bool
+DecodeMemorySection(Decoder& d, ModuleEnvironment* env)
+{
+    uint32_t sectionStart, sectionSize;
+    if (!d.startSection(SectionId::Memory, &sectionStart, &sectionSize, "memory"))
+        return false;
+    if (sectionStart == Decoder::NotStarted)
+        return true;
+
+    uint32_t numMemories;
+    if (!d.readVarU32(&numMemories))
+        return d.fail("failed to read number of memories");
+
+    if (numMemories != 1)
+        return d.fail("the number of memories must be exactly one");
+
+    if (!DecodeMemoryLimits(d, env))
+        return false;
+
+    if (!d.finishSection(sectionStart, sectionSize, "memory"))
+        return false;
+
+    return true;
+}
+
+static bool
+DecodeInitializerExpression(Decoder& d, const GlobalDescVector& globals, ValType expected,
+                            InitExpr* init)
+{
+    uint16_t op;
+    if (!d.readOp(&op))
+        return d.fail("failed to read initializer type");
+
+    switch (op) {
+      case uint16_t(Op::I32Const): {
+        int32_t i32;
+        if (!d.readVarS32(&i32))
+            return d.fail("failed to read initializer i32 expression");
+        *init = InitExpr(Val(uint32_t(i32)));
+        break;
+      }
+      case uint16_t(Op::I64Const): {
+        int64_t i64;
+        if (!d.readVarS64(&i64))
+            return d.fail("failed to read initializer i64 expression");
+        *init = InitExpr(Val(uint64_t(i64)));
+        break;
+      }
+      case uint16_t(Op::F32Const): {
+        RawF32 f32;
+        if (!d.readFixedF32(&f32))
+            return d.fail("failed to read initializer f32 expression");
+        *init = InitExpr(Val(f32));
+        break;
+      }
+      case uint16_t(Op::F64Const): {
+        RawF64 f64;
+        if (!d.readFixedF64(&f64))
+            return d.fail("failed to read initializer f64 expression");
+        *init = InitExpr(Val(f64));
+        break;
+      }
+      case uint16_t(Op::GetGlobal): {
+        uint32_t i;
+        if (!d.readVarU32(&i))
+            return d.fail("failed to read get_global index in initializer expression");
+        if (i >= globals.length())
+            return d.fail("global index out of range in initializer expression");
+        if (!globals[i].isImport() || globals[i].isMutable())
+            return d.fail("initializer expression must reference a global immutable import");
+        *init = InitExpr(i, globals[i].type());
+        break;
+      }
+      default: {
+        return d.fail("unexpected initializer expression");
+      }
+    }
+
+    if (expected != init->type())
+        return d.fail("type mismatch: initializer type and expected type don't match");
+
+    uint16_t end;
+    if (!d.readOp(&end) || end != uint16_t(Op::End))
+        return d.fail("failed to read end of initializer expression");
+
+    return true;
+}
+
+static bool
+DecodeGlobalSection(Decoder& d, GlobalDescVector* globals)
+{
+    uint32_t sectionStart, sectionSize;
+    if (!d.startSection(SectionId::Global, &sectionStart, &sectionSize, "global"))
+        return false;
+    if (sectionStart == Decoder::NotStarted)
+        return true;
+
+    uint32_t numDefs;
+    if (!d.readVarU32(&numDefs))
+        return d.fail("expected number of globals");
+
+    uint32_t numGlobals = globals->length() + numDefs;
+    if (numGlobals > MaxGlobals)
+        return d.fail("too many globals");
+
+    if (!globals->reserve(numGlobals))
+        return false;
+
+    for (uint32_t i = 0; i < numDefs; i++) {
+        ValType type;
+        bool isMutable;
+        if (!DecodeGlobalType(d, &type, &isMutable))
+            return false;
+
+        InitExpr initializer;
+        if (!DecodeInitializerExpression(d, *globals, type, &initializer))
+            return false;
+
+        globals->infallibleAppend(GlobalDesc(initializer, isMutable));
+    }
+
+    if (!d.finishSection(sectionStart, sectionSize, "global"))
+        return false;
+
+    return true;
+}
+
+typedef HashSet<const char*, CStringHasher, SystemAllocPolicy> CStringSet;
+
+static UniqueChars
+DecodeExportName(Decoder& d, CStringSet* dupSet)
+{
+    UniqueChars exportName = DecodeName(d);
+    if (!exportName) {
+        d.fail("expected valid export name");
+        return nullptr;
+    }
+
+    CStringSet::AddPtr p = dupSet->lookupForAdd(exportName.get());
+    if (p) {
+        d.fail("duplicate export");
+        return nullptr;
+    }
+
+    if (!dupSet->add(p, exportName.get()))
+        return nullptr;
+
+    return Move(exportName);
+}
+
+static bool
+DecodeExport(Decoder& d, ModuleEnvironment* env, CStringSet* dupSet)
+{
+    UniqueChars fieldName = DecodeExportName(d, dupSet);
+    if (!fieldName)
+        return false;
+
+    uint32_t exportKind;
+    if (!d.readVarU32(&exportKind))
+        return d.fail("failed to read export kind");
+
+    switch (DefinitionKind(exportKind)) {
+      case DefinitionKind::Function: {
+        uint32_t funcIndex;
+        if (!d.readVarU32(&funcIndex))
+            return d.fail("expected function index");
+
+        if (funcIndex >= env->numFuncs())
+            return d.fail("exported function index out of bounds");
+
+        return env->exports.emplaceBack(Move(fieldName), funcIndex, DefinitionKind::Function);
+      }
+      case DefinitionKind::Table: {
+        uint32_t tableIndex;
+        if (!d.readVarU32(&tableIndex))
+            return d.fail("expected table index");
+
+        if (tableIndex >= env->tables.length())
+            return d.fail("exported table index out of bounds");
+
+        MOZ_ASSERT(env->tables.length() == 1);
+        env->tables[0].external = true;
+
+        return env->exports.emplaceBack(Move(fieldName), DefinitionKind::Table);
+      }
+      case DefinitionKind::Memory: {
+        uint32_t memoryIndex;
+        if (!d.readVarU32(&memoryIndex))
+            return d.fail("expected memory index");
+
+        if (memoryIndex > 0 || !env->usesMemory())
+            return d.fail("exported memory index out of bounds");
+
+        return env->exports.emplaceBack(Move(fieldName), DefinitionKind::Memory);
+      }
+      case DefinitionKind::Global: {
+        uint32_t globalIndex;
+        if (!d.readVarU32(&globalIndex))
+            return d.fail("expected global index");
+
+        if (globalIndex >= env->globals.length())
+            return d.fail("exported global index out of bounds");
+
+        const GlobalDesc& global = env->globals[globalIndex];
+        if (!GlobalIsJSCompatible(d, global.type(), global.isMutable()))
+            return false;
+
+        return env->exports.emplaceBack(Move(fieldName), globalIndex, DefinitionKind::Global);
+      }
+      default:
+        return d.fail("unexpected export kind");
+    }
+
+    MOZ_CRASH("unreachable");
+}
+
+static bool
+DecodeExportSection(Decoder& d, ModuleEnvironment* env)
+{
+    uint32_t sectionStart, sectionSize;
+    if (!d.startSection(SectionId::Export, &sectionStart, &sectionSize, "export"))
+        return false;
+    if (sectionStart == Decoder::NotStarted)
+        return true;
+
+    CStringSet dupSet;
+    if (!dupSet.init())
+        return false;
+
+    uint32_t numExports;
+    if (!d.readVarU32(&numExports))
+        return d.fail("failed to read number of exports");
+
+    if (numExports > MaxExports)
+        return d.fail("too many exports");
+
+    for (uint32_t i = 0; i < numExports; i++) {
+        if (!DecodeExport(d, env, &dupSet))
+            return false;
+    }
+
+    if (!d.finishSection(sectionStart, sectionSize, "export"))
+        return false;
+
+    return true;
+}
+
+static bool
+DecodeStartSection(Decoder& d, ModuleEnvironment* env)
+{
+    uint32_t sectionStart, sectionSize;
+    if (!d.startSection(SectionId::Start, &sectionStart, &sectionSize, "start"))
+        return false;
+    if (sectionStart == Decoder::NotStarted)
+        return true;
+
+    uint32_t funcIndex;
+    if (!d.readVarU32(&funcIndex))
+        return d.fail("failed to read start func index");
+
+    if (funcIndex >= env->numFuncs())
+        return d.fail("unknown start function");
+
+    const Sig& sig = *env->funcSigs[funcIndex];
+    if (!IsVoid(sig.ret()))
+        return d.fail("start function must not return anything");
+
+    if (sig.args().length())
+        return d.fail("start function must be nullary");
+
+    env->startFuncIndex = Some(funcIndex);
+
+    if (!d.finishSection(sectionStart, sectionSize, "start"))
+        return false;
+
+    return true;
+}
+
+static bool
+DecodeElemSection(Decoder& d, ModuleEnvironment* env)
+{
+    uint32_t sectionStart, sectionSize;
+    if (!d.startSection(SectionId::Elem, &sectionStart, &sectionSize, "elem"))
+        return false;
+    if (sectionStart == Decoder::NotStarted)
+        return true;
+
+    uint32_t numSegments;
+    if (!d.readVarU32(&numSegments))
+        return d.fail("failed to read number of elem segments");
+
+    if (numSegments > MaxElemSegments)
+        return d.fail("too many elem segments");
+
+    for (uint32_t i = 0; i < numSegments; i++) {
+        uint32_t tableIndex;
+        if (!d.readVarU32(&tableIndex))
+            return d.fail("expected table index");
+
+        MOZ_ASSERT(env->tables.length() <= 1);
+        if (tableIndex >= env->tables.length())
+            return d.fail("table index out of range");
+
+        InitExpr offset;
+        if (!DecodeInitializerExpression(d, env->globals, ValType::I32, &offset))
+            return false;
+
+        uint32_t numElems;
+        if (!d.readVarU32(&numElems))
+            return d.fail("expected segment size");
+
+        Uint32Vector elemFuncIndices;
+        if (!elemFuncIndices.resize(numElems))
+            return false;
+
+        for (uint32_t i = 0; i < numElems; i++) {
+            if (!d.readVarU32(&elemFuncIndices[i]))
+                return d.fail("failed to read element function index");
+            if (elemFuncIndices[i] >= env->numFuncs())
+                return d.fail("table element out of range");
+        }
+
+        if (!env->elemSegments.emplaceBack(0, offset, Move(elemFuncIndices)))
+            return false;
+
+        env->tables[env->elemSegments.back().tableIndex].external = true;
+    }
+
+    if (!d.finishSection(sectionStart, sectionSize, "elem"))
+        return false;
+
+    return true;
+}
+
+bool
+wasm::DecodeModuleEnvironment(Decoder& d, ModuleEnvironment* env)
+{
+    if (!DecodePreamble(d))
+        return false;
+
+    if (!DecodeTypeSection(d, &env->sigs))
+        return false;
+
+    if (!DecodeImportSection(d, env))
+        return false;
+
+    if (!DecodeFunctionSection(d, env))
+        return false;
+
+    if (!DecodeTableSection(d, &env->tables))
+        return false;
+
+    if (!DecodeMemorySection(d, env))
+        return false;
+
+    if (!DecodeGlobalSection(d, &env->globals))
+        return false;
+
+    if (!DecodeExportSection(d, env))
+        return false;
+
+    if (!DecodeStartSection(d, env))
+        return false;
+
+    if (!DecodeElemSection(d, env))
+        return false;
+
+    return true;
+}
+
+bool
+wasm::DecodeDataSection(Decoder& d, const ModuleEnvironment& env, DataSegmentVector* segments)
+{
+    uint32_t sectionStart, sectionSize;
+    if (!d.startSection(SectionId::Data, &sectionStart, &sectionSize, "data"))
+        return false;
+    if (sectionStart == Decoder::NotStarted)
+        return true;
+
+    if (!env.usesMemory())
+        return d.fail("data section requires a memory section");
+
+    uint32_t numSegments;
+    if (!d.readVarU32(&numSegments))
+        return d.fail("failed to read number of data segments");
+
+    if (numSegments > MaxDataSegments)
+        return d.fail("too many data segments");
+
+    for (uint32_t i = 0; i < numSegments; i++) {
+        uint32_t linearMemoryIndex;
+        if (!d.readVarU32(&linearMemoryIndex))
+            return d.fail("expected linear memory index");
+
+        if (linearMemoryIndex != 0)
+            return d.fail("linear memory index must currently be 0");
+
+        DataSegment seg;
+        if (!DecodeInitializerExpression(d, env.globals, ValType::I32, &seg.offset))
+            return false;
+
+        if (!d.readVarU32(&seg.length))
+            return d.fail("expected segment size");
+
+        seg.bytecodeOffset = d.currentOffset();
+
+        if (!d.readBytes(seg.length))
+            return d.fail("data segment shorter than declared");
+
+        if (!segments->append(seg))
+            return false;
+    }
+
+    if (!d.finishSection(sectionStart, sectionSize, "data"))
+        return false;
+
+    return true;
+}
+
+bool
+wasm::DecodeUnknownSections(Decoder& d)
+{
+    while (!d.done()) {
+        if (!d.skipUserDefinedSection())
+            return false;
+    }
+
+    return true;
+}
new file mode 100644
--- /dev/null
+++ b/js/src/wasm/WasmValidate.h
@@ -0,0 +1,720 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ *
+ * Copyright 2016 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef wasm_validate_h
+#define wasm_validate_h
+
+#include "wasm/WasmCode.h"
+#include "wasm/WasmTypes.h"
+
+namespace js {
+namespace wasm {
+
+// The Encoder class appends bytes to the Bytes object it is given during
+// construction. The client is responsible for the Bytes's lifetime and must
+// keep the Bytes alive as long as the Encoder is used.
+
+class Encoder
+{
+    Bytes& bytes_;
+
+    template <class T>
+    MOZ_MUST_USE bool write(const T& v) {
+        return bytes_.append(reinterpret_cast<const uint8_t*>(&v), sizeof(T));
+    }
+
+    template <typename UInt>
+    MOZ_MUST_USE bool writeVarU(UInt i) {
+        do {
+            uint8_t byte = i & 0x7f;
+            i >>= 7;
+            if (i != 0)
+                byte |= 0x80;
+            if (!bytes_.append(byte))
+                return false;
+        } while (i != 0);
+        return true;
+    }
+
+    template <typename SInt>
+    MOZ_MUST_USE bool writeVarS(SInt i) {
+        bool done;
+        do {
+            uint8_t byte = i & 0x7f;
+            i >>= 7;
+            done = ((i == 0) && !(byte & 0x40)) || ((i == -1) && (byte & 0x40));
+            if (!done)
+                byte |= 0x80;
+            if (!bytes_.append(byte))
+                return false;
+        } while (!done);
+        return true;
+    }
+
+    void patchVarU32(size_t offset, uint32_t patchBits, uint32_t assertBits) {
+        do {
+            uint8_t assertByte = assertBits & 0x7f;
+            uint8_t patchByte = patchBits & 0x7f;
+            assertBits >>= 7;
+            patchBits >>= 7;
+            if (assertBits != 0) {
+                assertByte |= 0x80;
+                patchByte |= 0x80;
+            }
+            MOZ_ASSERT(assertByte == bytes_[offset]);
+            bytes_[offset] = patchByte;
+            offset++;
+        } while(assertBits != 0);
+    }
+
+    void patchFixedU7(size_t offset, uint8_t patchBits, uint8_t assertBits) {
+        MOZ_ASSERT(patchBits <= uint8_t(INT8_MAX));
+        patchFixedU8(offset, patchBits, assertBits);
+    }
+
+    void patchFixedU8(size_t offset, uint8_t patchBits, uint8_t assertBits) {
+        MOZ_ASSERT(bytes_[offset] == assertBits);
+        bytes_[offset] = patchBits;
+    }
+
+    uint32_t varU32ByteLength(size_t offset) const {
+        size_t start = offset;
+        while (bytes_[offset] & 0x80)
+            offset++;
+        return offset - start + 1;
+    }
+
+  public:
+    explicit Encoder(Bytes& bytes)
+      : bytes_(bytes)
+    {
+        MOZ_ASSERT(empty());
+    }
+
+    size_t currentOffset() const { return bytes_.length(); }
+    bool empty() const { return currentOffset() == 0; }
+
+    // Fixed-size encoding operations simply copy the literal bytes (without
+    // attempting to align).
+
+    MOZ_MUST_USE bool writeFixedU7(uint8_t i) {
+        MOZ_ASSERT(i <= uint8_t(INT8_MAX));
+        return writeFixedU8(i);
+    }
+    MOZ_MUST_USE bool writeFixedU8(uint8_t i) {
+        return write<uint8_t>(i);
+    }
+    MOZ_MUST_USE bool writeFixedU32(uint32_t i) {
+        return write<uint32_t>(i);
+    }
+    MOZ_MUST_USE bool writeFixedF32(RawF32 f) {
+        return write<uint32_t>(f.bits());
+    }
+    MOZ_MUST_USE bool writeFixedF64(RawF64 d) {
+        return write<uint64_t>(d.bits());
+    }
+    MOZ_MUST_USE bool writeFixedI8x16(const I8x16& i8x16) {
+        return write<I8x16>(i8x16);
+    }
+    MOZ_MUST_USE bool writeFixedI16x8(const I16x8& i16x8) {
+        return write<I16x8>(i16x8);
+    }
+    MOZ_MUST_USE bool writeFixedI32x4(const I32x4& i32x4) {
+        return write<I32x4>(i32x4);
+    }
+    MOZ_MUST_USE bool writeFixedF32x4(const F32x4& f32x4) {
+        return write<F32x4>(f32x4);
+    }
+
+    // Variable-length encodings that all use LEB128.
+
+    MOZ_MUST_USE bool writeVarU32(uint32_t i) {
+        return writeVarU<uint32_t>(i);
+    }
+    MOZ_MUST_USE bool writeVarS32(int32_t i) {
+        return writeVarS<int32_t>(i);
+    }
+    MOZ_MUST_USE bool writeVarU64(uint64_t i) {
+        return writeVarU<uint64_t>(i);
+    }
+    MOZ_MUST_USE bool writeVarS64(int64_t i) {
+        return writeVarS<int64_t>(i);
+    }
+    MOZ_MUST_USE bool writeValType(ValType type) {
+        static_assert(size_t(TypeCode::Limit) <= UINT8_MAX, "fits");
+        MOZ_ASSERT(size_t(type) < size_t(TypeCode::Limit));
+        return writeFixedU8(uint8_t(type));
+    }
+    MOZ_MUST_USE bool writeBlockType(ExprType type) {
+        static_assert(size_t(TypeCode::Limit) <= UINT8_MAX, "fits");
+        MOZ_ASSERT(size_t(type) < size_t(TypeCode::Limit));
+        return writeFixedU8(uint8_t(type));
+    }
+    MOZ_MUST_USE bool writeOp(Op op) {
+        static_assert(size_t(Op::Limit) <= 2 * UINT8_MAX, "fits");
+        MOZ_ASSERT(size_t(op) < size_t(Op::Limit));
+        if (size_t(op) < UINT8_MAX)
+            return writeFixedU8(uint8_t(op));
+        return writeFixedU8(UINT8_MAX) &&
+               writeFixedU8(size_t(op) - UINT8_MAX);
+    }
+
+    // Fixed-length encodings that allow back-patching.
+
+    MOZ_MUST_USE bool writePatchableFixedU7(size_t* offset) {
+        *offset = bytes_.length();
+        return writeFixedU8(UINT8_MAX);
+    }
+    void patchFixedU7(size_t offset, uint8_t patchBits) {
+        return patchFixedU7(offset, patchBits, UINT8_MAX);
+    }
+
+    // Variable-length encodings that allow back-patching.
+
+    MOZ_MUST_USE bool writePatchableVarU32(size_t* offset) {
+        *offset = bytes_.length();
+        return writeVarU32(UINT32_MAX);
+    }
+    void patchVarU32(size_t offset, uint32_t patchBits) {
+        return patchVarU32(offset, patchBits, UINT32_MAX);
+    }
+
+    // Byte ranges start with an LEB128 length followed by an arbitrary sequence
+    // of bytes. When used for strings, bytes are to be interpreted as utf8.
+
+    MOZ_MUST_USE bool writeBytes(const void* bytes, uint32_t numBytes) {
+        return writeVarU32(numBytes) &&
+               bytes_.append(reinterpret_cast<const uint8_t*>(bytes), numBytes);
+    }
+
+    // A "section" is a contiguous range of bytes that stores its own size so
+    // that it may be trivially skipped without examining the contents. Sections
+    // require backpatching since the size of the section is only known at the
+    // end while the size's varU32 must be stored at the beginning. Immediately
+    // after the section length is the string id of the section.
+
+    MOZ_MUST_USE bool startSection(SectionId id, size_t* offset) {
+        MOZ_ASSERT(id != SectionId::UserDefined); // not supported yet
+
+        return writeVarU32(uint32_t(id)) &&
+               writePatchableVarU32(offset);
+    }
+    void finishSection(size_t offset) {
+        return patchVarU32(offset, bytes_.length() - offset - varU32ByteLength(offset));
+    }
+};
+
+// The Decoder class decodes the bytes in the range it is given during
+// construction. The client is responsible for keeping the byte range alive as
+// long as the Decoder is used.
+
+class Decoder
+{
+    const uint8_t* const beg_;
+    const uint8_t* const end_;
+    const uint8_t* cur_;
+    UniqueChars* error_;
+
+    template <class T>
+    MOZ_MUST_USE bool read(T* out) {
+        if (bytesRemain() < sizeof(T))
+            return false;
+        memcpy((void*)out, cur_, sizeof(T));
+        cur_ += sizeof(T);
+        return true;
+    }
+
+    template <class T>
+    T uncheckedRead() {
+        MOZ_ASSERT(bytesRemain() >= sizeof(T));
+        T ret;
+        memcpy(&ret, cur_, sizeof(T));
+        cur_ += sizeof(T);
+        return ret;
+    }
+
+    template <class T>
+    void uncheckedRead(T* ret) {
+        MOZ_ASSERT(bytesRemain() >= sizeof(T));
+        memcpy(ret, cur_, sizeof(T));
+        cur_ += sizeof(T);
+    }
+
+    template <typename UInt>
+    MOZ_MUST_USE bool readVarU(UInt* out) {
+        const unsigned numBits = sizeof(UInt) * CHAR_BIT;
+        const unsigned remainderBits = numBits % 7;
+        const unsigned numBitsInSevens = numBits - remainderBits;
+        UInt u = 0;
+        uint8_t byte;
+        UInt shift = 0;
+        do {
+            if (!readFixedU8(&byte))
+                return false;
+            if (!(byte & 0x80)) {
+                *out = u | UInt(byte) << shift;
+                return true;
+            }
+            u |= UInt(byte & 0x7F) << shift;
+            shift += 7;
+        } while (shift != numBitsInSevens);
+        if (!readFixedU8(&byte) || (byte & (unsigned(-1) << remainderBits)))
+            return false;
+        *out = u | (UInt(byte) << numBitsInSevens);
+        return true;
+    }
+
+    template <typename SInt>
+    MOZ_MUST_USE bool readVarS(SInt* out) {
+        const unsigned numBits = sizeof(SInt) * CHAR_BIT;
+        const unsigned remainderBits = numBits % 7;
+        const unsigned numBitsInSevens = numBits - remainderBits;
+        SInt s = 0;
+        uint8_t byte;
+        unsigned shift = 0;
+        do {
+            if (!readFixedU8(&byte))
+                return false;
+            s |= SInt(byte & 0x7f) << shift;
+            shift += 7;
+            if (!(byte & 0x80)) {
+                if (byte & 0x40)
+                    s |= SInt(-1) << shift;
+                *out = s;
+                return true;
+            }
+        } while (shift < numBitsInSevens);
+        if (!remainderBits || !readFixedU8(&byte) || (byte & 0x80))
+            return false;
+        uint8_t mask = 0x7f & (uint8_t(-1) << remainderBits);
+        if ((byte & mask) != ((byte & (1 << (remainderBits - 1))) ? mask : 0))
+            return false;
+        *out = s | SInt(byte) << shift;
+        return true;
+    }
+
+  public:
+    Decoder(const uint8_t* begin, const uint8_t* end, UniqueChars* error)
+      : beg_(begin),
+        end_(end),
+        cur_(begin),
+        error_(error)
+    {
+        MOZ_ASSERT(begin <= end);
+    }
+    explicit Decoder(const Bytes& bytes, UniqueChars* error = nullptr)
+      : beg_(bytes.begin()),
+        end_(bytes.end()),
+        cur_(bytes.begin()),
+        error_(error)
+    {}
+
+    bool fail(const char* msg, ...) MOZ_FORMAT_PRINTF(2, 3);
+    bool fail(UniqueChars msg);
+    void clearError() {
+        if (error_)
+            error_->reset();
+    }
+
+    bool done() const {
+        MOZ_ASSERT(cur_ <= end_);
+        return cur_ == end_;
+    }
+
+    size_t bytesRemain() const {
+        MOZ_ASSERT(end_ >= cur_);
+        return size_t(end_ - cur_);
+    }
+    const uint8_t* currentPosition() const {
+        return cur_;
+    }
+    size_t currentOffset() const {
+        return cur_ - beg_;
+    }
+    const uint8_t* begin() const {
+        return beg_;
+    }
+
+    // Fixed-size encoding operations simply copy the literal bytes (without
+    // attempting to align).
+
+    MOZ_MUST_USE bool readFixedU8(uint8_t* i) {
+        return read<uint8_t>(i);
+    }
+    MOZ_MUST_USE bool readFixedU32(uint32_t* u) {
+        return read<uint32_t>(u);
+    }
+    MOZ_MUST_USE bool readFixedF32(RawF32* f) {
+        uint32_t u;
+        if (!read<uint32_t>(&u))
+            return false;
+        *f = RawF32::fromBits(u);
+        return true;
+    }
+    MOZ_MUST_USE bool readFixedF64(RawF64* d) {
+        uint64_t u;
+        if (!read<uint64_t>(&u))
+            return false;
+        *d = RawF64::fromBits(u);
+        return true;
+    }
+    MOZ_MUST_USE bool readFixedI8x16(I8x16* i8x16) {
+        return read<I8x16>(i8x16);
+    }
+    MOZ_MUST_USE bool readFixedI16x8(I16x8* i16x8) {
+        return read<I16x8>(i16x8);
+    }
+    MOZ_MUST_USE bool readFixedI32x4(I32x4* i32x4) {
+        return read<I32x4>(i32x4);
+    }
+    MOZ_MUST_USE bool readFixedF32x4(F32x4* f32x4) {
+        return read<F32x4>(f32x4);
+    }
+
+    // Variable-length encodings that all use LEB128.
+
+    MOZ_MUST_USE bool readVarU32(uint32_t* out) {
+        return readVarU<uint32_t>(out);
+    }
+    MOZ_MUST_USE bool readVarS32(int32_t* out) {
+        return readVarS<int32_t>(out);
+    }
+    MOZ_MUST_USE bool readVarU64(uint64_t* out) {
+        return readVarU<uint64_t>(out);
+    }
+    MOZ_MUST_USE bool readVarS64(int64_t* out) {
+        return readVarS<int64_t>(out);
+    }
+    MOZ_MUST_USE bool readValType(uint8_t* type) {
+        static_assert(uint8_t(TypeCode::Limit) <= UINT8_MAX, "fits");
+        return readFixedU8(type);
+    }
+    MOZ_MUST_USE bool readBlockType(uint8_t* type) {
+        static_assert(size_t(TypeCode::Limit) <= UINT8_MAX, "fits");
+        return readFixedU8(type);
+    }
+    MOZ_MUST_USE bool readOp(uint16_t* op) {
+        static_assert(size_t(Op::Limit) <= 2 * UINT8_MAX, "fits");
+        uint8_t u8;
+        if (!readFixedU8(&u8))
+            return false;
+        if (MOZ_LIKELY(u8 != UINT8_MAX)) {
+            *op = u8;
+            return true;
+        }
+        if (!readFixedU8(&u8))
+            return false;
+        *op = uint16_t(u8) + UINT8_MAX;
+        return true;
+    }
+
+    // See writeBytes comment.
+
+    MOZ_MUST_USE bool readBytes(uint32_t numBytes, const uint8_t** bytes = nullptr) {
+        if (bytes)
+            *bytes = cur_;
+        if (bytesRemain() < numBytes)
+            return false;
+        cur_ += numBytes;
+        return true;
+    }
+
+    // See "section" description in Encoder.
+
+    static const uint32_t NotStarted = UINT32_MAX;
+
+    MOZ_MUST_USE bool startSection(SectionId id,
+                                   uint32_t* startOffset,
+                                   uint32_t* size,
+                                   const char* sectionName)
+    {
+        const uint8_t* const before = cur_;
+        const uint8_t* beforeId = before;
+        uint32_t idValue;
+        if (!readVarU32(&idValue))
+            goto backup;
+        while (idValue != uint32_t(id)) {
+            if (idValue != uint32_t(SectionId::UserDefined))
+                goto backup;
+            // Rewind to the section id since skipUserDefinedSection expects it.
+            cur_ = beforeId;
+            if (!skipUserDefinedSection())
+                return false;
+            beforeId = cur_;
+            if (!readVarU32(&idValue))
+                goto backup;
+        }
+        if (!readVarU32(size))
+            goto fail;
+        if (bytesRemain() < *size)
+            goto fail;
+        *startOffset = cur_ - beg_;
+        return true;
+      backup:
+        cur_ = before;
+        *startOffset = NotStarted;
+        return true;
+      fail:
+        return fail("failed to start %s section", sectionName);
+    }
+    MOZ_MUST_USE bool finishSection(uint32_t startOffset, uint32_t size,
+                                    const char* sectionName)
+    {
+        if (size != (cur_ - beg_) - startOffset)
+            return fail("byte size mismatch in %s section", sectionName);
+        return true;
+    }
+
+    // "User sections" do not cause validation errors unless the error is in
+    // the user-defined section header itself.
+
+    MOZ_MUST_USE bool startUserDefinedSection(const char* expectedId,
+                                              size_t expectedIdSize,
+                                              uint32_t* sectionStart,
+                                              uint32_t* sectionSize)
+    {
+        const uint8_t* const before = cur_;
+        while (true) {
+            if (!startSection(SectionId::UserDefined, sectionStart, sectionSize, "user-defined"))
+                return false;
+            if (*sectionStart == NotStarted) {
+                cur_ = before;
+                return true;
+            }
+            uint32_t idSize;
+            if (!readVarU32(&idSize))
+                goto fail;
+            if (idSize > bytesRemain() || currentOffset() + idSize > *sectionStart + *sectionSize)
+                goto fail;
+            if (expectedId && (expectedIdSize != idSize || !!memcmp(cur_, expectedId, idSize))) {
+                finishUserDefinedSection(*sectionStart, *sectionSize);
+                continue;
+            }
+            cur_ += idSize;
+            return true;
+        }
+        MOZ_CRASH("unreachable");
+      fail:
+        return fail("failed to start user-defined section");
+    }
+    template <size_t IdSizeWith0>
+    MOZ_MUST_USE bool startUserDefinedSection(const char (&id)[IdSizeWith0],
+                                              uint32_t* sectionStart,
+                                              uint32_t* sectionSize)
+    {
+        MOZ_ASSERT(id[IdSizeWith0 - 1] == '\0');
+        return startUserDefinedSection(id, IdSizeWith0 - 1, sectionStart, sectionSize);
+    }
+    void finishUserDefinedSection(uint32_t sectionStart, uint32_t sectionSize) {
+        MOZ_ASSERT(cur_ >= beg_);
+        MOZ_ASSERT(cur_ <= end_);
+        cur_ = (beg_ + sectionStart) + sectionSize;
+        MOZ_ASSERT(cur_ <= end_);
+        clearError();
+    }
+    MOZ_MUST_USE bool skipUserDefinedSection() {
+        uint32_t sectionStart, sectionSize;
+        if (!startUserDefinedSection(nullptr, 0, &sectionStart, &sectionSize))
+            return false;
+        if (sectionStart == NotStarted)
+            return fail("expected user-defined section");
+        finishUserDefinedSection(sectionStart, sectionSize);
+        return true;
+    }
+
+    // The infallible "unchecked" decoding functions can be used when we are
+    // sure that the bytes are well-formed (by construction or due to previous
+    // validation).
+
+    uint8_t uncheckedReadFixedU8() {
+        return uncheckedRead<uint8_t>();
+    }
+    uint32_t uncheckedReadFixedU32() {
+        return uncheckedRead<uint32_t>();
+    }
+    RawF32 uncheckedReadFixedF32() {
+        return RawF32::fromBits(uncheckedRead<uint32_t>());
+    }
+    RawF64 uncheckedReadFixedF64() {
+        return RawF64::fromBits(uncheckedRead<uint64_t>());
+    }
+    template <typename UInt>
+    UInt uncheckedReadVarU() {
+        static const unsigned numBits = sizeof(UInt) * CHAR_BIT;
+        static const unsigned remainderBits = numBits % 7;
+        static const unsigned numBitsInSevens = numBits - remainderBits;
+        UInt decoded = 0;
+        uint32_t shift = 0;
+        do {
+            uint8_t byte = *cur_++;
+            if (!(byte & 0x80))
+                return decoded | (UInt(byte) << shift);
+            decoded |= UInt(byte & 0x7f) << shift;
+            shift += 7;
+        } while (shift != numBitsInSevens);
+        uint8_t byte = *cur_++;
+        MOZ_ASSERT(!(byte & 0xf0));
+        return decoded | (UInt(byte) << numBitsInSevens);
+    }
+    uint32_t uncheckedReadVarU32() {
+        return uncheckedReadVarU<uint32_t>();
+    }
+    int32_t uncheckedReadVarS32() {
+        int32_t i32 = 0;
+        MOZ_ALWAYS_TRUE(readVarS32(&i32));
+        return i32;
+    }
+    uint64_t uncheckedReadVarU64() {
+        return uncheckedReadVarU<uint64_t>();
+    }
+    int64_t uncheckedReadVarS64() {
+        int64_t i64 = 0;
+        MOZ_ALWAYS_TRUE(readVarS64(&i64));
+        return i64;
+    }
+    ValType uncheckedReadValType() {
+        return (ValType)uncheckedReadFixedU8();
+    }
+    Op uncheckedReadOp() {
+        static_assert(size_t(Op::Limit) <= 2 * UINT8_MAX, "fits");
+        uint8_t u8 = uncheckedReadFixedU8();
+        return u8 != UINT8_MAX
+               ? Op(u8)
+               : Op(uncheckedReadFixedU8() + UINT8_MAX);
+    }
+    void uncheckedReadFixedI8x16(I8x16* i8x16) {
+        struct T { I8x16 v; };
+        T t = uncheckedRead<T>();
+        memcpy(i8x16, &t, sizeof(t));
+    }
+    void uncheckedReadFixedI16x8(I16x8* i16x8) {
+        struct T { I16x8 v; };
+        T t = uncheckedRead<T>();
+        memcpy(i16x8, &t, sizeof(t));
+    }
+    void uncheckedReadFixedI32x4(I32x4* i32x4) {
+        struct T { I32x4 v; };
+        T t = uncheckedRead<T>();
+        memcpy(i32x4, &t, sizeof(t));
+    }
+    void uncheckedReadFixedF32x4(F32x4* f32x4) {
+        struct T { F32x4 v; };
+        T t = uncheckedRead<T>();
+        memcpy(f32x4, &t, sizeof(t));
+    }
+};
+
+// Reusable macro encoding/decoding functions reused by both the two
+// encoders (AsmJS/WasmTextToBinary) and all the decoders
+// (WasmCompile/WasmIonCompile/WasmBaselineCompile/WasmBinaryToText).
+
+// Misc helpers.
+
+MOZ_MUST_USE bool
+EncodeLocalEntries(Encoder& d, const ValTypeVector& locals);
+
+MOZ_MUST_USE bool
+DecodeLocalEntries(Decoder& d, ModuleKind kind, ValTypeVector* locals);
+
+// ModuleEnvironment contains all the state necessary to validate, process or
+// render functions. It is created by decoding all the sections before the wasm
+// code section and then used immutably during. When compiling a module using a
+// ModuleGenerator, the ModuleEnvironment holds state shared between the
+// ModuleGenerator thread and background compile threads. All the threads
+// are given a read-only view of the ModuleEnvironment, thus preventing race
+// conditions.
+
+struct ModuleEnvironment
+{
+    ModuleKind                kind;
+    MemoryUsage               memoryUsage;
+    mozilla::Atomic<uint32_t> minMemoryLength;
+    Maybe<uint32_t>           maxMemoryLength;
+
+    SigWithIdVector           sigs;
+    SigWithIdPtrVector        funcSigs;
+    Uint32Vector              funcImportGlobalDataOffsets;
+    GlobalDescVector          globals;
+    TableDescVector           tables;
+    Uint32Vector              asmJSSigToTableIndex;
+    ImportVector              imports;
+    ExportVector              exports;
+    Maybe<uint32_t>           startFuncIndex;
+    ElemSegmentVector         elemSegments;
+
+    explicit ModuleEnvironment(ModuleKind kind = ModuleKind::Wasm)
+      : kind(kind),
+        memoryUsage(MemoryUsage::None),
+        minMemoryLength(0)
+    {}
+
+    size_t numTables() const {
+        return tables.length();
+    }
+    size_t numSigs() const {
+        return sigs.length();
+    }
+    size_t numFuncs() const {
+        // asm.js pre-reserves a bunch of function index space which is
+        // incrementally filled in during function-body validation. Thus, there
+        // are a few possible interpretations of numFuncs() (total index space
+        // size vs.  exact number of imports/definitions encountered so far) and
+        // to simplify things we simply only define this quantity for wasm.
+        MOZ_ASSERT(!isAsmJS());
+        return funcSigs.length();
+    }
+    size_t numFuncDefs() const {
+        // asm.js overallocates the length of funcSigs and in general does not
+        // know the number of function definitions until it's done compiling.
+        MOZ_ASSERT(!isAsmJS());
+        return funcSigs.length() - funcImportGlobalDataOffsets.length();
+    }
+    bool usesMemory() const {
+        return UsesMemory(memoryUsage);
+    }
+    bool isAsmJS() const {
+        return kind == ModuleKind::AsmJS;
+    }
+    bool funcIsImport(uint32_t funcIndex) const {
+        return funcIndex < funcImportGlobalDataOffsets.length();
+    }
+    uint32_t funcIndexToSigIndex(uint32_t funcIndex) const {
+        return funcSigs[funcIndex] - sigs.begin();
+    }
+};
+
+typedef UniquePtr<ModuleEnvironment> UniqueModuleEnvironment;
+
+// Section macros.
+
+MOZ_MUST_USE bool
+DecodeModuleEnvironment(Decoder& d, ModuleEnvironment* env);
+
+MOZ_MUST_USE bool
+DecodeDataSection(Decoder& d, const ModuleEnvironment& env, DataSegmentVector* segments);
+
+MOZ_MUST_USE bool
+DecodeUnknownSections(Decoder& d);
+
+MOZ_MUST_USE bool
+ValidateFunctionBody(const ModuleEnvironment& env, uint32_t funcIndex, Decoder& d);
+
+}  // namespace wasm
+}  // namespace js
+
+#endif // namespace wasm_validate_h