Bug 1271375 - Extracts WASM AST and fixes wasmBinaryToText. r?luke draft
authorYury Delendik <ydelendik@mozilla.com>
Thu, 12 May 2016 14:45:29 -0500
changeset 366407 5ae212132e141ca57a286f595b95a26d3c771402
parent 365730 3461f3cae78495f100a0f7d3d2e0b89292d3ec02
child 520777 2a4275cd581c51df42b81754f87711b804883f20
push id17985
push userydelendik@mozilla.com
push dateThu, 12 May 2016 19:48:47 +0000
reviewersluke
bugs1271375
milestone49.0a1
Bug 1271375 - Extracts WASM AST and fixes wasmBinaryToText. r?luke MozReview-Commit-ID: 3ypQkippyzK
js/src/asmjs/WasmAST.h
js/src/asmjs/WasmBinaryToAST.cpp
js/src/asmjs/WasmBinaryToAST.h
js/src/asmjs/WasmBinaryToText.cpp
js/src/asmjs/WasmTextToBinary.cpp
js/src/jit-test/tests/wasm/totext1.js
js/src/moz.build
new file mode 100644
--- /dev/null
+++ b/js/src/asmjs/WasmAST.h
@@ -0,0 +1,765 @@
+/* -*- 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 2015 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 wasmast_h
+#define wasmast_h
+
+#include "asmjs/WasmTypes.h"
+
+#include "ds/LifoAlloc.h"
+#include "js/HashTable.h"
+#include "js/Vector.h"
+
+namespace js {
+namespace wasm {
+
+const uint32_t AstNoIndex = UINT32_MAX;
+const unsigned AST_LIFO_DEFAULT_CHUNK_SIZE = 4096;
+
+/*****************************************************************************/
+// wasm AST
+
+class AstExpr;
+
+template <class T>
+using AstVector = mozilla::Vector<T, 0, LifoAllocPolicy<Fallible>>;
+
+template <class K, class V, class HP>
+using AstHashMap = HashMap<K, V, HP, LifoAllocPolicy<Fallible>>;
+
+class AstName
+{
+    const char16_t* begin_;
+    const char16_t* end_;
+  public:
+    AstName(const char16_t* begin, size_t length) : begin_(begin), end_(begin + length) {}
+    AstName() : begin_(nullptr), end_(nullptr) {}
+    const char16_t* begin() const { return begin_; }
+    const char16_t* end() const { return end_; }
+    size_t length() const { return end_ - begin_; }
+    bool empty() const { return begin_ == nullptr; }
+
+    bool operator==(AstName rhs) const {
+        if (length() != rhs.length())
+            return false;
+        if (begin() == rhs.begin())
+            return true;
+        return EqualChars(begin(), rhs.begin(), length());
+    }
+    bool operator!=(AstName rhs) const {
+        return !(*this == rhs);
+    }
+};
+
+class AstRef
+{
+    AstName name_;
+    uint32_t index_;
+
+  public:
+    AstRef()
+      : index_(AstNoIndex)
+    {
+        MOZ_ASSERT(isInvalid());
+    }
+    AstRef(AstName name, uint32_t index)
+      : name_(name), index_(index)
+    {
+        MOZ_ASSERT(name.empty() ^ (index == AstNoIndex));
+        MOZ_ASSERT(!isInvalid());
+    }
+    bool isInvalid() const {
+        return name_.empty() && index_ == AstNoIndex;
+    }
+    AstName name() const {
+        return name_;
+    }
+    size_t index() const {
+        MOZ_ASSERT(index_ != AstNoIndex);
+        return index_;
+    }
+    void setIndex(uint32_t index) {
+        MOZ_ASSERT(index_ == AstNoIndex);
+        index_ = index;
+    }
+};
+
+struct AstNameHasher
+{
+    typedef const AstName Lookup;
+    static js::HashNumber hash(Lookup l) {
+        return mozilla::HashString(l.begin(), l.length());
+    }
+    static bool match(const AstName key, Lookup lookup) {
+        return key == lookup;
+    }
+};
+
+using AstNameMap = AstHashMap<AstName, uint32_t, AstNameHasher>;
+
+typedef AstVector<ValType> AstValTypeVector;
+typedef AstVector<AstExpr*> AstExprVector;
+typedef AstVector<AstName> AstNameVector;
+typedef AstVector<AstRef> AstRefVector;
+
+struct AstBase
+{
+    void* operator new(size_t numBytes, LifoAlloc& astLifo) throw() {
+        return astLifo.alloc(numBytes);
+    }
+};
+
+class AstSig : public AstBase
+{
+    AstName name_;
+    AstValTypeVector args_;
+    ExprType ret_;
+
+  public:
+    explicit AstSig(LifoAlloc& lifo)
+      : args_(lifo),
+        ret_(ExprType::Void)
+    {}
+    AstSig(AstValTypeVector&& args, ExprType ret)
+      : args_(Move(args)),
+        ret_(ret)
+    {}
+    AstSig(AstName name, AstSig&& rhs)
+      : name_(name),
+        args_(Move(rhs.args_)),
+        ret_(rhs.ret_)
+    {}
+    void operator=(AstSig&& rhs) {
+        args_ = Move(rhs.args_);
+        ret_ = rhs.ret_;
+    }
+    const AstValTypeVector& args() const {
+        return args_;
+    }
+    ExprType ret() const {
+        return ret_;
+    }
+    AstName name() const {
+        return name_;
+    }
+    bool operator==(const AstSig& rhs) const {
+        return ret() == rhs.ret() && EqualContainers(args(), rhs.args());
+    }
+
+    typedef const AstSig& Lookup;
+    static HashNumber hash(Lookup sig) {
+        return AddContainerToHash(sig.args(), HashNumber(sig.ret()));
+    }
+    static bool match(const AstSig* lhs, Lookup rhs) {
+        return *lhs == rhs;
+    }
+};
+
+class AstNode : public AstBase
+{};
+
+enum class AstExprKind
+{
+    BinaryOperator,
+    Block,
+    Branch,
+    BranchTable,
+    Call,
+    CallIndirect,
+    ComparisonOperator,
+    Const,
+    ConversionOperator,
+    GetLocal,
+    If,
+    Load,
+    Nop,
+    Return,
+    SetLocal,
+    Store,
+    TernaryOperator,
+    UnaryOperator,
+    Unreachable
+};
+
+class AstExpr : public AstNode
+{
+    const AstExprKind kind_;
+
+  protected:
+    explicit AstExpr(AstExprKind kind)
+      : kind_(kind)
+    {}
+
+  public:
+    AstExprKind kind() const { return kind_; }
+
+    template <class T>
+    T& as() {
+        MOZ_ASSERT(kind() == T::Kind);
+        return static_cast<T&>(*this);
+    }
+};
+
+struct AstNop : AstExpr
+{
+    static const AstExprKind Kind = AstExprKind::Nop;
+    AstNop()
+      : AstExpr(AstExprKind::Nop)
+    {}
+};
+
+struct AstUnreachable : AstExpr
+{
+    static const AstExprKind Kind = AstExprKind::Unreachable;
+    AstUnreachable()
+      : AstExpr(AstExprKind::Unreachable)
+    {}
+};
+
+class AstConst : public AstExpr
+{
+    const Val val_;
+
+  public:
+    static const AstExprKind Kind = AstExprKind::Const;
+    explicit AstConst(Val val)
+      : AstExpr(Kind),
+        val_(val)
+    {}
+    Val val() const { return val_; }
+};
+
+class AstGetLocal : public AstExpr
+{
+    AstRef local_;
+
+  public:
+    static const AstExprKind Kind = AstExprKind::GetLocal;
+    explicit AstGetLocal(AstRef local)
+      : AstExpr(Kind),
+        local_(local)
+    {}
+    AstRef& local() {
+        return local_;
+    }
+};
+
+class AstSetLocal : public AstExpr
+{
+    AstRef local_;
+    AstExpr& value_;
+
+  public:
+    static const AstExprKind Kind = AstExprKind::SetLocal;
+    AstSetLocal(AstRef local, AstExpr& value)
+      : AstExpr(Kind),
+        local_(local),
+        value_(value)
+    {}
+    AstRef& local() {
+        return local_;
+    }
+    AstExpr& value() const {
+        return value_;
+    }
+};
+
+class AstBlock : public AstExpr
+{
+    Expr expr_;
+    AstName breakName_;
+    AstName continueName_;
+    AstExprVector exprs_;
+
+  public:
+    static const AstExprKind Kind = AstExprKind::Block;
+    explicit AstBlock(Expr expr, AstName breakName,
+                          AstName continueName, AstExprVector&& exprs)
+      : AstExpr(Kind),
+        expr_(expr),
+        breakName_(breakName),
+        continueName_(continueName),
+        exprs_(Move(exprs))
+    {}
+
+    Expr expr() const { return expr_; }
+    AstName breakName() const { return breakName_; }
+    AstName continueName() const { return continueName_; }
+    const AstExprVector& exprs() const { return exprs_; }
+};
+
+class AstBranch : public AstExpr
+{
+    Expr expr_;
+    AstExpr* cond_;
+    AstRef target_;
+    AstExpr* value_;
+
+  public:
+    static const AstExprKind Kind = AstExprKind::Branch;
+    explicit AstBranch(Expr expr, AstExpr* cond, AstRef target, AstExpr* value)
+      : AstExpr(Kind),
+        expr_(expr),
+        cond_(cond),
+        target_(target),
+        value_(value)
+    {}
+
+    Expr expr() const { return expr_; }
+    AstRef& target() { return target_; }
+    AstExpr& cond() const { MOZ_ASSERT(cond_); return *cond_; }
+    AstExpr* maybeValue() const { return value_; }
+};
+
+class AstCall : public AstExpr
+{
+    Expr expr_;
+    AstRef func_;
+    AstExprVector args_;
+
+  public:
+    static const AstExprKind Kind = AstExprKind::Call;
+    AstCall(Expr expr, AstRef func, AstExprVector&& args)
+      : AstExpr(Kind), expr_(expr), func_(func), args_(Move(args))
+    {}
+
+    Expr expr() const { return expr_; }
+    AstRef& func() { return func_; }
+    const AstExprVector& args() const { return args_; }
+};
+
+class AstCallIndirect : public AstExpr
+{
+    AstRef sig_;
+    AstExpr* index_;
+    AstExprVector args_;
+
+  public:
+    static const AstExprKind Kind = AstExprKind::CallIndirect;
+    AstCallIndirect(AstRef sig, AstExpr* index, AstExprVector&& args)
+      : AstExpr(Kind), sig_(sig), index_(index), args_(Move(args))
+    {}
+    AstRef& sig() { return sig_; }
+    AstExpr* index() const { return index_; }
+    const AstExprVector& args() const { return args_; }
+};
+
+class AstReturn : public AstExpr
+{
+    AstExpr* maybeExpr_;
+
+  public:
+    static const AstExprKind Kind = AstExprKind::Return;
+    explicit AstReturn(AstExpr* maybeExpr)
+      : AstExpr(Kind),
+        maybeExpr_(maybeExpr)
+    {}
+    AstExpr* maybeExpr() const { return maybeExpr_; }
+};
+
+class AstIf : public AstExpr
+{
+    AstExpr* cond_;
+    AstExpr* thenBranch_;
+    AstExpr* elseBranch_;
+
+  public:
+    static const AstExprKind Kind = AstExprKind::If;
+    AstIf(AstExpr* cond, AstExpr* thenBranch, AstExpr* elseBranch)
+      : AstExpr(Kind),
+        cond_(cond),
+        thenBranch_(thenBranch),
+        elseBranch_(elseBranch)
+    {}
+
+    AstExpr& cond() const { return *cond_; }
+    AstExpr& thenBranch() const { return *thenBranch_; }
+    bool hasElse() const { return !!elseBranch_; }
+    AstExpr& elseBranch() const { MOZ_ASSERT(hasElse()); return *elseBranch_; }
+};
+
+class AstLoadStoreAddress
+{
+    AstExpr* base_;
+    int32_t flags_;
+    int32_t offset_;
+
+  public:
+    explicit AstLoadStoreAddress(AstExpr* base, int32_t flags, int32_t offset)
+      : base_(base),
+        flags_(flags),
+        offset_(offset)
+    {}
+
+    AstExpr& base() const { return *base_; }
+    int32_t flags() const { return flags_; }
+    int32_t offset() const { return offset_; }
+};
+
+class AstLoad : public AstExpr
+{
+    Expr expr_;
+    AstLoadStoreAddress address_;
+
+  public:
+    static const AstExprKind Kind = AstExprKind::Load;
+    explicit AstLoad(Expr expr, const AstLoadStoreAddress &address)
+      : AstExpr(Kind),
+        expr_(expr),
+        address_(address)
+    {}
+
+    Expr expr() const { return expr_; }
+    const AstLoadStoreAddress& address() const { return address_; }
+};
+
+class AstStore : public AstExpr
+{
+    Expr expr_;
+    AstLoadStoreAddress address_;
+    AstExpr* value_;
+
+  public:
+    static const AstExprKind Kind = AstExprKind::Store;
+    explicit AstStore(Expr expr, const AstLoadStoreAddress &address,
+                          AstExpr* value)
+      : AstExpr(Kind),
+        expr_(expr),
+        address_(address),
+        value_(value)
+    {}
+
+    Expr expr() const { return expr_; }
+    const AstLoadStoreAddress& address() const { return address_; }
+    AstExpr& value() const { return *value_; }
+};
+
+class AstBranchTable : public AstExpr
+{
+    AstExpr& index_;
+    AstRef default_;
+    AstRefVector table_;
+    AstExpr* value_;
+
+  public:
+    static const AstExprKind Kind = AstExprKind::BranchTable;
+    explicit AstBranchTable(AstExpr& index, AstRef def, AstRefVector&& table,
+                                AstExpr* maybeValue)
+      : AstExpr(Kind),
+        index_(index),
+        default_(def),
+        table_(Move(table)),
+        value_(maybeValue)
+    {}
+    AstExpr& index() const { return index_; }
+    AstRef& def() { return default_; }
+    AstRefVector& table() { return table_; }
+    AstExpr* maybeValue() { return value_; }
+};
+
+class AstFunc : public AstNode
+{
+    AstName name_;
+    AstRef sig_;
+    AstValTypeVector vars_;
+    AstNameVector localNames_;
+    AstExprVector body_;
+
+  public:
+    AstFunc(AstName name, AstRef sig, AstValTypeVector&& vars,
+                AstNameVector&& locals, AstExprVector&& body)
+      : name_(name),
+        sig_(sig),
+        vars_(Move(vars)),
+        localNames_(Move(locals)),
+        body_(Move(body))
+    {}
+    AstRef& sig() { return sig_; }
+    const AstValTypeVector& vars() const { return vars_; }
+    const AstNameVector& locals() const { return localNames_; }
+    const AstExprVector& body() const { return body_; }
+    AstName name() const { return name_; }
+};
+
+class AstImport : public AstNode
+{
+    AstName name_;
+    AstName module_;
+    AstName func_;
+    uint32_t sigIndex_;
+
+  public:
+    AstImport(AstName name, AstName module, AstName func, uint32_t sigIndex)
+      : name_(name), module_(module), func_(func), sigIndex_(sigIndex)
+    {}
+    AstName name() const { return name_; }
+    AstName module() const { return module_; }
+    AstName func() const { return func_; }
+    uint32_t sigIndex() const { return sigIndex_; }
+};
+
+enum class AstExportKind { Func, Memory };
+
+class AstExport : public AstNode
+{
+    AstName name_;
+    AstExportKind kind_;
+    AstRef func_;
+
+  public:
+    AstExport(AstName name, AstRef func)
+      : name_(name), kind_(AstExportKind::Func), func_(func)
+    {}
+    explicit AstExport(AstName name)
+      : name_(name), kind_(AstExportKind::Memory)
+    {}
+    AstName name() const { return name_; }
+    AstExportKind kind() const { return kind_; }
+    AstRef& func() { return func_; }
+};
+
+typedef AstVector<AstRef> AstTableElemVector;
+
+class AstTable : public AstNode
+{
+    AstTableElemVector elems_;
+
+  public:
+    explicit AstTable(AstTableElemVector&& elems) : elems_(Move(elems)) {}
+    AstTableElemVector& elems() { return elems_; }
+};
+
+class AstSegment : public AstNode
+{
+    uint32_t offset_;
+    AstName text_;
+
+  public:
+    AstSegment(uint32_t offset, AstName text)
+      : offset_(offset), text_(text)
+    {}
+    uint32_t offset() const { return offset_; }
+    AstName text() const { return text_; }
+};
+
+typedef AstVector<AstSegment*> AstSegmentVector;
+
+class AstMemory : public AstNode
+{
+    uint32_t initialSize_;
+    Maybe<uint32_t> maxSize_;
+    AstSegmentVector segments_;
+
+  public:
+    explicit AstMemory(uint32_t initialSize, Maybe<uint32_t> maxSize,
+                           AstSegmentVector&& segments)
+      : initialSize_(initialSize),
+        maxSize_(maxSize),
+        segments_(Move(segments))
+    {}
+    uint32_t initialSize() const { return initialSize_; }
+    const Maybe<uint32_t>& maxSize() const { return maxSize_; }
+    const AstSegmentVector& segments() const { return segments_; }
+};
+
+class AstModule : public AstNode
+{
+  public:
+    typedef AstVector<AstFunc*> FuncVector;
+    typedef AstVector<AstImport*> ImportVector;
+    typedef AstVector<AstExport*> ExportVector;
+    typedef AstVector<AstSig*> SigVector;
+
+  private:
+    typedef AstHashMap<AstSig*, uint32_t, AstSig> SigMap;
+
+    LifoAlloc& lifo_;
+    AstMemory* memory_;
+    SigVector sigs_;
+    SigMap sigMap_;
+    ImportVector imports_;
+    ExportVector exports_;
+    AstTable* table_;
+    FuncVector funcs_;
+
+  public:
+    explicit AstModule(LifoAlloc& lifo)
+      : lifo_(lifo),
+        memory_(nullptr),
+        sigs_(lifo),
+        sigMap_(lifo),
+        imports_(lifo),
+        exports_(lifo),
+        table_(nullptr),
+        funcs_(lifo)
+    {}
+    bool init() {
+        return sigMap_.init();
+    }
+    bool setMemory(AstMemory* memory) {
+        if (memory_)
+            return false;
+        memory_ = memory;
+        return true;
+    }
+    AstMemory* maybeMemory() const {
+        return memory_;
+    }
+    bool declare(AstSig&& sig, uint32_t* sigIndex) {
+        SigMap::AddPtr p = sigMap_.lookupForAdd(sig);
+        if (p) {
+            *sigIndex = p->value();
+            return true;
+        }
+        *sigIndex = sigs_.length();
+        auto* lifoSig = new (lifo_) AstSig(AstName(), Move(sig));
+        return lifoSig &&
+               sigs_.append(lifoSig) &&
+               sigMap_.add(p, sigs_.back(), *sigIndex);
+    }
+    bool append(AstSig* sig) {
+        uint32_t sigIndex = sigs_.length();
+        if (!sigs_.append(sig))
+            return false;
+        SigMap::AddPtr p = sigMap_.lookupForAdd(*sig);
+        return p || sigMap_.add(p, sig, sigIndex);
+    }
+    const SigVector& sigs() const {
+        return sigs_;
+    }
+    bool append(AstFunc* func) {
+        return funcs_.append(func);
+    }
+    const FuncVector& funcs() const {
+        return funcs_;
+    }
+    const ImportVector& imports() const {
+        return imports_;
+    }
+    bool append(AstImport* imp) {
+        return imports_.append(imp);
+    }
+    bool append(AstExport* exp) {
+        return exports_.append(exp);
+    }
+    const ExportVector& exports() const {
+        return exports_;
+    }
+    bool initTable(AstTable* table) {
+        if (table_)
+            return false;
+        table_ = table;
+        return true;
+    }
+    AstTable* maybeTable() const {
+        return table_;
+    }
+};
+
+class AstUnaryOperator final : public AstExpr
+{
+    Expr expr_;
+    AstExpr* op_;
+
+  public:
+    static const AstExprKind Kind = AstExprKind::UnaryOperator;
+    explicit AstUnaryOperator(Expr expr, AstExpr* op)
+      : AstExpr(Kind),
+        expr_(expr), op_(op)
+    {}
+
+    Expr expr() const { return expr_; }
+    AstExpr* op() const { return op_; }
+};
+
+class AstBinaryOperator final : public AstExpr
+{
+    Expr expr_;
+    AstExpr* lhs_;
+    AstExpr* rhs_;
+
+  public:
+    static const AstExprKind Kind = AstExprKind::BinaryOperator;
+    explicit AstBinaryOperator(Expr expr, AstExpr* lhs, AstExpr* rhs)
+      : AstExpr(Kind),
+        expr_(expr), lhs_(lhs), rhs_(rhs)
+    {}
+
+    Expr expr() const { return expr_; }
+    AstExpr* lhs() const { return lhs_; }
+    AstExpr* rhs() const { return rhs_; }
+};
+
+class AstTernaryOperator : public AstExpr
+{
+    Expr expr_;
+    AstExpr* op0_;
+    AstExpr* op1_;
+    AstExpr* op2_;
+
+  public:
+    static const AstExprKind Kind = AstExprKind::TernaryOperator;
+    AstTernaryOperator(Expr expr, AstExpr* op0, AstExpr* op1, AstExpr* op2)
+      : AstExpr(Kind),
+        expr_(expr), op0_(op0), op1_(op1), op2_(op2)
+    {}
+
+    Expr expr() const { return expr_; }
+    AstExpr* op0() const { return op0_; }
+    AstExpr* op1() const { return op1_; }
+    AstExpr* op2() const { return op2_; }
+};
+
+class AstComparisonOperator final : public AstExpr
+{
+    Expr expr_;
+    AstExpr* lhs_;
+    AstExpr* rhs_;
+
+  public:
+    static const AstExprKind Kind = AstExprKind::ComparisonOperator;
+    explicit AstComparisonOperator(Expr expr, AstExpr* lhs, AstExpr* rhs)
+      : AstExpr(Kind),
+        expr_(expr), lhs_(lhs), rhs_(rhs)
+    {}
+
+    Expr expr() const { return expr_; }
+    AstExpr* lhs() const { return lhs_; }
+    AstExpr* rhs() const { return rhs_; }
+};
+
+class AstConversionOperator final : public AstExpr
+{
+    Expr expr_;
+    AstExpr* op_;
+
+  public:
+    static const AstExprKind Kind = AstExprKind::ConversionOperator;
+    explicit AstConversionOperator(Expr expr, AstExpr* op)
+      : AstExpr(Kind),
+        expr_(expr), op_(op)
+    {}
+
+    Expr expr() const { return expr_; }
+    AstExpr* op() const { return op_; }
+};
+
+} // end wasm namespace
+} // end js namespace
+
+#endif // namespace wasmast_h
new file mode 100644
--- /dev/null
+++ b/js/src/asmjs/WasmBinaryToAST.cpp
@@ -0,0 +1,1387 @@
+/* -*- 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 "asmjs/WasmBinaryToAST.h"
+
+#include "mozilla/MathAlgorithms.h"
+
+#include "asmjs/Wasm.h"
+#include "asmjs/WasmBinaryIterator.h"
+
+using namespace js;
+using namespace js::wasm;
+
+using mozilla::FloorLog2;
+
+enum AstDecodeTerminationKind
+{
+    Unknown,
+    End,
+    Else
+};
+
+struct AstDecodeStackItem
+{
+    AstExpr* expr;
+    union {
+      uint32_t stackDelta;
+      AstDecodeTerminationKind terminationKind;
+    };
+
+    explicit AstDecodeStackItem(): expr(nullptr), terminationKind(AstDecodeTerminationKind::Unknown) {}
+    explicit AstDecodeStackItem(AstDecodeTerminationKind terminationKind): expr(nullptr), terminationKind(terminationKind) {}
+    explicit AstDecodeStackItem(AstExpr* expr): expr(expr), stackDelta(0) {}
+    explicit AstDecodeStackItem(AstExpr* expr, uint32_t stackDelta): expr(expr), stackDelta(stackDelta) {}
+};
+
+struct AstDecodePolicy : ExprIterPolicy
+{
+    static const bool Output = true;
+    typedef AstDecodeStackItem Value;
+};
+
+typedef ExprIter<AstDecodePolicy> AstDecodeExprIter;
+
+class AstDecodeContext
+{
+  public:
+    typedef AstVector<uint32_t> AstIndexVector;
+
+    JSContext* cx;
+    LifoAlloc& lifo;
+    Decoder& d;
+    bool generateNames;
+
+    uint32_t numTableElems;
+    Maybe<uint32_t> initialSizePages;
+    Maybe<uint32_t> maxSizePages;
+
+  private:
+    AstModule& module_;
+    AstIndexVector funcSigs_;
+    AstDecodeExprIter *iter_;
+    const ValTypeVector* locals_;
+
+  public:
+    AstDecodeContext(JSContext* cx, LifoAlloc& lifo, Decoder& d, AstModule& module, bool generateNames)
+     : cx(cx),
+       lifo(lifo),
+       d(d),
+       generateNames(generateNames),
+       numTableElems(0),
+       initialSizePages(),
+       maxSizePages(),
+       module_(module),
+       funcSigs_(lifo),
+       iter_(nullptr),
+       locals_(nullptr)
+    {}
+
+    AstModule& module() { return module_; }
+    AstIndexVector& funcSigs() { return funcSigs_; }
+    AstDecodeExprIter& iter() { return *iter_; }
+    const ValTypeVector& locals() { return *locals_; }
+
+    void startFunction(AstDecodeExprIter *iter, const ValTypeVector* locals)
+    {
+        this->iter_ = iter;
+        this->locals_ = locals;
+    }
+    void endFunction()
+    {
+        this->iter_ = nullptr;
+        this->locals_ = nullptr;
+    }
+};
+
+static bool
+AstDecodeFail(AstDecodeContext& c, const char* str)
+{
+    uint32_t offset = c.d.currentOffset();
+    char offsetStr[sizeof "4294967295"];
+    JS_snprintf(offsetStr, sizeof offsetStr, "%" PRIu32, offset);
+    JS_ReportErrorNumber(c.cx, GetErrorMessage, nullptr, JSMSG_WASM_DECODE_FAIL, offsetStr, str);
+    return false;
+}
+
+static bool
+AstDecodeGenerateName(AstDecodeContext& c, const AstName& prefix, uint32_t index, AstName& name)
+{
+    if (!c.generateNames) {
+        name = AstName();
+        return true;
+    }
+
+    AstVector<char16_t> result(c.lifo);
+    if (!result.append(prefix.begin(), prefix.length()))
+        return false;
+    uint32_t tmp = index;
+    do {
+        if (!result.append(MOZ_UTF16('0')))
+            return false;
+        tmp /= 10;
+    } while (tmp);
+
+    if (index) {
+        char16_t* p = result.end();
+        for (tmp = index; tmp; tmp /= 10)
+            *(--p) = MOZ_UTF16('0' + (tmp % 10));
+    }
+
+    size_t length = result.length();
+    char16_t* begin = result.extractOrCopyRawBuffer();
+    if (!begin)
+        return false;
+
+    name = AstName(begin, length);
+    return true;
+}
+
+static bool
+AstDecodeGenerateRef(AstDecodeContext& c, const AstName& prefix, uint32_t index, AstRef& ref)
+{
+    MOZ_ASSERT(index != AstNoIndex);
+
+    if (!c.generateNames) {
+        ref = AstRef(AstName(), index);
+        return true;
+    }
+
+    AstName name;
+    if (!AstDecodeGenerateName(c, prefix, index, name))
+        return false;
+    MOZ_ASSERT(!name.empty());
+
+    ref = AstRef(name, AstNoIndex);
+    ref.setIndex(index);
+    return true;
+}
+
+static bool
+AstDecodeCallArgs(AstDecodeContext& c, uint32_t arity, const AstSig& sig, AstExprVector& funcArgs)
+{
+    if (arity != sig.args().length())
+        return c.iter().fail("call arity out of range");
+
+    const AstValTypeVector& args = sig.args();
+    uint32_t numArgs = args.length();
+
+    if (!funcArgs.resize(numArgs))
+        return false;
+
+    for (size_t i = 0; i < numArgs; ++i) {
+        ValType argType = args[i];
+        AstDecodeStackItem item;
+        if (!c.iter().readCallArg(argType, numArgs, i, &item))
+            return false;
+        funcArgs[i] = item.expr;
+    }
+
+    return c.iter().readCallArgsEnd(numArgs);
+}
+
+static bool
+AstDecodeCallReturn(AstDecodeContext& c, const AstSig& sig)
+{
+    return c.iter().readCallReturn(sig.ret());
+}
+
+static bool
+AstDecodeExpr(AstDecodeContext& c);
+
+static bool
+AstDecodeCall(AstDecodeContext& c)
+{
+    uint32_t calleeIndex;
+    uint32_t arity;
+    if (!c.iter().readCall(&calleeIndex, &arity))
+        return false;
+
+    if (calleeIndex >= c.funcSigs().length())
+        return c.iter().fail("callee index out of range");
+
+    uint32_t sigIndex = c.funcSigs()[calleeIndex];
+    const AstSig* sig = c.module().sigs()[sigIndex];
+    AstRef funcRef;
+    if (!AstDecodeGenerateRef(c, AstName(MOZ_UTF16("$func$"), 6), calleeIndex, funcRef))
+        return false;
+    AstExprVector args(c.lifo);
+    if (!AstDecodeCallArgs(c, arity, *sig, args) ||
+        !AstDecodeCallReturn(c, *sig))
+        return false;
+    AstCall* call = new(c.lifo) AstCall(Expr::Call, funcRef, Move(args));
+    c.iter().setResult(AstDecodeStackItem(call, args.length()));
+    return call;
+}
+
+static bool
+AstDecodeCallIndirect(AstDecodeContext& c)
+{
+    uint32_t sigIndex;
+    uint32_t arity;
+    if (!c.iter().readCallIndirect(&sigIndex, &arity))
+        return false;
+
+    if (sigIndex >= c.module().sigs().length())
+        return c.iter().fail("signature index out of range");
+
+    const AstSig* sig = c.module().sigs()[sigIndex];
+    AstExprVector args(c.lifo);
+    if (!AstDecodeCallArgs(c, arity, *sig, args))
+        return false;
+
+    AstDecodeStackItem index;
+    if (!c.iter().readCallIndirectCallee(&index))
+        return false;
+
+
+    if (!AstDecodeCallReturn(c, *sig))
+        return false;
+    AstRef sigRef;
+    if (!AstDecodeGenerateRef(c, AstName(MOZ_UTF16("$type$"), 6), sigIndex, sigRef))
+        return false;
+    AstCallIndirect* call = new(c.lifo) AstCallIndirect(sigRef, index.expr, Move(args));
+    c.iter().setResult(AstDecodeStackItem(call, 1 + args.length()));
+    return call;
+}
+
+static bool
+AstDecodeCallImport(AstDecodeContext& c)
+{
+    uint32_t importIndex;
+    uint32_t arity;
+    if (!c.iter().readCallImport(&importIndex, &arity))
+        return false;
+
+    if (importIndex >= c.module().imports().length())
+        return c.iter().fail("import index out of range");
+
+    const AstImport* import = c.module().imports()[importIndex];
+    const AstSig* sig = c.module().sigs()[import->sigIndex()];
+    AstRef funcRef;
+    if (!AstDecodeGenerateRef(c, AstName(MOZ_UTF16("$import$"), 8), importIndex, funcRef))
+        return false;
+    AstExprVector args(c.lifo);
+    if (!AstDecodeCallArgs(c, arity, *sig, args) ||
+        !AstDecodeCallReturn(c, *sig))
+        return false;
+    AstCall* call = new(c.lifo) AstCall(Expr::CallImport, funcRef, Move(args));
+    c.iter().setResult(AstDecodeStackItem(call, args.length()));
+    return call;
+}
+
+static bool
+AstDecodeBrTable(AstDecodeContext& c)
+{
+    uint32_t tableLength;
+    ExprType type;
+    AstDecodeStackItem index;
+    AstDecodeStackItem value;
+    if (!c.iter().readBrTable(&tableLength, &type, &value, &index))
+        return false;
+
+    AstRefVector table(c.lifo);
+    if (!table.resize(tableLength))
+        return false;
+
+    uint32_t depth;
+    for (size_t i = 0, e = tableLength; i < e; ++i) {
+        if (!c.iter().readBrTableEntry(type, &depth))
+            return false;
+        table[i] = AstRef(AstName(), depth);
+    }
+
+    // Read the default label.
+    if (!c.iter().readBrTableEntry(type, &depth))
+        return false;
+    AstRef def(AstName(), depth);
+
+
+    AstBranchTable* branchTable = new(c.lifo) AstBranchTable(*index.expr, def, Move(table), value.expr);
+    c.iter().setResult(AstDecodeStackItem(branchTable, value.expr ? 2 : 1));
+    return branchTable;
+}
+
+static bool
+AstDecodeBlock(AstDecodeContext& c, Expr expr)
+{
+    MOZ_ASSERT(expr == Expr::Block || expr == Expr::Loop);
+
+    if (expr == Expr::Loop) {
+      if (!c.iter().readLoop())
+          return false;
+    } else {
+      if (!c.iter().readBlock())
+          return false;
+    }
+    AstExprVector exprs(c.lifo);
+    while (true) {
+        if (!AstDecodeExpr(c))
+            return false;
+        AstDecodeStackItem item = c.iter().getResult();
+        if (!item.expr) // Expr::End was found
+            break;
+        exprs.shrinkBy(item.stackDelta);
+        if (!exprs.append(item.expr))
+            return false;
+    }
+    AstBlock* block = new(c.lifo) AstBlock(expr, AstName(), AstName(), Move(exprs));
+    c.iter().setResult(AstDecodeStackItem(block));
+    return block;
+}
+
+static bool
+AstDecodeIf(AstDecodeContext& c)
+{
+    AstDecodeStackItem cond;
+    if (!c.iter().readIf(&cond))
+        return false;
+
+    if (!AstDecodeExpr(c))
+        return false;
+
+    AstDecodeStackItem nextItem;
+    AstDecodeStackItem thenItem = c.iter().getResult();
+    while (true) {
+        if (!AstDecodeExpr(c))
+            return false;
+        nextItem = c.iter().getResult();
+        if (!nextItem.expr)
+            break;
+        thenItem = nextItem;
+    }
+
+    AstDecodeStackItem elseItem;
+    if (nextItem.terminationKind == AstDecodeTerminationKind::Else) {
+        if (!AstDecodeExpr(c))
+            return false;
+        elseItem = c.iter().getResult();
+
+        while (true) {
+            if (!AstDecodeExpr(c))
+                return false;
+            nextItem = c.iter().getResult();
+            if (!nextItem.expr)
+                break;
+            elseItem = nextItem;
+        }
+    }
+
+    AstIf* if_ = new(c.lifo) AstIf(cond.expr, thenItem.expr, elseItem.expr);
+    c.iter().setResult(AstDecodeStackItem(if_, 1));
+    return if_;
+}
+
+static bool
+AstDecodeEnd(AstDecodeContext& c)
+{
+    LabelKind kind;
+    ExprType type;
+    AstDecodeStackItem tmp;
+    if (!c.iter().readEnd(&kind, &type, &tmp))
+        return false;
+
+    c.iter().setResult(AstDecodeStackItem(AstDecodeTerminationKind::End));
+    return true;
+}
+
+static bool
+AstDecodeElse(AstDecodeContext& c)
+{
+    ExprType type;
+    AstDecodeStackItem tmp;
+
+    if (!c.iter().readElse(&type, &tmp))
+        return false;
+
+    c.iter().setResult(AstDecodeStackItem(AstDecodeTerminationKind::Else));
+    return true;
+}
+
+static bool
+AstDecodeUnary(AstDecodeContext& c, ValType type, Expr expr)
+{
+    AstDecodeStackItem op;
+    if (!c.iter().readUnary(type, &op))
+        return false;
+    AstUnaryOperator* unary = new(c.lifo) AstUnaryOperator(expr, op.expr);
+    c.iter().setResult(AstDecodeStackItem(unary, 1));
+    return unary;
+}
+
+static bool
+AstDecodeBinary(AstDecodeContext& c, ValType type, Expr expr)
+{
+    AstDecodeStackItem lhs;
+    AstDecodeStackItem rhs;
+    if (!c.iter().readBinary(type, &lhs, &rhs))
+        return false;
+    AstBinaryOperator* binary = new(c.lifo) AstBinaryOperator(expr, lhs.expr, rhs.expr);
+    c.iter().setResult(AstDecodeStackItem(binary, 2));
+    return binary;
+}
+
+static bool
+AstDecodeSelect(AstDecodeContext& c)
+{
+    ExprType type;
+    AstDecodeStackItem cond;
+    AstDecodeStackItem selectTrue;
+    AstDecodeStackItem selectFalse;
+    if (!c.iter().readSelect(&type, &cond, &selectTrue, &selectFalse))
+        return false;
+    AstTernaryOperator* ternary = new(c.lifo) AstTernaryOperator(Expr::Select, cond.expr, selectTrue.expr, selectFalse.expr);
+    c.iter().setResult(AstDecodeStackItem(ternary, 3));
+    return ternary;
+}
+
+static bool
+AstDecodeComparison(AstDecodeContext& c, ValType type, Expr expr)
+{
+    AstDecodeStackItem lhs;
+    AstDecodeStackItem rhs;
+    if (!c.iter().readComparison(type, &lhs, &rhs))
+        return false;
+    AstComparisonOperator* comparison = new(c.lifo) AstComparisonOperator(expr, lhs.expr, rhs.expr);
+    c.iter().setResult(AstDecodeStackItem(comparison, 2));
+    return comparison;
+}
+
+static bool
+AstDecodeConversion(AstDecodeContext& c, ValType fromType, ValType toType, Expr expr)
+{
+    AstDecodeStackItem op;
+    if (!c.iter().readConversion(fromType, toType, &op))
+        return false;
+    AstConversionOperator* conversion = new(c.lifo) AstConversionOperator(expr, op.expr);
+    c.iter().setResult(AstDecodeStackItem(conversion, 1));
+    return conversion;
+}
+
+static AstLoadStoreAddress
+AstDecodeLoadStoreAddress(const LinearMemoryAddress<AstDecodeStackItem>& addr)
+{
+    uint32_t flags = FloorLog2(addr.align);
+    return AstLoadStoreAddress(addr.base.expr, flags, addr.offset);
+}
+
+static bool
+AstDecodeLoad(AstDecodeContext& c, ValType type, uint32_t byteSize, Expr expr)
+{
+    LinearMemoryAddress<AstDecodeStackItem> addr;
+    if (!c.iter().readLoad(type, byteSize, &addr))
+        return false;
+    AstLoad* load = new(c.lifo) AstLoad(expr, AstDecodeLoadStoreAddress(addr));
+    c.iter().setResult(AstDecodeStackItem(load, 1));
+    return load;
+}
+
+static bool
+AstDecodeStore(AstDecodeContext& c, ValType type, uint32_t byteSize, Expr expr)
+{
+    LinearMemoryAddress<AstDecodeStackItem> addr;
+    AstDecodeStackItem value;
+    if (!c.iter().readStore(type, byteSize, &addr, &value))
+        return false;
+    AstStore* store = new(c.lifo) AstStore(expr, AstDecodeLoadStoreAddress(addr), value.expr);
+    c.iter().setResult(AstDecodeStackItem(store, 2));
+    return store;
+}
+
+static bool
+AstDecodeBranch(AstDecodeContext& c, Expr expr)
+{
+    MOZ_ASSERT(expr == Expr::Br || expr == Expr::BrIf);
+
+    uint32_t depth;
+    ExprType type;
+    AstDecodeStackItem value;
+    AstDecodeStackItem cond;
+    uint32_t stackDelta;
+    if (expr == Expr::Br) {
+        if (!c.iter().readBr(&depth, &type, &value))
+            return false;
+        stackDelta = value.expr ? 1 : 0;
+    } else {
+        if (!c.iter().readBrIf(&depth, &type, &value, &cond))
+            return false;
+        stackDelta = value.expr ? 2 : 1;
+    }
+
+    AstRef depthRef(AstName(), depth);
+    AstBranch* branch = new(c.lifo) AstBranch(expr, cond.expr, depthRef, value.expr);
+    c.iter().setResult(AstDecodeStackItem(branch, stackDelta));
+    return branch;
+}
+
+static bool
+AstDecodeGetLocal(AstDecodeContext& c)
+{
+    uint32_t getLocalId;
+    if (!c.iter().readGetLocal(c.locals(), &getLocalId))
+        return false;
+    AstRef localRef;
+    if (!AstDecodeGenerateRef(c, AstName(MOZ_UTF16("$var$"), 5), getLocalId, localRef))
+        return false;
+    AstGetLocal* getLocal = new(c.lifo) AstGetLocal(localRef);
+    c.iter().setResult(AstDecodeStackItem(getLocal));
+    return getLocal;
+}
+
+static bool
+AstDecodeSetLocal(AstDecodeContext& c)
+{
+    uint32_t setLocalId;
+    AstDecodeStackItem setLocalValue;
+    if (!c.iter().readSetLocal(c.locals(), &setLocalId, &setLocalValue))
+        return false;
+    AstRef localRef;
+    if (!AstDecodeGenerateRef(c, AstName(MOZ_UTF16("$var$"), 5), setLocalId, localRef))
+        return false;
+    AstSetLocal* setLocal = new(c.lifo) AstSetLocal(localRef, *setLocalValue.expr);
+    c.iter().setResult(AstDecodeStackItem(setLocal, 1));
+    return setLocal;
+}
+
+static bool
+AstDecodeReturn(AstDecodeContext& c)
+{
+    AstDecodeStackItem result;
+    if (!c.iter().readReturn(&result))
+        return false;
+    AstReturn* ret = new(c.lifo) AstReturn(result.expr);
+    c.iter().setResult(AstDecodeStackItem(ret, result.expr ? 1 : 0));
+    return ret;
+}
+
+static bool
+AstDecodeExpr(AstDecodeContext& c)
+{
+    Expr expr;
+    if (!c.iter().readExpr(&expr))
+        return false;
+
+    AstExpr* tmp;
+    switch (expr) {
+      case Expr::Nop:
+        if (!c.iter().readNullary())
+            return false;
+        tmp = new(c.lifo) AstNop();
+        c.iter().setResult(AstDecodeStackItem(tmp));
+        return tmp;
+      case Expr::Call:
+        return AstDecodeCall(c);
+      case Expr::CallIndirect:
+        return AstDecodeCallIndirect(c);
+      case Expr::CallImport:
+        return AstDecodeCallImport(c);
+      case Expr::I32Const:
+        int32_t i32;
+        if (!c.iter().readI32Const(&i32))
+            return false;
+        tmp = new(c.lifo) AstConst(Val((uint32_t)i32));
+        c.iter().setResult(AstDecodeStackItem(tmp));
+        return tmp;
+      case Expr::I64Const:
+        int64_t i64;
+        if (!c.iter().readI64Const(&i64))
+            return false;
+        tmp = new(c.lifo) AstConst(Val((uint64_t)i64));
+        c.iter().setResult(AstDecodeStackItem(tmp));
+        return tmp;
+      case Expr::F32Const:
+        float f32;
+        if (!c.iter().readF32Const(&f32))
+            return false;
+        tmp = new(c.lifo) AstConst(Val(f32));
+        c.iter().setResult(AstDecodeStackItem(tmp));
+        return tmp;
+      case Expr::F64Const:
+        double f64;
+        if (!c.iter().readF64Const(&f64))
+            return false;
+        tmp = new(c.lifo) AstConst(Val(f64));
+        c.iter().setResult(AstDecodeStackItem(tmp));
+        return tmp;
+      case Expr::GetLocal:
+        return AstDecodeGetLocal(c);
+      case Expr::SetLocal:
+        return AstDecodeSetLocal(c);
+      case Expr::Select:
+        return AstDecodeSelect(c);
+      case Expr::Block:
+      case Expr::Loop:
+        return AstDecodeBlock(c, expr);
+      case Expr::If:
+        return AstDecodeIf(c);
+      case Expr::Else:
+        return AstDecodeElse(c);
+      case Expr::End:
+        return AstDecodeEnd(c);
+      case Expr::I32Clz:
+      case Expr::I32Ctz:
+      case Expr::I32Popcnt:
+        return AstDecodeUnary(c, ValType::I32, expr);
+      case Expr::I64Clz:
+      case Expr::I64Ctz:
+      case Expr::I64Popcnt:
+        return AstDecodeUnary(c, ValType::I64, expr);
+      case Expr::F32Abs:
+      case Expr::F32Neg:
+      case Expr::F32Ceil:
+      case Expr::F32Floor:
+      case Expr::F32Sqrt:
+      case Expr::F32Trunc:
+      case Expr::F32Nearest:
+        return AstDecodeUnary(c, ValType::F32, expr);
+      case Expr::F64Abs:
+      case Expr::F64Neg:
+      case Expr::F64Ceil:
+      case Expr::F64Floor:
+      case Expr::F64Sqrt:
+      case Expr::F64Trunc:
+      case Expr::F64Nearest:
+        return AstDecodeUnary(c, ValType::F64, expr);
+      case Expr::I32Add:
+      case Expr::I32Sub:
+      case Expr::I32Mul:
+      case Expr::I32DivS:
+      case Expr::I32DivU:
+      case Expr::I32RemS:
+      case Expr::I32RemU:
+      case Expr::I32And:
+      case Expr::I32Or:
+      case Expr::I32Xor:
+      case Expr::I32Shl:
+      case Expr::I32ShrS:
+      case Expr::I32ShrU:
+      case Expr::I32Rotl:
+      case Expr::I32Rotr:
+        return AstDecodeBinary(c, ValType::I32, expr);
+      case Expr::I64Add:
+      case Expr::I64Sub:
+      case Expr::I64Mul:
+      case Expr::I64DivS:
+      case Expr::I64DivU:
+      case Expr::I64RemS:
+      case Expr::I64RemU:
+      case Expr::I64And:
+      case Expr::I64Or:
+      case Expr::I64Xor:
+      case Expr::I64Shl:
+      case Expr::I64ShrS:
+      case Expr::I64ShrU:
+      case Expr::I64Rotl:
+      case Expr::I64Rotr:
+        return AstDecodeBinary(c, ValType::I64, expr);
+      case Expr::F32Add:
+      case Expr::F32Sub:
+      case Expr::F32Mul:
+      case Expr::F32Div:
+      case Expr::F32Min:
+      case Expr::F32Max:
+      case Expr::F32CopySign:
+        return AstDecodeBinary(c, ValType::F32, expr);
+      case Expr::F64Add:
+      case Expr::F64Sub:
+      case Expr::F64Mul:
+      case Expr::F64Div:
+      case Expr::F64Min:
+      case Expr::F64Max:
+      case Expr::F64CopySign:
+        return AstDecodeBinary(c, ValType::F64, expr);
+      case Expr::I32Eq:
+      case Expr::I32Ne:
+      case Expr::I32LtS:
+      case Expr::I32LtU:
+      case Expr::I32LeS:
+      case Expr::I32LeU:
+      case Expr::I32GtS:
+      case Expr::I32GtU:
+      case Expr::I32GeS:
+      case Expr::I32GeU:
+        return AstDecodeComparison(c, ValType::I32, expr);
+      case Expr::I64Eq:
+      case Expr::I64Ne:
+      case Expr::I64LtS:
+      case Expr::I64LtU:
+      case Expr::I64LeS:
+      case Expr::I64LeU:
+      case Expr::I64GtS:
+      case Expr::I64GtU:
+      case Expr::I64GeS:
+      case Expr::I64GeU:
+        return AstDecodeComparison(c, ValType::I64, expr);
+      case Expr::F32Eq:
+      case Expr::F32Ne:
+      case Expr::F32Lt:
+      case Expr::F32Le:
+      case Expr::F32Gt:
+      case Expr::F32Ge:
+        return AstDecodeComparison(c, ValType::F32, expr);
+      case Expr::F64Eq:
+      case Expr::F64Ne:
+      case Expr::F64Lt:
+      case Expr::F64Le:
+      case Expr::F64Gt:
+      case Expr::F64Ge:
+        return AstDecodeComparison(c, ValType::F64, expr);
+      case Expr::I32Eqz:
+        return AstDecodeConversion(c, ValType::I32, ValType::I32, expr);
+      case Expr::I64Eqz:
+        return AstDecodeConversion(c, ValType::I64, ValType::I32, expr);
+      case Expr::I32TruncSF32:
+      case Expr::I32TruncUF32:
+      case Expr::I32ReinterpretF32:
+        return AstDecodeConversion(c, ValType::F32, ValType::I32, expr);
+      case Expr::I32TruncSF64:
+      case Expr::I32TruncUF64:
+        return AstDecodeConversion(c, ValType::F64, ValType::I32, expr);
+      case Expr::I64ExtendSI32:
+      case Expr::I64ExtendUI32:
+        return AstDecodeConversion(c, ValType::I32, ValType::I64, expr);
+      case Expr::I64TruncSF32:
+      case Expr::I64TruncUF32:
+        return AstDecodeConversion(c, ValType::F32, ValType::I64, expr);
+      case Expr::I64TruncSF64:
+      case Expr::I64TruncUF64:
+      case Expr::I64ReinterpretF64:
+        return AstDecodeConversion(c, ValType::F64, ValType::I64, expr);
+      case Expr::F32ConvertSI32:
+      case Expr::F32ConvertUI32:
+      case Expr::F32ReinterpretI32:
+        return AstDecodeConversion(c, ValType::I32, ValType::F32, expr);
+      case Expr::F32ConvertSI64:
+      case Expr::F32ConvertUI64:
+        return AstDecodeConversion(c, ValType::I64, ValType::F32, expr);
+      case Expr::F32DemoteF64:
+        return AstDecodeConversion(c, ValType::F64, ValType::F32, expr);
+      case Expr::F64ConvertSI32:
+      case Expr::F64ConvertUI32:
+        return AstDecodeConversion(c, ValType::I32, ValType::F64, expr);
+      case Expr::F64ConvertSI64:
+      case Expr::F64ConvertUI64:
+      case Expr::F64ReinterpretI64:
+        return AstDecodeConversion(c, ValType::I64, ValType::F64, expr);
+      case Expr::F64PromoteF32:
+        return AstDecodeConversion(c, ValType::F32, ValType::F64, expr);
+      case Expr::I32Load8S:
+      case Expr::I32Load8U:
+        return AstDecodeLoad(c, ValType::I32, 1, expr);
+      case Expr::I32Load16S:
+      case Expr::I32Load16U:
+        return AstDecodeLoad(c, ValType::I32, 2, expr);
+      case Expr::I32Load:
+        return AstDecodeLoad(c, ValType::I32, 4, expr);
+      case Expr::I64Load8S:
+      case Expr::I64Load8U:
+        return AstDecodeLoad(c, ValType::I64, 1, expr);
+      case Expr::I64Load16S:
+      case Expr::I64Load16U:
+        return AstDecodeLoad(c, ValType::I64, 2, expr);
+      case Expr::I64Load32S:
+      case Expr::I64Load32U:
+        return AstDecodeLoad(c, ValType::I64, 4, expr);
+      case Expr::I64Load:
+        return AstDecodeLoad(c, ValType::I64, 8, expr);
+      case Expr::F32Load:
+        return AstDecodeLoad(c, ValType::F32, 4, expr);
+      case Expr::F64Load:
+        return AstDecodeLoad(c, ValType::F64, 8, expr);
+      case Expr::I32Store8:
+        return AstDecodeStore(c, ValType::I32, 1, expr);
+      case Expr::I32Store16:
+        return AstDecodeStore(c, ValType::I32, 2, expr);
+      case Expr::I32Store:
+        return AstDecodeStore(c, ValType::I32, 4, expr);
+      case Expr::I64Store8:
+        return AstDecodeStore(c, ValType::I64, 1, expr);
+      case Expr::I64Store16:
+        return AstDecodeStore(c, ValType::I64, 2, expr);
+      case Expr::I64Store32:
+        return AstDecodeStore(c, ValType::I64, 4, expr);
+      case Expr::I64Store:
+        return AstDecodeStore(c, ValType::I64, 8, expr);
+      case Expr::F32Store:
+        return AstDecodeStore(c, ValType::F32, 4, expr);
+      case Expr::F64Store:
+        return AstDecodeStore(c, ValType::F64, 8, expr);
+      case Expr::Br:
+      case Expr::BrIf:
+        return AstDecodeBranch(c, expr);
+      case Expr::BrTable:
+        return AstDecodeBrTable(c);
+      case Expr::Return:
+        return AstDecodeReturn(c);
+      case Expr::Unreachable:
+        if (!c.iter().readUnreachable())
+            return false;
+        tmp = new(c.lifo) AstUnreachable();
+        c.iter().setResult(AstDecodeStackItem(tmp));
+        return tmp;
+      default:
+        // Note: it's important not to remove this default since readExpr()
+        // can return Expr values for which there is no enumerator.
+        break;
+    }
+
+    return c.iter().unrecognizedOpcode(expr);
+}
+
+/*****************************************************************************/
+// wasm decoding and generation
+
+static bool
+AstDecodeTypeSection(AstDecodeContext& c)
+{
+    uint32_t sectionStart, sectionSize;
+    if (!c.d.startSection(TypeSectionId, &sectionStart, &sectionSize))
+        return AstDecodeFail(c, "failed to start section");
+    if (sectionStart == Decoder::NotStarted)
+        return true;
+
+    uint32_t numSigs;
+    if (!c.d.readVarU32(&numSigs))
+        return AstDecodeFail(c, "expected number of signatures");
+
+    if (numSigs > MaxSigs)
+        return AstDecodeFail(c, "too many signatures");
+
+    for (uint32_t sigIndex = 0; sigIndex < numSigs; sigIndex++) {
+        uint32_t form;
+        if (!c.d.readVarU32(&form) || form != uint32_t(TypeConstructor::Function))
+            return AstDecodeFail(c, "expected function form");
+
+        uint32_t numArgs;
+        if (!c.d.readVarU32(&numArgs))
+            return AstDecodeFail(c, "bad number of function args");
+
+        if (numArgs > MaxArgsPerFunc)
+            return AstDecodeFail(c, "too many arguments in signature");
+
+        AstValTypeVector args(c.lifo);
+        if (!args.resize(numArgs))
+            return false;
+
+        for (uint32_t i = 0; i < numArgs; i++) {
+            if (!c.d.readValType(&args[i]))
+                return AstDecodeFail(c, "bad value type");
+        }
+
+        uint32_t numRets;
+        if (!c.d.readVarU32(&numRets))
+            return AstDecodeFail(c, "bad number of function returns");
+
+        if (numRets > 1)
+            return AstDecodeFail(c, "too many returns in signature");
+
+        ExprType result = ExprType::Void;
+
+        if (numRets == 1) {
+            ValType type;
+            if (!c.d.readValType(&type))
+                return AstDecodeFail(c, "bad expression type");
+
+            result = ToExprType(type);
+        }
+
+        AstSig sigNoName(Move(args), result);
+        AstName sigName;
+        if (!AstDecodeGenerateName(c, AstName(MOZ_UTF16("$type$"), 6), sigIndex, sigName))
+            return false;
+
+        AstSig* sig = new(c.lifo) AstSig(sigName, Move(sigNoName));
+        if (!c.module().append(sig))
+            return false;
+    }
+
+    if (!c.d.finishSection(sectionStart, sectionSize))
+        return AstDecodeFail(c, "decls section byte size mismatch");
+
+    return true;
+}
+
+static bool
+AstDecodeSignatureIndex(AstDecodeContext& c, uint32_t* sigIndex)
+{
+    if (!c.d.readVarU32(sigIndex))
+        return AstDecodeFail(c, "expected signature index");
+
+    if (*sigIndex >= c.module().sigs().length())
+        return AstDecodeFail(c, "signature index out of range");
+
+    return true;
+}
+
+static bool
+AstDecodeFunctionSection(AstDecodeContext& c)
+{
+    uint32_t sectionStart, sectionSize;
+    if (!c.d.startSection(FunctionSectionId, &sectionStart, &sectionSize))
+        return AstDecodeFail(c, "failed to start section");
+    if (sectionStart == Decoder::NotStarted)
+        return true;
+
+    uint32_t numDecls;
+    if (!c.d.readVarU32(&numDecls))
+        return AstDecodeFail(c, "expected number of declarations");
+
+    if (numDecls > MaxFuncs)
+        return AstDecodeFail(c, "too many functions");
+
+
+    if (!c.funcSigs().resize(numDecls))
+        return false;
+
+    for (uint32_t i = 0; i < numDecls; i++) {
+        if (!AstDecodeSignatureIndex(c, &c.funcSigs()[i]))
+            return false;
+    }
+
+    if (!c.d.finishSection(sectionStart, sectionSize))
+        return AstDecodeFail(c, "decls section byte size mismatch");
+
+    return true;
+}
+
+static bool
+AstDecodeTableSection(AstDecodeContext& c)
+{
+    uint32_t sectionStart, sectionSize;
+    if (!c.d.startSection(TableSectionId, &sectionStart, &sectionSize))
+        return AstDecodeFail(c, "failed to start section");
+    if (sectionStart == Decoder::NotStarted)
+        return true;
+
+    if (!c.d.readVarU32(&c.numTableElems))
+        return AstDecodeFail(c, "expected number of table elems");
+
+    if (c.numTableElems > MaxTableElems)
+        return AstDecodeFail(c, "too many table elements");
+
+    AstTableElemVector elems(c.lifo);
+    if (!elems.resize(c.numTableElems))
+        return false;
+
+    for (uint32_t i = 0; i < c.numTableElems; i++) {
+        uint32_t funcIndex;
+        if (!c.d.readVarU32(&funcIndex))
+            return AstDecodeFail(c, "expected table element");
+
+        if (funcIndex >= c.funcSigs().length())
+            return AstDecodeFail(c, "table element out of range");
+
+        elems[i] = AstRef(AstName(), funcIndex);
+    }
+
+    c.module().initTable(new(c.lifo) AstTable(Move(elems)));
+
+    if (!c.d.finishSection(sectionStart, sectionSize))
+        return AstDecodeFail(c, "table section byte size mismatch");
+
+    return true;
+}
+
+static bool
+AstDecodeName(AstDecodeContext& c, AstName* name)
+{
+    Bytes bytes;
+    if (!c.d.readBytes(&bytes))
+        return false;
+    size_t length = bytes.length();
+    char16_t *buffer = static_cast<char16_t *>(c.lifo.alloc(length * sizeof(char16_t)));
+    for (size_t i = 0; i < length; i++)
+        buffer[i] = bytes[i];
+    *name = AstName(buffer, length);
+    return true;
+}
+
+static bool
+AstDecodeImport(AstDecodeContext& c, uint32_t importIndex, AstImport** import)
+{
+    uint32_t sigIndex = AstNoIndex;
+    if (!AstDecodeSignatureIndex(c, &sigIndex))
+        return false;
+
+    AstName moduleName;
+    if (!AstDecodeName(c, &moduleName))
+        return AstDecodeFail(c, "expected import module name");
+
+    if (moduleName.empty())
+        return AstDecodeFail(c, "module name cannot be empty");
+
+    AstName funcName;
+    if (!AstDecodeName(c, &funcName))
+        return AstDecodeFail(c, "expected import func name");
+
+    AstName importName;
+    if (!AstDecodeGenerateName(c, AstName(MOZ_UTF16("$import$"), 8), importIndex, importName))
+        return false;
+
+    *import = new(c.lifo) AstImport(importName, moduleName, funcName, sigIndex);
+    return *import;
+}
+
+static bool
+AstDecodeImportSection(AstDecodeContext& c)
+{
+    uint32_t sectionStart, sectionSize;
+    if (!c.d.startSection(ImportSectionId, &sectionStart, &sectionSize))
+        return AstDecodeFail(c, "failed to start section");
+    if (sectionStart == Decoder::NotStarted)
+        return true;
+
+    uint32_t numImports;
+    if (!c.d.readVarU32(&numImports))
+        return AstDecodeFail(c, "failed to read number of imports");
+
+    if (numImports > MaxImports)
+        return AstDecodeFail(c,  "too many imports");
+
+    for (uint32_t i = 0; i < numImports; i++) {
+        AstImport* import;
+        if (!AstDecodeImport(c, i, &import))
+            return false;
+        if (!c.module().append(import))
+            return false;
+    }
+
+    if (!c.d.finishSection(sectionStart, sectionSize))
+        return AstDecodeFail(c, "import section byte size mismatch");
+
+    return true;
+}
+
+static bool
+AstDecodeMemorySection(AstDecodeContext& c)
+{
+    uint32_t sectionStart, sectionSize;
+    if (!c.d.startSection(MemorySectionId, &sectionStart, &sectionSize))
+        return AstDecodeFail(c, "failed to start section");
+    if (sectionStart == Decoder::NotStarted)
+        return true;
+
+    uint32_t initialSizePages;
+    if (!c.d.readVarU32(&initialSizePages))
+        return AstDecodeFail(c, "expected initial memory size");
+
+    CheckedInt<int32_t> initialSize = initialSizePages;
+    initialSize *= PageSize;
+    if (!initialSize.isValid())
+        return AstDecodeFail(c, "initial memory size too big");
+
+    uint32_t maxSizePages;
+    if (!c.d.readVarU32(&maxSizePages))
+        return AstDecodeFail(c, "expected initial memory size");
+
+    CheckedInt<int32_t> maxSize = maxSizePages;
+    maxSize *= PageSize;
+    if (!maxSize.isValid())
+        return AstDecodeFail(c, "initial memory size too big");
+
+    uint8_t exported;
+    if (!c.d.readFixedU8(&exported))
+        return AstDecodeFail(c, "expected exported byte");
+
+    c.initialSizePages.emplace(initialSizePages);
+    if (initialSizePages != maxSizePages) {
+      c.maxSizePages.emplace(maxSizePages);
+    }
+
+    if (exported) {
+        if (!c.module().append(new(c.lifo) AstExport(AstName(MOZ_UTF16("memory"), 6))))
+            return false;
+    }
+
+    if (!c.d.finishSection(sectionStart, sectionSize))
+        return AstDecodeFail(c, "memory section byte size mismatch");
+
+    return true;
+}
+
+static bool
+AstDecodeFunctionExport(AstDecodeContext& c, AstExport** export_)
+{
+    uint32_t funcIndex;
+    if (!c.d.readVarU32(&funcIndex))
+        return AstDecodeFail(c, "expected export internal index");
+
+    if (funcIndex >= c.funcSigs().length())
+        return AstDecodeFail(c, "export function index out of range");
+
+    AstName fieldName;
+    if (!AstDecodeName(c, &fieldName))
+        return AstDecodeFail(c, "expected export name");
+
+    *export_ = new(c.lifo) AstExport(fieldName, AstRef(AstName(), funcIndex));
+    return *export_;
+}
+
+static bool
+AstDecodeExportSection(AstDecodeContext& c)
+{
+    uint32_t sectionStart, sectionSize;
+    if (!c.d.startSection(ExportSectionId, &sectionStart, &sectionSize))
+        return AstDecodeFail(c, "failed to start section");
+    if (sectionStart == Decoder::NotStarted)
+        return true;
+
+    uint32_t numExports;
+    if (!c.d.readVarU32(&numExports))
+        return AstDecodeFail(c, "failed to read number of exports");
+
+    if (numExports > MaxExports)
+        return AstDecodeFail(c, "too many exports");
+
+    for (uint32_t i = 0; i < numExports; i++) {
+        AstExport* export_;
+        if (!AstDecodeFunctionExport(c, &export_))
+            return false;
+        if (!c.module().append(export_))
+            return false;
+    }
+
+    if (!c.d.finishSection(sectionStart, sectionSize))
+        return AstDecodeFail(c, "export section byte size mismatch");
+
+    return true;
+}
+
+static bool
+AstDecodeFunctionBody(AstDecodeContext &c, uint32_t funcIndex, AstFunc** func)
+{
+    uint32_t bodySize;
+    if (!c.d.readVarU32(&bodySize))
+        return AstDecodeFail(c, "expected number of function body bytes");
+
+    if (c.d.bytesRemain() < bodySize)
+        return AstDecodeFail(c, "function body length too big");
+
+    const uint8_t* bodyBegin = c.d.currentPosition();
+    const uint8_t* bodyEnd = bodyBegin + bodySize;
+
+    AstDecodeExprIter iter(AstDecodePolicy(), c.d);
+
+    uint32_t sigIndex = c.funcSigs()[funcIndex];
+    const AstSig* sig = c.module().sigs()[sigIndex];
+
+    AstValTypeVector vars(c.lifo);
+    AstNameVector localsNames(c.lifo);
+    AstExprVector body(c.lifo);
+
+    ValTypeVector locals;
+    if (!locals.appendAll(sig->args()))
+        return false;
+
+    if (!DecodeLocalEntries(c.d, &locals))
+        return AstDecodeFail(c, "failed decoding local entries");
+
+    c.startFunction(&iter, &locals);
+
+    uint32_t numParams = sig->args().length();
+    uint32_t numLocals = locals.length();
+    for (uint32_t i = numParams; i < numLocals; i++) {
+        if (!vars.append(locals[i]))
+            return false;
+    }
+    for (uint32_t i = 0; i < numLocals; i++) {
+        AstName varName;
+        if (!AstDecodeGenerateName(c, AstName(MOZ_UTF16("$var$"), 5), i, varName))
+            return false;
+        if (!localsNames.append(varName))
+            return false;
+    }
+
+    if (!c.iter().readFunctionStart())
+        return false;
+
+    while (c.d.currentPosition() < bodyEnd) {
+        if (!AstDecodeExpr(c))
+            return false;
+        AstDecodeStackItem item = c.iter().getResult();
+        body.shrinkBy(item.stackDelta);
+        if (!body.append(item.expr))
+            return false;
+    }
+
+    AstDecodeStackItem tmp;
+    if (!c.iter().readFunctionEnd(sig->ret(), &tmp))
+        return false;
+
+    c.endFunction();
+
+    if (c.d.currentPosition() != bodyEnd)
+        return AstDecodeFail(c, "function body length mismatch");
+
+    AstName funcName;
+    if (!AstDecodeGenerateName(c, AstName(MOZ_UTF16("$func$"), 6), funcIndex, funcName))
+        return false;
+    AstRef sigRef;
+    if (!AstDecodeGenerateRef(c, AstName(MOZ_UTF16("$type$"), 6), sigIndex, sigRef))
+        return false;
+    *func = new(c.lifo) AstFunc(funcName, sigRef,
+                                    Move(vars), Move(localsNames), Move(body));
+    return *func;
+}
+
+static bool
+AstDecodeCodeSection(AstDecodeContext &c)
+{
+    uint32_t sectionStart, sectionSize;
+    if (!c.d.startSection(CodeSectionId, &sectionStart, &sectionSize))
+        return AstDecodeFail(c, "failed to start section");
+
+    if (sectionStart == Decoder::NotStarted) {
+        if (c.funcSigs().length() != 0)
+            return AstDecodeFail(c, "expected function bodies");
+
+        return false;
+    }
+
+    uint32_t numFuncBodies;
+    if (!c.d.readVarU32(&numFuncBodies))
+        return AstDecodeFail(c, "expected function body count");
+
+    if (numFuncBodies != c.funcSigs().length())
+        return AstDecodeFail(c, "function body count does not match function signature count");
+
+    for (uint32_t funcIndex = 0; funcIndex < numFuncBodies; funcIndex++) {
+        AstFunc* func;
+        if (!AstDecodeFunctionBody(c, funcIndex, &func))
+            return false;
+        if (!c.module().append(func))
+            return false;
+    }
+
+    if (!c.d.finishSection(sectionStart, sectionSize))
+        return AstDecodeFail(c, "function section byte size mismatch");
+
+    return true;
+}
+
+static bool
+AstDecodeDataSection(AstDecodeContext &c)
+{
+    uint32_t sectionStart, sectionSize;
+    if (!c.d.startSection(DataSectionId, &sectionStart, &sectionSize))
+        return AstDecodeFail(c, "failed to start section");
+    AstSegmentVector segments(c.lifo);
+    if (sectionStart == Decoder::NotStarted) {
+        if (!c.initialSizePages)
+            return true;
+        c.module().setMemory(new(c.lifo) AstMemory(*c.initialSizePages, c.maxSizePages, Move(segments)));
+
+        return true;
+    }
+
+    uint32_t numSegments;
+    if (!c.d.readVarU32(&numSegments))
+        return AstDecodeFail(c, "failed to read number of data segments");
+
+    uint32_t initialSizePages = c.initialSizePages ? *c.initialSizePages : 0;
+    uint32_t heapLength = initialSizePages * PageSize;
+    uint32_t prevEnd = 0;
+
+    for (uint32_t i = 0; i < numSegments; i++) {
+        uint32_t dstOffset;
+        if (!c.d.readVarU32(&dstOffset))
+            return AstDecodeFail(c, "expected segment destination offset");
+
+        if (dstOffset < prevEnd)
+            return AstDecodeFail(c, "data segments must be disjoint and ordered");
+
+        uint32_t numBytes;
+        if (!c.d.readVarU32(&numBytes))
+            return AstDecodeFail(c, "expected segment size");
+
+        if (dstOffset > heapLength || heapLength - dstOffset < numBytes)
+            return AstDecodeFail(c, "data segment does not fit in memory");
+
+        const uint8_t* src;
+        if (!c.d.readBytesRaw(numBytes, &src))
+            return AstDecodeFail(c, "data segment shorter than declared");
+
+        char16_t *buffer = static_cast<char16_t *>(c.lifo.alloc(numBytes * sizeof(char16_t)));
+        for (size_t i = 0; i < numBytes; i++)
+            buffer[i] = src[i];
+        AstName name(buffer, numBytes);
+        if (!segments.append(new(c.lifo) AstSegment(dstOffset, name)))
+            return false;
+        prevEnd = dstOffset + numBytes;
+    }
+
+    c.module().setMemory(new(c.lifo) AstMemory(initialSizePages, c.maxSizePages, Move(segments)));
+
+    if (!c.d.finishSection(sectionStart, sectionSize))
+        return AstDecodeFail(c, "data section byte size mismatch");
+
+    return true;
+}
+
+
+bool
+wasm::BinaryToAst(JSContext* cx, const uint8_t* bytes, uint32_t length,
+                  LifoAlloc& lifo, AstModule** module)
+{
+    AstModule* result = new(lifo) AstModule(lifo);
+    if (!result->init())
+        return false;
+
+    Decoder d(bytes, bytes + length);
+    AstDecodeContext c(cx, lifo, d, *result, true);
+
+    uint32_t u32;
+    if (!d.readFixedU32(&u32) || u32 != MagicNumber)
+        return AstDecodeFail(c, "failed to match magic number");
+
+    if (!d.readFixedU32(&u32) || u32 != EncodingVersion)
+        return AstDecodeFail(c, "failed to match binary version");
+
+    if (!AstDecodeTypeSection(c))
+        return false;
+
+    if (!AstDecodeImportSection(c))
+        return false;
+
+    if (!AstDecodeFunctionSection(c))
+        return false;
+
+    if (!AstDecodeTableSection(c))
+        return false;
+
+    if (!AstDecodeMemorySection(c))
+        return false;
+
+    if (!AstDecodeExportSection(c))
+        return false;
+
+    if (!AstDecodeCodeSection(c))
+        return false;
+
+    if (!AstDecodeDataSection(c))
+        return false;
+
+    while (!d.done()) {
+        if (!d.skipSection())
+            return AstDecodeFail(c, "failed to skip unknown section at end");
+    }
+
+    *module = result;
+    return true;
+}
+
new file mode 100644
--- /dev/null
+++ b/js/src/asmjs/WasmBinaryToAST.h
@@ -0,0 +1,36 @@
+/* -*- 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 2015 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 wasmbinarytoast_h
+#define wasmbinarytoast_h
+
+#include "asmjs/WasmAST.h"
+#include "asmjs/WasmTypes.h"
+
+#include "ds/LifoAlloc.h"
+
+namespace js {
+namespace wasm {
+
+bool
+BinaryToAst(JSContext* cx, const uint8_t* bytes, uint32_t length, LifoAlloc& lifo, AstModule** module);
+
+} // end wasm namespace
+} // end js namespace
+
+#endif // namespace wasmbinarytoast_h
--- a/js/src/asmjs/WasmBinaryToText.cpp
+++ b/js/src/asmjs/WasmBinaryToText.cpp
@@ -19,61 +19,53 @@
 #include "asmjs/WasmBinaryToText.h"
 
 #include "mozilla/CheckedInt.h"
 
 #include "jsnum.h"
 #include "jsprf.h"
 
 #include "asmjs/Wasm.h"
+#include "asmjs/WasmAST.h"
+#include "asmjs/WasmBinaryToAST.h"
 #include "asmjs/WasmTypes.h"
 #include "vm/ArrayBufferObject.h"
 #include "vm/StringBuffer.h"
 
 using namespace js;
 using namespace js::wasm;
 
 using mozilla::CheckedInt;
 using mozilla::IsInfinite;
 using mozilla::IsNaN;
 using mozilla::IsNegativeZero;
 
 struct WasmRenderContext
 {
     JSContext* cx;
-    Decoder& d;
+    AstModule* module;
     StringBuffer& buffer;
     uint32_t indent;
 
     DeclaredSigVector signatures;
     Uint32Vector funcSigs;
     Uint32Vector funcLocals;
     Uint32Vector importSigs;
 
     uint32_t currentFuncIndex;
 
-    WasmRenderContext(JSContext* cx, Decoder& d, StringBuffer& buffer)
-      : cx(cx), d(d), buffer(buffer), indent(0), currentFuncIndex(0)
+    WasmRenderContext(JSContext* cx, AstModule* module, StringBuffer& buffer)
+      : cx(cx), module(module), buffer(buffer), indent(0), currentFuncIndex(0)
     {}
 };
 
 /*****************************************************************************/
 // utilities
 
 static bool
-RenderFail(WasmRenderContext& c, const char* str)
-{
-    uint32_t offset = c.d.currentOffset();
-    char offsetStr[sizeof "4294967295"];
-    JS_snprintf(offsetStr, sizeof offsetStr, "%" PRIu32, offset);
-    JS_ReportErrorNumber(c.cx, GetErrorMessage, nullptr, JSMSG_WASM_DECODE_FAIL, offsetStr, str);
-    return false;
-}
-
-static bool
 RenderIndent(WasmRenderContext& c)
 {
     for (uint32_t i = 0; i < c.indent; i++) {
         if (!c.buffer.append("  "))
             return false;
     }
     return true;
 }
@@ -123,20 +115,22 @@ RenderDouble(WasmRenderContext& c, doubl
         if (num > 0)
             return c.buffer.append("infinity");
         return c.buffer.append("-infinity");
     }
     return NumberValueToStringBuffer(c.cx, DoubleValue(num), c.buffer);
 }
 
 static bool
-RenderString(WasmRenderContext& c, const uint8_t* s, size_t length)
+RenderEscapedString(WasmRenderContext& c, const AstName& s)
 {
+    size_t length = s.length();
+    const char16_t* p = s.begin();
     for (size_t i = 0; i < length; i++) {
-        uint8_t byte = s[i];
+        char16_t byte = p[i];
         bool success;
         switch (byte) {
             case '\n': success = c.buffer.append("\\n"); break;
             case '\r': success = c.buffer.append("\\0d"); break;
             case '\t': success = c.buffer.append("\\t"); break;
             case '\f': success = c.buffer.append("\\0c"); break;
             case '\b': success = c.buffer.append("\\08"); break;
             case '\\': success = c.buffer.append("\\\\"); break;
@@ -174,321 +168,211 @@ RenderExprType(WasmRenderContext& c, Exp
 
 static bool
 RenderValType(WasmRenderContext& c, ValType type)
 {
     return RenderExprType(c, ToExprType(type));
 }
 
 static bool
-RenderExpr(WasmRenderContext& c);
+RenderName(WasmRenderContext& c, const AstName& name)
+{
+    return c.buffer.append(name.begin(), name.end());
+}
 
 static bool
-RenderFullLine(WasmRenderContext& c)
+RenderRef(WasmRenderContext& c, const AstRef& ref) {
+    return ref.name().empty() ? RenderInt32(c, ref.index()) : RenderName(c, ref.name());
+}
+
+static bool
+RenderExpr(WasmRenderContext& c, AstExpr& expr);
+
+static bool
+RenderFullLine(WasmRenderContext& c, AstExpr& expr)
 {
     if (!RenderIndent(c))
         return false;
-    if (!RenderExpr(c))
+    if (!RenderExpr(c, expr))
         return false;
     return c.buffer.append('\n');
 }
 
 /*****************************************************************************/
 // binary format parsing and rendering
 
 static bool
-RenderNop(WasmRenderContext& c)
+RenderNop(WasmRenderContext& c, AstNop& nop)
 {
     return c.buffer.append("(nop)");
 }
 
 static bool
-RenderUnreachable(WasmRenderContext& c)
+RenderUnreachable(WasmRenderContext& c, AstUnreachable& unreachable)
 {
     return c.buffer.append("(trap)");
 }
 
 static bool
-RenderCallWithSig(WasmRenderContext& c, uint32_t sigIndex)
+RenderCallArgs(WasmRenderContext& c, const AstExprVector& args)
 {
-    const DeclaredSig& sig = c.signatures[sigIndex];
-    for (uint32_t i = 0; i < sig.args().length(); i++) {
+    for (uint32_t i = 0; i < args.length(); i++) {
         if (!c.buffer.append(" "))
             return false;
-        if (!RenderExpr(c))
+        if (!RenderExpr(c, *args[i]))
             return false;
     }
 
     return true;
 }
 
 static bool
-RenderCall(WasmRenderContext& c)
+RenderCall(WasmRenderContext& c, AstCall& call)
 {
-    uint32_t funcIndex;
-    if (!c.d.readVarU32(&funcIndex))
-        return RenderFail(c, "unable to read import index");
-
-    if (!c.buffer.append("(call $func$"))
+    if (call.expr() == Expr::Call) {
+        if (!c.buffer.append("(call "))
+            return false;
+    } else if (call.expr() == Expr::CallImport) {
+        if (!c.buffer.append("(call_import "))
+            return false;
+    } else
         return false;
-    if (!RenderInt32(c, funcIndex))
+    if (!RenderRef(c, call.func()))
         return false;
-
-    if (funcIndex >= c.funcSigs.length())
-        return RenderFail(c, "callee index out of range");
-
-    uint32_t sigIndex = c.funcSigs[funcIndex];
-    if (!RenderCallWithSig(c, sigIndex))
+    if (!RenderCallArgs(c, call.args()))
         return false;
     if (!c.buffer.append(")"))
         return false;
     return true;
 }
 
 static bool
-RenderCallImport(WasmRenderContext& c)
+RenderCallIndirect(WasmRenderContext& c, AstCallIndirect& call)
 {
-    uint32_t importIndex;
-    if (!c.d.readVarU32(&importIndex))
-        return RenderFail(c, "unable to read import index");
-
-    if (!c.buffer.append("(call_import $import$"))
-        return false;
-    if (!RenderInt32(c, importIndex))
-        return false;
-
-    if (importIndex >= c.importSigs.length())
-        return RenderFail(c, "import index out of range");
-
-    uint32_t sigIndex = c.importSigs[importIndex];
-    if (!RenderCallWithSig(c, sigIndex))
+    if (!c.buffer.append("(call_indirect "))
         return false;
-    if (!c.buffer.append(")"))
-        return false;
-
-    return true;
-}
-
-static bool
-RenderCallIndirect(WasmRenderContext& c)
-{
-    uint32_t sigIndex;
-    if (!c.d.readVarU32(&sigIndex))
-        return RenderFail(c, "unable to read indirect call signature index");
-
-    if (!c.buffer.append("(call_indirect $type$"))
-        return false;
-    if (!RenderInt32(c, sigIndex))
+    if (!RenderRef(c, call.sig()))
         return false;
 
     if (!c.buffer.append(" "))
         return false;
 
-    if (!RenderExpr(c))
+    if (!RenderExpr(c, *call.index()))
         return false;
 
     if (!c.buffer.append(" "))
         return false;
-
-    if (!RenderCallWithSig(c, sigIndex))
+    if (!RenderCallArgs(c, call.args()))
         return false;
-    if (!c.buffer.append(")"))
-        return false;
-
     return true;
 }
 
 static bool
-RenderConstI32(WasmRenderContext& c)
+RenderConst(WasmRenderContext& c, AstConst& cst)
 {
-    if (!c.buffer.append("(i32.const "))
+    if (!c.buffer.append('('))
+        return false;
+    if (!RenderValType(c, cst.val().type()))
+        return false;
+    if (!c.buffer.append(".const "))
         return false;
 
-    int32_t i32;
-    if (!c.d.readVarS32(&i32))
-        return RenderFail(c, "unable to read i32.const immediate");
+    switch (ToExprType(cst.val().type()))
+    {
+      case ExprType::I32:
+        if (!RenderInt32(c, (uint32_t)cst.val().i32()))
+            return false;
+        break;
+      case ExprType::I64:
+        if (!RenderInt64(c, (uint32_t)cst.val().i64()))
+            return false;
+        break;
+      case ExprType::F32:
+        if (!RenderDouble(c, (double)cst.val().f32()))
+            return false;
+        break;
+      case ExprType::F64:
+        if (!RenderDouble(c, cst.val().f64()))
+            return false;
+        break;
+      default:
+        return false;
+    }
+    if (!c.buffer.append(")"))
+        return false;
+    return true;
+}
 
-    if (!RenderInt32(c, (uint32_t)i32))
+static bool
+RenderGetLocal(WasmRenderContext& c, AstGetLocal& gl)
+{
+    if (!c.buffer.append("(get_local "))
+        return false;
+    if (!RenderRef(c, gl.local()))
+        return false;
+    if (!c.buffer.append(")"))
+        return false;
+    return true;
+}
+
+static bool
+RenderSetLocal(WasmRenderContext& c, AstSetLocal& sl)
+{
+    if (!c.buffer.append("(set_local "))
+        return false;
+    if (!RenderRef(c, sl.local()))
+        return false;
+    if (!c.buffer.append(" "))
+        return false;
+
+    if (!RenderExpr(c, sl.value()))
         return false;
 
     if (!c.buffer.append(")"))
         return false;
     return true;
 }
 
 static bool
-RenderConstI64(WasmRenderContext& c)
-{
-    if (!c.buffer.append("(i64.const "))
-        return false;
-
-    int64_t i64;
-    if (!c.d.readVarS64(&i64))
-        return RenderFail(c, "unable to read i64.const immediate");
-
-    if (!RenderInt64(c, i64))
-        return false;
-
-    if (!c.buffer.append(")"))
-        return false;
-    return true;
-}
-
-static bool
-RenderConstF32(WasmRenderContext& c)
-{
-    if (!c.buffer.append("(f32.const "))
-        return false;
-
-    float value;
-    if (!c.d.readFixedF32(&value))
-        return RenderFail(c, "unable to read f32.const immediate");
-
-    if (IsNaN(value)) {
-        const float jsNaN = (float)JS::GenericNaN();
-        if (memcmp(&value, &jsNaN, sizeof(value)) != 0)
-            return RenderFail(c, "NYI: NaN literals with custom payloads");
-    }
-
-    if (!RenderDouble(c, (double)value))
-        return false;
-
-    if (!c.buffer.append(")"))
-        return false;
-    return true;
-}
-
-static bool
-RenderConstF64(WasmRenderContext& c)
+RenderBlock(WasmRenderContext& c, AstBlock& block)
 {
-    if (!c.buffer.append("(f64.const "))
-        return false;
-
-    double value;
-    if (!c.d.readFixedF64(&value))
-        return RenderFail(c, "unable to read f64.const immediate");
-
-    if (IsNaN(value)) {
-        const double jsNaN = JS::GenericNaN();
-        if (memcmp(&value, &jsNaN, sizeof(value)) != 0)
-            return RenderFail(c, "NYI: NaN literals with custom payloads");
-    }
-
-    if (!RenderDouble(c, value))
-        return false;
-
-    if (!c.buffer.append(")"))
-        return false;
-    return true;
-}
-
-static bool
-RenderGetLocal(WasmRenderContext& c)
-{
-    uint32_t localIndex;
-    if (!c.d.readVarU32(&localIndex))
-        return RenderFail(c, "unable to read get_local index");
-
-    if (localIndex >= c.funcLocals[c.currentFuncIndex])
-        return RenderFail(c, "get_local index out of range");
-
-    if (!c.buffer.append("(get_local $var$"))
-        return false;
-    if (!RenderInt32(c, localIndex))
-        return false;
-    if (!c.buffer.append(")"))
-        return false;
-    return true;
-}
-
-static bool
-RenderSetLocal(WasmRenderContext& c)
-{
-    uint32_t localIndex;
-    if (!c.d.readVarU32(&localIndex))
-        return RenderFail(c, "unable to read set_local index");
-
-    if (localIndex >= c.funcLocals[c.currentFuncIndex])
-        return RenderFail(c, "set_local index out of range");
-
-    if (!c.buffer.append("(set_local $var$"))
-        return false;
-    if (!RenderInt32(c, localIndex))
-        return false;
-    if (!c.buffer.append(" "))
-        return false;
-
-    if (!RenderExpr(c))
-        return false;
-
-    if (!c.buffer.append(")"))
-        return false;
-    return true;
-}
-
-static bool
-RenderBlock(WasmRenderContext& c)
-{
-    if (!c.buffer.append("(block"))
+    if (block.expr() == Expr::Block) {
+        if (!c.buffer.append("(block "))
+            return false;
+    } else if (block.expr() == Expr::Loop) {
+        if (!c.buffer.append("(loop "))
+            return false;
+    } else
         return false;
 
     c.indent++;
 
-    uint32_t numExprs;
-    if (!c.d.readVarU32(&numExprs))
-        return RenderFail(c, "unable to read block's number of expressions");
-
+    uint32_t numExprs = block.exprs().length();
     for (uint32_t i = 0; i < numExprs; i++) {
         if (!c.buffer.append(" "))
             return false;
-        if (!RenderExpr(c))
+        if (!RenderExpr(c, *(block.exprs()[i])))
             return false;
     }
 
     c.indent--;
     if (!c.buffer.append(")"))
         return false;
 
     return true;
 }
 
 static bool
-RenderLoop(WasmRenderContext& c)
-{
-    if (!c.buffer.append("(loop"))
-        return false;
-
-    c.indent++;
-
-    uint32_t numExprs;
-    if (!c.d.readVarU32(&numExprs))
-        return RenderFail(c, "unable to read block's number of expressions");
-
-    for (uint32_t i = 0; i < numExprs; i++) {
-        if (!c.buffer.append(" "))
-            return false;
-        if (!RenderExpr(c))
-            return false;
-    }
-
-    c.indent--;
-    if (!c.buffer.append(")"))
-        return false;
-
-    return true;
-}
-
-static bool
-RenderUnaryOperator(WasmRenderContext& c, Expr expr, ValType argType)
+RenderUnaryOperator(WasmRenderContext& c, AstUnaryOperator& op)
 {
     if (!c.buffer.append("("))
       return false;
 
     bool success = false;
-    switch (expr) {
+    switch (op.expr()) {
       case Expr::I32Eqz:     success = c.buffer.append("i32.eqz"); break;
       case Expr::I32Clz:     success = c.buffer.append("i32.clz"); break;
       case Expr::I32Ctz:     success = c.buffer.append("i32.ctz"); break;
       case Expr::I32Popcnt:  success = c.buffer.append("i32.popcnt"); break;
       case Expr::I64Clz:     success = c.buffer.append("i64.clz"); break;
       case Expr::I64Ctz:     success = c.buffer.append("i64.ctz"); break;
       case Expr::I64Popcnt:  success = c.buffer.append("i64.popcnt"); break;
       case Expr::F32Abs:     success = c.buffer.append("f32.abs"); break;
@@ -506,32 +390,32 @@ RenderUnaryOperator(WasmRenderContext& c
       default: return false;
     }
     if (!success)
         return false;
 
     if (!c.buffer.append(" "))
         return false;
 
-    if (!RenderExpr(c))
+    if (!RenderExpr(c, *op.op()))
         return false;
 
     if (!c.buffer.append(")"))
         return false;
     return true;
 }
 
 static bool
-RenderBinaryOperator(WasmRenderContext& c, Expr expr, ValType argType)
+RenderBinaryOperator(WasmRenderContext& c, AstBinaryOperator& op)
 {
     if (!c.buffer.append("("))
       return false;
 
     bool success = false;
-    switch (expr) {
+    switch (op.expr()) {
       case Expr::I32Add:      success = c.buffer.append("i32.add"); break;
       case Expr::I32Sub:      success = c.buffer.append("i32.sub"); break;
       case Expr::I32Mul:      success = c.buffer.append("i32.mul"); break;
       case Expr::I32DivS:     success = c.buffer.append("i32.div_s"); break;
       case Expr::I32DivU:     success = c.buffer.append("i32.div_u"); break;
       case Expr::I32RemS:     success = c.buffer.append("i32.rem_s"); break;
       case Expr::I32RemU:     success = c.buffer.append("i32.rem_u"); break;
       case Expr::I32And:      success = c.buffer.append("i32.and"); break;
@@ -567,35 +451,65 @@ RenderBinaryOperator(WasmRenderContext& 
       case Expr::F64Min:      success = c.buffer.append("f64.min"); break;
       case Expr::F64Max:      success = c.buffer.append("f64.max"); break;
       default: return false;
     }
     if (!success)
         return false;
     if (!c.buffer.append(" "))
         return false;
-    if (!RenderExpr(c))
+    if (!RenderExpr(c, *op.lhs()))
         return false;
     if (!c.buffer.append(" "))
         return false;
-    if (!RenderExpr(c))
+    if (!RenderExpr(c, *op.rhs()))
         return false;
     if (!c.buffer.append(")"))
         return false;
     return true;
 }
 
 static bool
-RenderComparisonOperator(WasmRenderContext& c, Expr expr, ValType argType)
+RenderTernaryOperator(WasmRenderContext& c, AstTernaryOperator& op)
+{
+    if (!c.buffer.append("("))
+        return false;
+
+    bool success = false;
+    switch (op.expr()) {
+      case Expr::Select: success = c.buffer.append("select"); break;
+      default: return false;
+    }
+    if (!success)
+        return false;
+    if (!c.buffer.append(" "))
+        return false;
+    if (!RenderExpr(c, *op.op0()))
+        return false;
+    if (!c.buffer.append(" "))
+        return false;
+    if (!RenderExpr(c, *op.op1()))
+        return false;
+    if (!c.buffer.append(" "))
+        return false;
+    if (!RenderExpr(c, *op.op2()))
+        return false;
+    if (!c.buffer.append(")"))
+        return false;
+    return true;
+}
+
+static bool
+RenderComparisonOperator(WasmRenderContext& c, AstComparisonOperator& op)
 {
     if (!c.buffer.append("("))
       return false;
 
     bool success = false;
-    switch (expr) {
+    switch (op.expr()) {
       case Expr::I32Eq:  success = c.buffer.append("i32.eq"); break;
       case Expr::I32Ne:  success = c.buffer.append("i32.ne"); break;
       case Expr::I32LtS: success = c.buffer.append("i32.lt_s"); break;
       case Expr::I32LtU: success = c.buffer.append("i32.lt_u"); break;
       case Expr::I32LeS: success = c.buffer.append("i32.le_s"); break;
       case Expr::I32LeU: success = c.buffer.append("i32.le_u"); break;
       case Expr::I32GtS: success = c.buffer.append("i32.gt_s"); break;
       case Expr::I32GtU: success = c.buffer.append("i32.gt_u"); break;
@@ -625,35 +539,35 @@ RenderComparisonOperator(WasmRenderConte
       case Expr::F64Ge:  success = c.buffer.append("f64.ge"); break;
       default: return false;
     }
     if (!success)
         return false;
 
     if (!c.buffer.append(" "))
         return false;
-    if (!RenderExpr(c))
+    if (!RenderExpr(c, *op.lhs()))
         return false;
     if (!c.buffer.append(" "))
         return false;
-    if (!RenderExpr(c))
+    if (!RenderExpr(c, *op.rhs()))
         return false;
     if (!c.buffer.append(")"))
         return false;
     return true;
 }
 
 static bool
-RenderConversionOperator(WasmRenderContext& c, Expr expr, ValType to, ValType argType)
+RenderConversionOperator(WasmRenderContext& c, AstConversionOperator& op)
 {
     if (!c.buffer.append("("))
       return false;
 
     bool success = false;
-    switch (expr) {
+    switch (op.expr()) {
       case Expr::I32WrapI64:        success = c.buffer.append("i32.wrap/i64"); break;
       case Expr::I32TruncSF32:      success = c.buffer.append("i32.trunc_s/f32"); break;
       case Expr::I32TruncUF32:      success = c.buffer.append("i32.trunc_u/f32"); break;
       case Expr::I32ReinterpretF32: success = c.buffer.append("i32.reinterpret/f32"); break;
       case Expr::I32TruncSF64:      success = c.buffer.append("i32.trunc_s/f64"); break;
       case Expr::I32TruncUF64:      success = c.buffer.append("i32.trunc_u/f64"); break;
       case Expr::I64ExtendSI32:     success = c.buffer.append("i64.extend_s/i32"); break;
       case Expr::I64ExtendUI32:     success = c.buffer.append("i64.extend_u/i32"); break;
@@ -677,575 +591,409 @@ RenderConversionOperator(WasmRenderConte
       default: return false;
     }
     if (!success)
         return false;
 
     if (!c.buffer.append(" "))
         return false;
 
-    if (!RenderExpr(c))
+    if (!RenderExpr(c, *op.op()))
         return false;
 
     if (!c.buffer.append(")"))
         return false;
     return true;
 }
 
 static bool
-RenderIfElse(WasmRenderContext& c, bool hasElse)
+RenderIfElse(WasmRenderContext& c, AstIf& if_)
 {
     if (!c.buffer.append("(if "))
         return false;
-    if (!RenderExpr(c))
+    if (!RenderExpr(c, if_.cond()))
         return false;
 
     if (!c.buffer.append(" "))
         return false;
 
     c.indent++;
-    if (!RenderExpr(c))
+    if (!RenderExpr(c, if_.thenBranch()))
         return false;
     c.indent--;
 
-    if (hasElse) {
+    if (if_.hasElse()) {
         if (!c.buffer.append(" "))
             return false;
 
         c.indent++;
-        if (!RenderExpr(c))
+        if (!RenderExpr(c, if_.elseBranch()))
             return false;
         c.indent--;
     }
 
     if (!c.buffer.append(")"))
         return false;
 
     return true;
 }
 
 static bool
-RenderLoadStoreAddress(WasmRenderContext& c)
+RenderLoadStoreAddress(WasmRenderContext& c, const AstLoadStoreAddress& lsa, uint32_t defaultAlignLog2)
 {
-    uint32_t flags;
-    if (!c.d.readVarU32(&flags))
-        return RenderFail(c, "expected memory access flags");
-
-    uint32_t offset;
-    if (!c.d.readVarU32(&offset))
-        return RenderFail(c, "expected memory access offset");
-
-    if (offset != 0) {
+    if (lsa.offset() != 0) {
       if (!c.buffer.append(" offset="))
           return false;
-      if (!RenderInt32(c, offset))
+      if (!RenderInt32(c, lsa.offset()))
           return false;
     }
 
-    uint32_t alignLog2 = flags;
-    if (!c.buffer.append(" align="))
-        return false;
-    if (!RenderInt32(c, 1 << alignLog2))
-        return false;
+    uint32_t alignLog2 = lsa.flags();
+    if (defaultAlignLog2 != alignLog2) {
+      if (!c.buffer.append(" align="))
+          return false;
+      if (!RenderInt32(c, 1 << alignLog2))
+          return false;
+    }
 
     if (!c.buffer.append(" "))
         return false;
 
-    if (!RenderExpr(c))
+    if (!RenderExpr(c, lsa.base()))
         return false;
 
     return true;
 }
 
 static bool
-RenderLoad(WasmRenderContext& c, Expr expr, ValType loadType)
+RenderLoad(WasmRenderContext& c, AstLoad& load)
 {
-    if (!c.buffer.append("("))
-        return false;
-    if (!RenderValType(c, loadType))
-        return false;
-    if (!c.buffer.append(".load"))
-        return false;
-    switch (expr) {
+    uint32_t defaultAlignLog2;
+    switch (load.expr()) {
       case Expr::I32Load8S:
+        if (!c.buffer.append("(i32.load8_s"))
+            return false;
+        defaultAlignLog2 = 0;
+        break;
       case Expr::I64Load8S:
-        if (!c.buffer.append("8_s"))
+        if (!c.buffer.append("(i64.load8_s"))
             return false;
+        defaultAlignLog2 = 0;
         break;
       case Expr::I32Load8U:
+        if (!c.buffer.append("(i32.load8_u"))
+            return false;
+        defaultAlignLog2 = 0;
+        break;
       case Expr::I64Load8U:
-        if (!c.buffer.append("8_u"))
+        if (!c.buffer.append("(i64.load8_u"))
             return false;
+        defaultAlignLog2 = 0;
         break;
       case Expr::I32Load16S:
+        if (!c.buffer.append("(i32.load16_s"))
+            return false;
+        defaultAlignLog2 = 1;
+        break;
       case Expr::I64Load16S:
-        if (!c.buffer.append("16_s"))
+        if (!c.buffer.append("(i64.load16_s"))
             return false;
+        defaultAlignLog2 = 1;
         break;
       case Expr::I32Load16U:
+        if (!c.buffer.append("(i32.load16_u"))
+            return false;
+        defaultAlignLog2 = 1;
+        break;
       case Expr::I64Load16U:
-        if (!c.buffer.append("16_u"))
+        if (!c.buffer.append("(i64.load16_u"))
             return false;
+        defaultAlignLog2 = 1;
         break;
       case Expr::I64Load32S:
-        if (!c.buffer.append("32_s"))
+        if (!c.buffer.append("(i64.load32_s"))
             return false;
+        defaultAlignLog2 = 2;
         break;
       case Expr::I64Load32U:
-        if (!c.buffer.append("32_u"))
+        if (!c.buffer.append("(i64.load32_u"))
             return false;
+        defaultAlignLog2 = 2;
+        break;
+      case Expr::I32Load:
+        if (!c.buffer.append("(i32.load"))
+            return false;
+        defaultAlignLog2 = 2;
         break;
-      default: /* rest of them have not prefix */
+      case Expr::I64Load:
+        if (!c.buffer.append("(i64.load"))
+            return false;
+        defaultAlignLog2 = 3;
         break;
+      case Expr::F32Load:
+        if (!c.buffer.append("(f32.load"))
+            return false;
+        defaultAlignLog2 = 2;
+        break;
+      case Expr::F64Load:
+        if (!c.buffer.append("(f64.load"))
+            return false;
+        defaultAlignLog2 = 3;
+        break;
+      default:
+        return false;
     }
 
-    if (!RenderLoadStoreAddress(c))
+
+    if (!RenderLoadStoreAddress(c, load.address(), defaultAlignLog2))
         return false;
 
     if (!c.buffer.append(")"))
         return false;
     return true;
 }
 
 static bool
-RenderStore(WasmRenderContext& c, Expr expr, ValType storeType)
+RenderStore(WasmRenderContext& c, AstStore& store)
 {
-    if (!c.buffer.append("("))
-        return false;
-    if (!RenderValType(c, storeType))
-        return false;
-    if (!c.buffer.append(".store"))
-        return false;
-    switch (expr) {
+    uint32_t defaultAlignLog2;
+    switch (store.expr()) {
       case Expr::I32Store8:
+        if (!c.buffer.append("(i32.store8"))
+            return false;
+        defaultAlignLog2 = 0;
+        break;
       case Expr::I64Store8:
-        if (!c.buffer.append("8"))
+        if (!c.buffer.append("(i64.store8"))
             return false;
+        defaultAlignLog2 = 0;
         break;
       case Expr::I32Store16:
+        if (!c.buffer.append("(i32.store16"))
+            return false;
+        defaultAlignLog2 = 1;
+        break;
       case Expr::I64Store16:
-        if (!c.buffer.append("16"))
+        if (!c.buffer.append("(i64.store16"))
             return false;
+        defaultAlignLog2 = 1;
         break;
       case Expr::I64Store32:
-        if (!c.buffer.append("32"))
+        if (!c.buffer.append("(i64.store32"))
             return false;
+        defaultAlignLog2 = 2;
+        break;
+      case Expr::I32Store:
+        if (!c.buffer.append("(i32.store"))
+            return false;
+        defaultAlignLog2 = 2;
         break;
-      default: /* rest of them have not prefix */
+      case Expr::I64Store:
+        if (!c.buffer.append("(i64.store"))
+            return false;
+        defaultAlignLog2 = 3;
         break;
+      case Expr::F32Store:
+        if (!c.buffer.append("(f32.store"))
+            return false;
+        defaultAlignLog2 = 2;
+        break;
+      case Expr::F64Store:
+        if (!c.buffer.append("(f64.store"))
+            return false;
+        defaultAlignLog2 = 3;
+        break;
+      default:
+        return false;
     }
 
-    if (!RenderLoadStoreAddress(c))
+    if (!RenderLoadStoreAddress(c, store.address(), defaultAlignLog2))
         return false;
 
     if (!c.buffer.append(" "))
         return false;
 
-    if (!RenderExpr(c))
+    if (!RenderExpr(c, store.value()))
         return false;
 
     if (!c.buffer.append(")"))
         return false;
 
     return true;
 }
 
 static bool
-RenderBranch(WasmRenderContext& c, Expr expr)
+RenderBranch(WasmRenderContext& c, AstBranch& branch)
 {
+    Expr expr = branch.expr();
     MOZ_ASSERT(expr == Expr::BrIf || expr == Expr::Br);
 
-    uint32_t relativeDepth;
-    if (!c.d.readVarU32(&relativeDepth))
-        return RenderFail(c, "expected relative depth");
-
     if (expr == Expr::BrIf ? !c.buffer.append("(br_if ") : !c.buffer.append("(br "))
         return false;
 
-    if (!RenderInt32(c, relativeDepth))
-        return false;
-
-    if (!c.buffer.append(" "))
-        return false;
-
-    if (!RenderExpr(c))
+    if (!RenderRef(c, branch.target()))
         return false;
 
     if (expr == Expr::BrIf) {
         if (!c.buffer.append(" "))
             return false;
-        if (!RenderExpr(c))
-            return false;
-    }
 
-    if (!c.buffer.append(")"))
-        return false;
-    return true;
-}
-
-static bool
-RenderBrTable(WasmRenderContext& c)
-{
-    uint32_t tableLength;
-    if (!c.d.readVarU32(&tableLength))
-        return false;
-
-    if (!c.buffer.append("(br_table "))
-        return false;
-
-    for (uint32_t i = 0; i < tableLength; i++) {
-        uint32_t depth;
-        if (!c.d.readFixedU32(&depth))
-            return RenderFail(c, "missing br_table entry");
-
-        if (!RenderInt32(c, depth))
-            return false;
-
-        if (!c.buffer.append(" "))
+        if (!RenderExpr(c, branch.cond()))
             return false;
     }
 
-    uint32_t defaultDepth;
-    if (!c.d.readFixedU32(&defaultDepth))
-        return RenderFail(c, "expected default relative depth");
-
-    if (!RenderInt32(c, defaultDepth))
-        return false;
-
-    if (!c.buffer.append(" "))
-        return false;
-
-    // Value
-    if (!RenderExpr(c))
-        return false;
-
-    if (!c.buffer.append(" "))
-        return false;
-
-    // Index
-    if (!RenderExpr(c))
-        return false;
-
-    if (!c.buffer.append(")"))
-        return false;
-
-    return true;
-}
-
-static bool
-RenderReturn(WasmRenderContext& c)
-{
-    if (!c.buffer.append("(return"))
-        return false;
-
-    if (c.signatures[c.funcSigs[c.currentFuncIndex]].ret() != ExprType::Void) {
+    if (branch.maybeValue()) {
         if (!c.buffer.append(" "))
             return false;
-        if (!RenderExpr(c))
+        if (!RenderExpr(c, *(branch.maybeValue())))
             return false;
     }
 
     if (!c.buffer.append(")"))
         return false;
     return true;
 }
 
 static bool
-RenderSelect(WasmRenderContext& c)
+RenderBrTable(WasmRenderContext& c, AstBranchTable& table)
 {
-    if (!c.buffer.append("(select "))
+    if (!c.buffer.append("(br_table "))
         return false;
 
-    if (!RenderExpr(c))
-        return false;
+    uint32_t tableLength = table.table().length();
+    for (uint32_t i = 0; i < tableLength; i++) {
+        if (!RenderRef(c, table.table()[i]))
+            return false;
 
-    if (!c.buffer.append(" "))
-        return false;
+        if (!c.buffer.append(" "))
+            return false;
+    }
 
-    if (!RenderExpr(c))
+    if (!RenderRef(c, table.def()))
         return false;
 
     if (!c.buffer.append(" "))
         return false;
 
-    if (!RenderExpr(c))
+    if (table.maybeValue()) {
+      if (!RenderExpr(c, *(table.maybeValue())))
+          return false;
+
+      if (!c.buffer.append(" "))
+          return false;
+    }
+
+    // Index
+    if (!RenderExpr(c, table.index()))
+        return false;
+
+    if (!c.buffer.append(")"))
         return false;
 
-    return c.buffer.append(")");
+    return true;
+}
+
+static bool
+RenderReturn(WasmRenderContext& c, AstReturn& ret)
+{
+    if (!c.buffer.append("(return"))
+        return false;
+
+    if (ret.maybeExpr()) {
+        if (!c.buffer.append(" "))
+            return false;
+        if (!RenderExpr(c, *(ret.maybeExpr())))
+            return false;
+    }
+
+    if (!c.buffer.append(")"))
+        return false;
+    return true;
 }
 
 static bool
-RenderExpr(WasmRenderContext& c)
+RenderExpr(WasmRenderContext& c, AstExpr& expr)
 {
-    Expr expr;
-    if (!c.d.readExpr(&expr))
-        return RenderFail(c, "unable to read expression");
-
-    switch (expr) {
-      case Expr::Nop:
-        return RenderNop(c);
-      case Expr::Unreachable:
-        return RenderUnreachable(c);
-      case Expr::Call:
-        return RenderCall(c);
-      case Expr::CallImport:
-        return RenderCallImport(c);
-      case Expr::CallIndirect:
-        return RenderCallIndirect(c);
-      case Expr::I32Const:
-        return RenderConstI32(c);
-      case Expr::I64Const:
-        return RenderConstI64(c);
-      case Expr::F32Const:
-        return RenderConstF32(c);
-      case Expr::F64Const:
-        return RenderConstF64(c);
-      case Expr::GetLocal:
-        return RenderGetLocal(c);
-      case Expr::SetLocal:
-        return RenderSetLocal(c);
-      case Expr::Block:
-        return RenderBlock(c);
-      case Expr::Loop:
-        return RenderLoop(c);
-      case Expr::If:
-        return RenderIfElse(c, false);
-      case Expr::Else:
-        return RenderIfElse(c, true);
-      case Expr::I32Clz:
-      case Expr::I32Ctz:
-      case Expr::I32Popcnt:
-      case Expr::I32Eqz:
-        return RenderUnaryOperator(c, expr, ValType::I32);
-      case Expr::I64Clz:
-      case Expr::I64Ctz:
-      case Expr::I64Popcnt:
-        return RenderFail(c, "NYI: i64") &&
-               RenderUnaryOperator(c, expr, ValType::I64);
-      case Expr::F32Abs:
-      case Expr::F32Neg:
-      case Expr::F32Ceil:
-      case Expr::F32Floor:
-      case Expr::F32Sqrt:
-        return RenderUnaryOperator(c, expr, ValType::F32);
-      case Expr::F32Trunc:
-        return RenderFail(c, "NYI: trunc");
-      case Expr::F32Nearest:
-        return RenderFail(c, "NYI: nearest");
-      case Expr::F64Abs:
-      case Expr::F64Neg:
-      case Expr::F64Ceil:
-      case Expr::F64Floor:
-      case Expr::F64Sqrt:
-        return RenderUnaryOperator(c, expr, ValType::F64);
-      case Expr::F64Trunc:
-        return RenderFail(c, "NYI: trunc");
-      case Expr::F64Nearest:
-        return RenderFail(c, "NYI: nearest");
-      case Expr::I32Add:
-      case Expr::I32Sub:
-      case Expr::I32Mul:
-      case Expr::I32DivS:
-      case Expr::I32DivU:
-      case Expr::I32RemS:
-      case Expr::I32RemU:
-      case Expr::I32And:
-      case Expr::I32Or:
-      case Expr::I32Xor:
-      case Expr::I32Shl:
-      case Expr::I32ShrS:
-      case Expr::I32ShrU:
-        return RenderBinaryOperator(c, expr, ValType::I32);
-      case Expr::I64Add:
-      case Expr::I64Sub:
-      case Expr::I64Mul:
-      case Expr::I64DivS:
-      case Expr::I64DivU:
-      case Expr::I64RemS:
-      case Expr::I64RemU:
-      case Expr::I64And:
-      case Expr::I64Or:
-      case Expr::I64Xor:
-      case Expr::I64Shl:
-      case Expr::I64ShrS:
-      case Expr::I64ShrU:
-        return RenderBinaryOperator(c, expr, ValType::I64);
-      case Expr::F32Add:
-      case Expr::F32Sub:
-      case Expr::F32Mul:
-      case Expr::F32Div:
-      case Expr::F32Min:
-      case Expr::F32Max:
-        return RenderBinaryOperator(c, expr, ValType::F32);
-      case Expr::F32CopySign:
-        return RenderFail(c, "NYI: copysign");
-      case Expr::F64Add:
-      case Expr::F64Sub:
-      case Expr::F64Mul:
-      case Expr::F64Div:
-      case Expr::F64Min:
-      case Expr::F64Max:
-        return RenderBinaryOperator(c, expr, ValType::F64);
-      case Expr::F64CopySign:
-        return RenderFail(c, "NYI: copysign");
-      case Expr::I32Eq:
-      case Expr::I32Ne:
-      case Expr::I32LtS:
-      case Expr::I32LtU:
-      case Expr::I32LeS:
-      case Expr::I32LeU:
-      case Expr::I32GtS:
-      case Expr::I32GtU:
-      case Expr::I32GeS:
-      case Expr::I32GeU:
-        return RenderComparisonOperator(c, expr, ValType::I32);
-      case Expr::I64Eq:
-      case Expr::I64Ne:
-      case Expr::I64LtS:
-      case Expr::I64LtU:
-      case Expr::I64LeS:
-      case Expr::I64LeU:
-      case Expr::I64GtS:
-      case Expr::I64GtU:
-      case Expr::I64GeS:
-      case Expr::I64GeU:
-        return RenderComparisonOperator(c, expr, ValType::I64);
-      case Expr::F32Eq:
-      case Expr::F32Ne:
-      case Expr::F32Lt:
-      case Expr::F32Le:
-      case Expr::F32Gt:
-      case Expr::F32Ge:
-        return RenderComparisonOperator(c, expr, ValType::F32);
-      case Expr::F64Eq:
-      case Expr::F64Ne:
-      case Expr::F64Lt:
-      case Expr::F64Le:
-      case Expr::F64Gt:
-      case Expr::F64Ge:
-        return RenderComparisonOperator(c, expr, ValType::F64);
-      case Expr::I32WrapI64:
-        return RenderConversionOperator(c, expr, ValType::I32, ValType::I64);
-      case Expr::I32TruncSF32:
-      case Expr::I32TruncUF32:
-      case Expr::I32ReinterpretF32:
-        return RenderConversionOperator(c, expr, ValType::I32, ValType::F32);
-      case Expr::I32TruncSF64:
-      case Expr::I32TruncUF64:
-        return RenderConversionOperator(c, expr, ValType::I32, ValType::F64);
-      case Expr::I64ExtendSI32:
-      case Expr::I64ExtendUI32:
-        return RenderConversionOperator(c, expr, ValType::I64, ValType::I32);
-      case Expr::I64TruncSF32:
-      case Expr::I64TruncUF32:
-        return RenderConversionOperator(c, expr, ValType::I64, ValType::F32);
-      case Expr::I64TruncSF64:
-      case Expr::I64TruncUF64:
-      case Expr::I64ReinterpretF64:
-        return RenderConversionOperator(c, expr, ValType::I64, ValType::F64);
-      case Expr::F32ConvertSI32:
-      case Expr::F32ConvertUI32:
-      case Expr::F32ReinterpretI32:
-        return RenderConversionOperator(c, expr, ValType::F32, ValType::I32);
-      case Expr::F32ConvertSI64:
-      case Expr::F32ConvertUI64:
-        return RenderConversionOperator(c, expr, ValType::F32, ValType::I64);
-      case Expr::F32DemoteF64:
-        return RenderConversionOperator(c, expr, ValType::F32, ValType::I64);
-      case Expr::F64ConvertSI32:
-      case Expr::F64ConvertUI32:
-        return RenderConversionOperator(c, expr, ValType::F64, ValType::I32);
-      case Expr::F64ConvertSI64:
-      case Expr::F64ConvertUI64:
-      case Expr::F64ReinterpretI64:
-        return RenderConversionOperator(c, expr, ValType::F64, ValType::I64);
-      case Expr::F64PromoteF32:
-        return RenderConversionOperator(c, expr, ValType::F64, ValType::F32);
-      case Expr::I32Load8S:
-      case Expr::I32Load8U:
-      case Expr::I32Load16S:
-      case Expr::I32Load16U:
-      case Expr::I32Load:
-        return RenderLoad(c, expr, ValType::I32);
-      case Expr::I64Load:
-      case Expr::I64Load8S:
-      case Expr::I64Load8U:
-      case Expr::I64Load16S:
-      case Expr::I64Load16U:
-      case Expr::I64Load32S:
-      case Expr::I64Load32U:
-        return RenderFail(c, "NYI: i64") &&
-               RenderLoad(c, expr, ValType::I64);
-      case Expr::F32Load:
-        return RenderLoad(c, expr, ValType::F32);
-      case Expr::F64Load:
-        return RenderLoad(c, expr, ValType::F64);
-      case Expr::I32Store8:
-        return RenderStore(c, expr, ValType::I32);
-      case Expr::I32Store16:
-        return RenderStore(c, expr, ValType::I32);
-      case Expr::I32Store:
-        return RenderStore(c, expr, ValType::I32);
-      case Expr::I64Store:
-      case Expr::I64Store8:
-      case Expr::I64Store16:
-      case Expr::I64Store32:
-        return RenderFail(c, "NYI: i64") &&
-               RenderStore(c, expr, ValType::I64);
-      case Expr::F32Store:
-        return RenderStore(c, expr, ValType::F32);
-      case Expr::F64Store:
-        return RenderStore(c, expr, ValType::F64);
-      case Expr::Br:
-        return RenderBranch(c, expr);
-      case Expr::BrIf:
-        return RenderBranch(c, expr);
-      case Expr::BrTable:
-        return RenderBrTable(c);
-      case Expr::Return:
-        return RenderReturn(c);
-      case Expr::Select:
-        return RenderSelect(c);
+    switch (expr.kind()) {
+      case AstExprKind::Nop:
+        return RenderNop(c, expr.as<AstNop>());
+      case AstExprKind::Unreachable:
+        return RenderUnreachable(c, expr.as<AstUnreachable>());
+      case AstExprKind::Call:
+        return RenderCall(c, expr.as<AstCall>());
+      case AstExprKind::CallIndirect:
+        return RenderCallIndirect(c, expr.as<AstCallIndirect>());
+      case AstExprKind::Const:
+        return RenderConst(c, expr.as<AstConst>());
+      case AstExprKind::GetLocal:
+        return RenderGetLocal(c, expr.as<AstGetLocal>());
+      case AstExprKind::SetLocal:
+        return RenderSetLocal(c, expr.as<AstSetLocal>());
+      case AstExprKind::Block:
+        return RenderBlock(c, expr.as<AstBlock>());
+      case AstExprKind::If:
+        return RenderIfElse(c, expr.as<AstIf>());
+      case AstExprKind::UnaryOperator:
+        return RenderUnaryOperator(c, expr.as<AstUnaryOperator>());
+      case AstExprKind::BinaryOperator:
+        return RenderBinaryOperator(c, expr.as<AstBinaryOperator>());
+      case AstExprKind::TernaryOperator:
+        return RenderTernaryOperator(c, expr.as<AstTernaryOperator>());
+      case AstExprKind::ComparisonOperator:
+        return RenderComparisonOperator(c, expr.as<AstComparisonOperator>());
+      case AstExprKind::ConversionOperator:
+        return RenderConversionOperator(c, expr.as<AstConversionOperator>());
+      case AstExprKind::Load:
+        return RenderLoad(c, expr.as<AstLoad>());
+      case AstExprKind::Store:
+        return RenderStore(c, expr.as<AstStore>());
+      case AstExprKind::Branch:
+        return RenderBranch(c, expr.as<AstBranch>());
+      case AstExprKind::BranchTable:
+        return RenderBrTable(c, expr.as<AstBranchTable>());
+      case AstExprKind::Return:
+        return RenderReturn(c, expr.as<AstReturn>());
       default:
         // Note: it's important not to remove this default since readExpr()
         // can return Expr values for which there is no enumerator.
         break;
     }
 
-    return RenderFail(c, "bad expression code");
+    return false;
 }
 
 static bool
-RenderSignature(WasmRenderContext& c, const DeclaredSig& sig, bool varAssignment)
+RenderSignature(WasmRenderContext& c, const AstSig& sig, const AstNameVector* maybeLocals = nullptr)
 {
     uint32_t paramsNum = sig.args().length();
 
-    if (varAssignment) {
+    if (maybeLocals) {
       for (uint32_t i = 0; i < paramsNum; i++) {
           if (!c.buffer.append(" (param "))
               return false;
-          if (!c.buffer.append("$var$"))
-              return false;
-          if (!RenderInt32(c, i))
-              return false;
-          if (!c.buffer.append(" "))
-              return false;
-          ValType arg = sig.arg(i);
+          const AstName& name = (*maybeLocals)[i];
+          if (!name.empty()) {
+              if (!RenderName(c, name))
+                  return false;
+              if (!c.buffer.append(" "))
+                  return false;
+          }
+          ValType arg = sig.args()[i];
           if (!RenderValType(c, arg))
               return false;
           if (!c.buffer.append(")"))
               return false;
       }
     } else if (paramsNum > 0) {
       if (!c.buffer.append(" (param"))
           return false;
       for (uint32_t i = 0; i < paramsNum; i++) {
           if (!c.buffer.append(" "))
               return false;
-          ValType arg = sig.arg(i);
+          ValType arg = sig.args()[i];
           if (!RenderValType(c, arg))
               return false;
       }
       if (!c.buffer.append(")"))
           return false;
     }
     if (sig.ret() != ExprType::Void) {
         if (!c.buffer.append(" (result "))
@@ -1254,579 +1002,351 @@ RenderSignature(WasmRenderContext& c, co
             return false;
         if (!c.buffer.append(")"))
             return false;
     }
     return true;
 }
 
 static bool
-RenderTypeSection(WasmRenderContext& c)
+RenderTypeSection(WasmRenderContext& c, const AstModule::SigVector& sigs)
 {
-    uint32_t sectionStart, sectionSize;
-    if (!c.d.startSection(TypeSectionId, &sectionStart, &sectionSize))
-        return RenderFail(c, "failed to start section");
-    if (sectionStart == Decoder::NotStarted)
+    uint32_t numSigs = sigs.length();
+    if (!numSigs)
         return true;
 
-    uint32_t numSigs;
-    if (!c.d.readVarU32(&numSigs))
-        return RenderFail(c, "expected number of signatures");
-
-    if (!c.signatures.resize(numSigs))
-        return false;
-
     for (uint32_t sigIndex = 0; sigIndex < numSigs; sigIndex++) {
-        uint32_t form;
-        if (!c.d.readVarU32(&form) || form != uint32_t(TypeConstructor::Function))
-            return RenderFail(c, "expected function form");
-
-        uint32_t numArgs;
-        if (!c.d.readVarU32(&numArgs))
-            return RenderFail(c, "bad number of signature args");
-
-        ValTypeVector args;
-        if (!args.resize(numArgs))
-            return false;
-
-        for (uint32_t i = 0; i < numArgs; i++) {
-            ValType arg;
-            if (!c.d.readValType(&arg))
-                return RenderFail(c, "bad value type");
-            args[i] = arg;
-        }
-
-        uint32_t numRets;
-        if (!c.d.readVarU32(&numRets))
-            return RenderFail(c, "bad number of function returns");
-
-        if (numRets > 1)
-            return RenderFail(c, "too many returns in signature");
-
-        ExprType result = ExprType::Void;
-
-        if (numRets == 1) {
-            ValType type;
-            if (!c.d.readValType(&type))
-                return RenderFail(c, "bad expression type");
-
-            result = ToExprType(type);
-        }
-
-        c.signatures[sigIndex] = Sig(Move(args), result);
-
+        const AstSig* sig = sigs[sigIndex];
         if (!RenderIndent(c))
             return false;
-        if (!c.buffer.append("(type $type$"))
+        if (!c.buffer.append("(type"))
             return false;
-        if (!RenderInt32(c, sigIndex))
-            return false;
+        if (!sig->name().empty()) {
+          if (!c.buffer.append(" "))
+              return false;
+          if (!RenderName(c, sig->name()))
+              return false;
+        }
         if (!c.buffer.append(" (func"))
             return false;
-        if (!RenderSignature(c, c.signatures[sigIndex], false))
+        if (!RenderSignature(c, *sig))
             return false;
         if (!c.buffer.append("))\n"))
             return false;
     }
+    return true;
+}
 
-    if (!c.d.finishSection(sectionStart, sectionSize))
-        return RenderFail(c, "decls section byte size mismatch");
+static bool
+RenderTableSection(WasmRenderContext& c, AstTable* maybeTable, const AstModule::FuncVector& funcs)
+{
+    if (!maybeTable)
+        return true;
+
+    uint32_t numTableElems = maybeTable->elems().length();
+
+    if (!c.buffer.append("(table "))
+        return false;
+
+    for (uint32_t i = 0; i < numTableElems; i++) {
+        AstRef& elem = maybeTable->elems()[i];
+        AstFunc* func = funcs[elem.index()];
+        if (func->name().empty()) {
+            if (!RenderInt32(c, elem.index()))
+                return false;
+        } else {
+          if (!RenderName(c, func->name()))
+              return false;
+        }
+    }
+
+    if (!c.buffer.append(")"))
+        return false;
 
     return true;
 }
 
 static bool
-RenderFunctionSection(WasmRenderContext& c)
-{
-    uint32_t sectionStart, sectionSize;
-    if (!c.d.startSection(FunctionSectionId, &sectionStart, &sectionSize))
-        return RenderFail(c, "failed to start section");
-    if (sectionStart == Decoder::NotStarted)
-        return true;
-
-    uint32_t numDecls;
-    if (!c.d.readVarU32(&numDecls))
-        return RenderFail(c, "expected number of declarations");
-
-    if (!c.funcSigs.resize(numDecls))
-        return false;
-
-    for (uint32_t i = 0; i < numDecls; i++) {
-        uint32_t sigIndex;
-        if (!c.d.readVarU32(&sigIndex))
-            return RenderFail(c, "expected signature index");
-        c.funcSigs[i] = sigIndex;
-    }
-
-    if (!c.d.finishSection(sectionStart, sectionSize))
-        return RenderFail(c, "decls section byte size mismatch");
-
-    return true;
-}
-
-static bool
-RenderTableSection(WasmRenderContext& c)
+RenderImport(WasmRenderContext& c, AstImport& import, const AstModule::SigVector& sigs)
 {
-    uint32_t sectionStart, sectionSize;
-    if (!c.d.startSection(TableSectionId, &sectionStart, &sectionSize))
-        return RenderFail(c, "failed to start section");
-    if (sectionStart == Decoder::NotStarted)
-        return true;
-
-    uint32_t numTableElems;
-    if (!c.d.readVarU32(&numTableElems))
-        return RenderFail(c, "expected number of table elems");
-
-    if (!c.buffer.append("(table "))
-        return false;
-
-    for (uint32_t i = 0; i < numTableElems; i++) {
-        uint32_t funcIndex;
-        if (!c.d.readVarU32(&funcIndex))
-            return RenderFail(c, "expected table element");
-        if (!RenderInt32(c, funcIndex))
-            return false;
-        if (!c.buffer.append(" "))
-            return false;
-    }
-
-    if (!c.buffer.append(")"))
-        return false;
-
-    if (!c.d.finishSection(sectionStart, sectionSize))
-        return RenderFail(c, "table section byte size mismatch");
-
-    return true;
-}
-
-static bool
-RenderImport(WasmRenderContext& c, uint32_t importIndex)
-{
-    uint32_t sigIndex;
-    if (!c.d.readVarU32(&sigIndex))
-        return RenderFail(c, "expected signature index");
-
-    c.importSigs[importIndex] = sigIndex;
-
+    const AstSig* sig = sigs[import.sigIndex()];
     if (!RenderIndent(c))
         return false;
-    if (!c.buffer.append("(import $import$"))
+    if (!c.buffer.append("(import "))
         return false;
-    if (!RenderInt32(c, importIndex))
+    if (!RenderName(c, import.name()))
         return false;
     if (!c.buffer.append(" \""))
         return false;
 
-    Bytes moduleName;
-    if (!c.d.readBytes(&moduleName))
-        return RenderFail(c, "expected import module name");
-
-    if (moduleName.empty())
-        return RenderFail(c, "module name cannot be empty");
-
-    if (!RenderString(c, moduleName.begin(), moduleName.length()))
+    const AstName& moduleName = import.module();
+    if (!RenderEscapedString(c, moduleName))
         return false;
 
     if (!c.buffer.append("\" \""))
         return false;
 
-    Bytes funcName;
-    if (!c.d.readBytes(&funcName))
-        return RenderFail(c, "expected import func name");
-
-    if (!RenderString(c, funcName.begin(), funcName.length()))
+    const AstName& funcName = import.func();
+    if (!RenderEscapedString(c, funcName))
         return false;
 
     if (!c.buffer.append("\""))
         return false;
 
-    if (!RenderSignature(c, c.signatures[sigIndex], false))
+    if (!RenderSignature(c, *sig))
         return false;
     if (!c.buffer.append(")\n"))
         return false;
 
     return true;
 }
 
 
 static bool
-RenderImportSection(WasmRenderContext& c)
+RenderImportSection(WasmRenderContext& c, const AstModule::ImportVector& imports, const AstModule::SigVector& sigs)
 {
-    uint32_t sectionStart, sectionSize;
-    if (!c.d.startSection(ImportSectionId, &sectionStart, &sectionSize))
-        return RenderFail(c, "failed to start section");
-    if (sectionStart == Decoder::NotStarted)
-        return true;
-
-    uint32_t numImports;
-    if (!c.d.readVarU32(&numImports))
-        return RenderFail(c, "failed to read number of imports");
-
-    if (!c.importSigs.resize(numImports))
-        return false;
+    uint32_t numImports = imports.length();
 
     for (uint32_t i = 0; i < numImports; i++) {
-        if (!RenderImport(c, i))
+        if (!RenderImport(c, *imports[i], sigs))
             return false;
     }
 
-    if (!c.d.finishSection(sectionStart, sectionSize))
-        return RenderFail(c, "import section byte size mismatch");
-
     return true;
 }
 
 static bool
-RenderMemorySection(WasmRenderContext& c, uint32_t* memInitial, uint32_t* memMax)
+RenderExport(WasmRenderContext& c, AstExport& export_, const AstModule::FuncVector& funcs)
 {
-    *memInitial = 0;
-    *memMax = 0;
-
-    uint32_t sectionStart, sectionSize;
-    if (!c.d.startSection(MemorySectionId, &sectionStart, &sectionSize))
-        return RenderFail(c, "failed to start section");
-    if (sectionStart == Decoder::NotStarted)
-        return true;
-
-    uint32_t initialSizePages;
-    if (!c.d.readVarU32(&initialSizePages))
-        return RenderFail(c, "expected initial memory size");
-    *memInitial = initialSizePages;
-
-    uint32_t maxSizePages;
-    if (!c.d.readVarU32(&maxSizePages))
-        return RenderFail(c, "expected initial memory size");
-    *memMax = maxSizePages;
-
-    uint8_t exported;
-    if (!c.d.readFixedU8(&exported))
-        return RenderFail(c, "expected exported byte");
-
-    if (!c.d.finishSection(sectionStart, sectionSize))
-        return RenderFail(c, "memory section byte size mismatch");
-
-    if (exported && !c.buffer.append("(export \"memory\" memory)"))
-        return false;
-
-    return true;
-}
-
-static bool
-RenderExportName(WasmRenderContext& c)
-{
-    Bytes fieldBytes;
-    if (!c.d.readBytes(&fieldBytes)) {
-        RenderFail(c, "expected export name");
-        return false;
-    }
-
-    if (!RenderString(c, fieldBytes.begin(), fieldBytes.length()))
-        return false;
-
-    return true;
-}
-
-static bool
-RenderFunctionExport(WasmRenderContext& c)
-{
-    uint32_t funcIndex;
-    if (!c.d.readVarU32(&funcIndex))
-        return RenderFail(c, "expected export internal index");
-
-    if (funcIndex >= c.funcSigs.length())
-        return RenderFail(c, "export function index out of range");
-
     if (!RenderIndent(c))
         return false;
     if (!c.buffer.append("(export \""))
         return false;
-    if (!RenderExportName(c))
+    if (!RenderEscapedString(c, export_.name()))
         return false;
     if (!c.buffer.append("\" "))
         return false;
-    if (!c.buffer.append("$func$"))
-        return false;
-    if (!RenderInt32(c, funcIndex))
-        return false;
+    if (export_.kind() == AstExportKind::Memory) {
+        if (!c.buffer.append("memory"))
+          return false;
+    } else {
+        const AstFunc* func = funcs[export_.func().index()];
+        if (func->name().empty()) {
+            if (!RenderInt32(c, export_.func().index()))
+                return false;
+        } else {
+            if (!RenderName(c, func->name()))
+                return false;
+        }
+    }
     if (!c.buffer.append(")\n"))
         return false;
 
     return true;
 }
 
 static bool
-RenderExportSection(WasmRenderContext& c)
+RenderExportSection(WasmRenderContext& c, const AstModule::ExportVector& exports, const AstModule::FuncVector& funcs)
 {
-    uint32_t sectionStart, sectionSize;
-    if (!c.d.startSection(ExportSectionId, &sectionStart, &sectionSize))
-        return RenderFail(c, "failed to start section");
-    if (sectionStart == Decoder::NotStarted)
-        return true;
-
-    uint32_t numExports;
-    if (!c.d.readVarU32(&numExports))
-        return RenderFail(c, "failed to read number of exports");
-
+    uint32_t numExports = exports.length();
     for (uint32_t i = 0; i < numExports; i++) {
-        if (!RenderFunctionExport(c))
+        if (!RenderExport(c, *exports[i], funcs))
             return false;
     }
-
-    if (!c.d.finishSection(sectionStart, sectionSize))
-        return RenderFail(c, "export section byte size mismatch");
-
     return true;
 }
 
 static bool
-RenderFunctionBody(WasmRenderContext& c, uint32_t funcIndex, uint32_t paramsNum)
+RenderFunctionBody(WasmRenderContext& c, AstFunc& func, const AstModule::SigVector& sigs)
 {
-    uint32_t bodySize;
-    if (!c.d.readVarU32(&bodySize))
-        return RenderFail(c, "expected number of function body bytes");
-
-    if (c.d.bytesRemain() < bodySize)
-        return RenderFail(c, "function body length too big");
-
-    const uint8_t* bodyBegin = c.d.currentPosition();
-    const uint8_t* bodyEnd = bodyBegin + bodySize;
-
+    const AstSig* sig = sigs[func.sig().index()];
     c.indent++;
 
-    ValTypeVector locals;
-    if (!DecodeLocalEntries(c.d, &locals))
-        return RenderFail(c, "failed decoding local entries");
-
-    uint32_t localsNum = locals.length();
+    uint32_t argsNum = sig->args().length();
+    uint32_t localsNum = func.vars().length();
     if (localsNum > 0) {
         if (!RenderIndent(c))
             return false;
         for (uint32_t i = 0; i < localsNum; i++) {
             if (!c.buffer.append("(local "))
                 return false;
-            if (!c.buffer.append("$var$"))
-                return false;
-            if (!RenderInt32(c, i + paramsNum))
-                return false;
-            ValType local = locals[i];
-            if (!c.buffer.append(" "))
-                return false;
+            const AstName& name = func.locals()[argsNum + i];
+            if (!name.empty()) {
+              if (!RenderName(c, name))
+                  return false;
+              if (!c.buffer.append(" "))
+                  return false;
+            }
+            ValType local = func.vars()[i];
             if (!RenderValType(c, local))
                 return false;
             if (!c.buffer.append(") "))
                 return false;
         }
         if (!c.buffer.append("\n"))
             return false;
     }
 
-    if (funcIndex >= c.funcLocals.length() && !c.funcLocals.resize(funcIndex + 1))
-        return false;
 
-    c.funcLocals[funcIndex] = localsNum + paramsNum;
-
-    while (c.d.currentPosition() < bodyEnd) {
-      if (!RenderFullLine(c))
+    uint32_t exprsNum = func.body().length();
+    for (uint32_t i = 0; i < exprsNum; i++) {
+      if (!RenderFullLine(c, *func.body()[i]))
           return false;
     }
 
-    if (c.d.currentPosition() != bodyEnd)
-        return RenderFail(c, "function body length mismatch");
-
     c.indent--;
 
     return true;
 }
 
 static bool
-RenderCodeSection(WasmRenderContext& c)
+RenderCodeSection(WasmRenderContext& c, const AstModule::FuncVector& funcs, const AstModule::SigVector& sigs)
 {
-    uint32_t sectionStart, sectionSize;
-    if (!c.d.startSection(CodeSectionId, &sectionStart, &sectionSize))
-        return RenderFail(c, "failed to start section");
-
-    if (sectionStart == Decoder::NotStarted)
-        return true;
-
-    uint32_t numFuncSigs = c.funcSigs.length();
-
-    uint32_t numFuncBodies;
-    if (!c.d.readVarU32(&numFuncBodies))
-        return RenderFail(c, "expected function body count");
-    if (numFuncBodies != numFuncSigs)
-        return RenderFail(c, "function body count does not match function signature count");
+    uint32_t numFuncBodies = funcs.length();
     for (uint32_t funcIndex = 0; funcIndex < numFuncBodies; funcIndex++) {
-        uint32_t sigIndex = c.funcSigs[funcIndex];
-        const DeclaredSig& sig = c.signatures[sigIndex];
+        AstFunc* func = funcs[funcIndex];
+        uint32_t sigIndex = func->sig().index();
+        AstSig* sig = sigs[sigIndex];
 
         if (!RenderIndent(c))
             return false;
-        if (!c.buffer.append("(func $func$"))
+        if (!c.buffer.append("(func "))
             return false;
-        if (!RenderInt32(c, funcIndex))
-            return false;
+        if (!func->name().empty()) {
+          if (!RenderName(c, func->name()))
+              return false;
+        }
 
-        if (!RenderSignature(c, sig, true))
+        if (!RenderSignature(c, *sig, &(func->locals())))
             return false;
         if (!c.buffer.append("\n"))
             return false;
 
         c.currentFuncIndex = funcIndex;
 
         c.indent++;
-        if (!RenderFunctionBody(c, funcIndex, sig.args().length()))
+        if (!RenderFunctionBody(c, *func, sigs))
             return false;
         c.indent--;
         if (!RenderIndent(c))
             return false;
         if (!c.buffer.append(")\n"))
             return false;
     }
 
-    if (!c.d.finishSection(sectionStart, sectionSize))
-        return RenderFail(c, "function section byte size mismatch");
-
    return true;
 }
 
 
 static bool
-RenderDataSection(WasmRenderContext& c, uint32_t memInitial, uint32_t memMax)
+RenderDataSection(WasmRenderContext& c, AstMemory* maybeMemory)
 {
+    if (!maybeMemory)
+        return true;
+
     if (!RenderIndent(c))
         return false;
     if (!c.buffer.append("(memory "))
         return false;
-    if (!RenderInt32(c, memInitial))
+    if (!RenderInt32(c, maybeMemory->initialSize()))
        return false;
-    if (memMax != 0 && ~memMax != 0) {
+    Maybe<uint32_t> memMax = maybeMemory->maxSize();
+    if (memMax) {
         if (!c.buffer.append(" "))
             return false;
-        if (!RenderInt32(c, memMax))
+        if (!RenderInt32(c, *memMax))
             return false;
     }
 
     c.indent++;
-    uint32_t sectionStart;
-    uint32_t sectionSize;
-    if (!c.d.startSection(DataSectionId, &sectionStart, &sectionSize))
-        return RenderFail(c, "failed to start section");
-    if (sectionStart == Decoder::NotStarted) {
+
+    uint32_t numSegments = maybeMemory->segments().length();
+    if (!numSegments) {
       if (!c.buffer.append(")\n"))
           return false;
       return true;
     }
     if (!c.buffer.append("\n"))
         return false;
 
-    uint32_t numSegments;
-    if (!c.d.readVarU32(&numSegments))
-        return RenderFail(c, "failed to read number of data segments");
-
     for (uint32_t i = 0; i < numSegments; i++) {
-        uint32_t dstOffset;
-        if (!c.d.readVarU32(&dstOffset))
-            return RenderFail(c, "expected segment destination offset");
+        const AstSegment* segment = maybeMemory->segments()[i];
 
         if (!RenderIndent(c))
             return false;
         if (!c.buffer.append("(segment "))
            return false;
-        if (!RenderInt32(c, dstOffset))
+        if (!RenderInt32(c, segment->offset()))
            return false;
         if (!c.buffer.append(" \""))
            return false;
 
-        uint32_t numBytes;
-        if (!c.d.readVarU32(&numBytes))
-            return RenderFail(c, "expected segment size");
-
-        const uint8_t* src;
-        if (!c.d.readBytesRaw(numBytes, &src))
-            return RenderFail(c, "data segment shorter than declared");
-
-        RenderString(c, src, numBytes);
+        RenderEscapedString(c, segment->text());
 
         if (!c.buffer.append("\")\n"))
            return false;
     }
 
-    if (!c.d.finishSection(sectionStart, sectionSize))
-        return RenderFail(c, "data section byte size mismatch");
-
     c.indent--;
     if (!c.buffer.append(")\n"))
         return false;
 
     return true;
 }
 
 static bool
-RenderModule(WasmRenderContext& c)
+RenderModule(WasmRenderContext& c, AstModule& module)
 {
-    uint32_t u32;
-    if (!c.d.readFixedU32(&u32) || u32 != MagicNumber)
-        return RenderFail(c, "failed to match magic number");
-
-    if (!c.d.readFixedU32(&u32) || u32 != EncodingVersion)
-        return RenderFail(c, "failed to match binary version");
-
     if (!c.buffer.append("(module\n"))
         return false;
 
     c.indent++;
 
-    if (!RenderTypeSection(c))
+    if (!RenderTypeSection(c, module.sigs()))
         return false;
 
-    if (!RenderImportSection(c))
-        return false;
-
-    if (!RenderFunctionSection(c))
-        return false;
-
-    if (!RenderTableSection(c))
+    if (!RenderImportSection(c, module.imports(), module.sigs()))
         return false;
 
-    uint32_t memInitial, memMax;
-    if (!RenderMemorySection(c, &memInitial, &memMax))
+    if (!RenderTableSection(c, module.maybeTable(), module.funcs()))
         return false;
 
-    if (!RenderExportSection(c))
+    if (!RenderExportSection(c, module.exports(), module.funcs()))
         return false;
 
-    if (!RenderCodeSection(c))
+    if (!RenderCodeSection(c, module.funcs(), module.sigs()))
         return false;
 
-    if (!RenderDataSection(c, memInitial, memMax))
+    if (!RenderDataSection(c, module.maybeMemory()))
         return false;
 
     c.indent--;
 
     if (!c.buffer.append(")"))
         return false;
 
     return true;
 }
 
 /*****************************************************************************/
 // Top-level functions
 
 bool
 wasm::BinaryToText(JSContext* cx, const uint8_t* bytes, size_t length, StringBuffer& buffer)
 {
-    Decoder d(bytes, bytes + length);
+
+    LifoAlloc lifo(AST_LIFO_DEFAULT_CHUNK_SIZE);
 
-    WasmRenderContext c(cx, d, buffer);
-
-    if (!c.buffer.append("Binary-to-text is temporarily unavailable\n"))
+    AstModule* module;
+    if (!BinaryToAst(cx, bytes, length, lifo, &module))
         return false;
 
-    // FIXME: Implement binary-to-text and re-enable this.
-    if (0 && !RenderModule(c)) {
+    WasmRenderContext c(cx, module, buffer);
+
+    if (!RenderModule(c, *module)) {
         if (!cx->isExceptionPending())
             ReportOutOfMemory(cx);
         return false;
     }
 
     return true;
 }
 
--- a/js/src/asmjs/WasmTextToBinary.cpp
+++ b/js/src/asmjs/WasmTextToBinary.cpp
@@ -22,16 +22,17 @@
 #include "mozilla/MathAlgorithms.h"
 #include "mozilla/Maybe.h"
 
 #include "jsdtoa.h"
 #include "jsnum.h"
 #include "jsprf.h"
 #include "jsstr.h"
 
+#include "asmjs/WasmAST.h"
 #include "asmjs/WasmBinary.h"
 #include "asmjs/WasmTypes.h"
 #include "ds/LifoAlloc.h"
 #include "js/CharacterEncoding.h"
 #include "js/HashTable.h"
 
 using namespace js;
 using namespace js::wasm;
@@ -40,746 +41,16 @@ using mozilla::BitwiseCast;
 using mozilla::CeilingLog2;
 using mozilla::CountLeadingZeroes32;
 using mozilla::CheckedInt;
 using mozilla::FloatingPoint;
 using mozilla::Maybe;
 using mozilla::PositiveInfinity;
 using mozilla::SpecificNaN;
 
-static const unsigned AST_LIFO_DEFAULT_CHUNK_SIZE = 4096;
-static const uint32_t WasmNoIndex = UINT32_MAX;
-
-/*****************************************************************************/
-// wasm AST
-
-namespace {
-
-class WasmAstExpr;
-
-template <class T>
-using WasmAstVector = mozilla::Vector<T, 0, LifoAllocPolicy<Fallible>>;
-
-template <class K, class V, class HP>
-using WasmAstHashMap = HashMap<K, V, HP, LifoAllocPolicy<Fallible>>;
-
-class WasmName
-{
-    const char16_t* begin_;
-    const char16_t* end_;
-  public:
-    WasmName(const char16_t* begin, size_t length) : begin_(begin), end_(begin + length) {}
-    WasmName() : begin_(nullptr), end_(nullptr) {}
-    const char16_t* begin() const { return begin_; }
-    const char16_t* end() const { return end_; }
-    size_t length() const { return end_ - begin_; }
-    bool empty() const { return begin_ == nullptr; }
-
-    bool operator==(WasmName rhs) const {
-        if (length() != rhs.length())
-            return false;
-        if (begin() == rhs.begin())
-            return true;
-        return EqualChars(begin(), rhs.begin(), length());
-    }
-    bool operator!=(WasmName rhs) const {
-        return !(*this == rhs);
-    }
-};
-
-class WasmRef
-{
-    WasmName name_;
-    uint32_t index_;
-
-  public:
-    WasmRef()
-      : index_(WasmNoIndex)
-    {
-        MOZ_ASSERT(isInvalid());
-    }
-    WasmRef(WasmName name, uint32_t index)
-      : name_(name), index_(index)
-    {
-        MOZ_ASSERT(name.empty() ^ (index == WasmNoIndex));
-        MOZ_ASSERT(!isInvalid());
-    }
-    bool isInvalid() const {
-        return name_.empty() && index_ == WasmNoIndex;
-    }
-    WasmName name() const {
-        return name_;
-    }
-    size_t index() const {
-        MOZ_ASSERT(index_ != WasmNoIndex);
-        return index_;
-    }
-    void setIndex(uint32_t index) {
-        MOZ_ASSERT(index_ == WasmNoIndex);
-        index_ = index;
-    }
-};
-
-struct WasmNameHasher
-{
-    typedef const WasmName Lookup;
-    static js::HashNumber hash(Lookup l) {
-        return mozilla::HashString(l.begin(), l.length());
-    }
-    static bool match(const WasmName key, Lookup lookup) {
-        return key == lookup;
-    }
-};
-
-using WasmNameMap = WasmAstHashMap<WasmName, uint32_t, WasmNameHasher>;
-
-typedef WasmAstVector<ValType> WasmAstValTypeVector;
-typedef WasmAstVector<WasmAstExpr*> WasmAstExprVector;
-typedef WasmAstVector<WasmName> WasmNameVector;
-typedef WasmAstVector<WasmRef> WasmRefVector;
-
-struct WasmAstBase
-{
-    void* operator new(size_t numBytes, LifoAlloc& astLifo) throw() {
-        return astLifo.alloc(numBytes);
-    }
-};
-
-class WasmAstSig : public WasmAstBase
-{
-    WasmName name_;
-    WasmAstValTypeVector args_;
-    ExprType ret_;
-
-  public:
-    explicit WasmAstSig(LifoAlloc& lifo)
-      : args_(lifo),
-        ret_(ExprType::Void)
-    {}
-    WasmAstSig(WasmAstValTypeVector&& args, ExprType ret)
-      : args_(Move(args)),
-        ret_(ret)
-    {}
-    WasmAstSig(WasmName name, WasmAstSig&& rhs)
-      : name_(name),
-        args_(Move(rhs.args_)),
-        ret_(rhs.ret_)
-    {}
-    void operator=(WasmAstSig&& rhs) {
-        args_ = Move(rhs.args_);
-        ret_ = rhs.ret_;
-    }
-    const WasmAstValTypeVector& args() const {
-        return args_;
-    }
-    ExprType ret() const {
-        return ret_;
-    }
-    WasmName name() const {
-        return name_;
-    }
-    bool operator==(const WasmAstSig& rhs) const {
-        return ret() == rhs.ret() && EqualContainers(args(), rhs.args());
-    }
-
-    typedef const WasmAstSig& Lookup;
-    static HashNumber hash(Lookup sig) {
-        return AddContainerToHash(sig.args(), HashNumber(sig.ret()));
-    }
-    static bool match(const WasmAstSig* lhs, Lookup rhs) {
-        return *lhs == rhs;
-    }
-};
-
-class WasmAstNode : public WasmAstBase
-{};
-
-enum class WasmAstExprKind
-{
-    BinaryOperator,
-    Block,
-    Branch,
-    BranchTable,
-    Call,
-    CallIndirect,
-    ComparisonOperator,
-    Const,
-    ConversionOperator,
-    GetLocal,
-    If,
-    Load,
-    Nop,
-    Return,
-    SetLocal,
-    Store,
-    TernaryOperator,
-    UnaryOperator,
-    Unreachable
-};
-
-class WasmAstExpr : public WasmAstNode
-{
-    const WasmAstExprKind kind_;
-
-  protected:
-    explicit WasmAstExpr(WasmAstExprKind kind)
-      : kind_(kind)
-    {}
-
-  public:
-    WasmAstExprKind kind() const { return kind_; }
-
-    template <class T>
-    T& as() {
-        MOZ_ASSERT(kind() == T::Kind);
-        return static_cast<T&>(*this);
-    }
-};
-
-struct WasmAstNop : WasmAstExpr
-{
-    WasmAstNop()
-      : WasmAstExpr(WasmAstExprKind::Nop)
-    {}
-};
-
-struct WasmAstUnreachable : WasmAstExpr
-{
-    WasmAstUnreachable()
-      : WasmAstExpr(WasmAstExprKind::Unreachable)
-    {}
-};
-
-class WasmAstConst : public WasmAstExpr
-{
-    const Val val_;
-
-  public:
-    static const WasmAstExprKind Kind = WasmAstExprKind::Const;
-    explicit WasmAstConst(Val val)
-      : WasmAstExpr(Kind),
-        val_(val)
-    {}
-    Val val() const { return val_; }
-};
-
-class WasmAstGetLocal : public WasmAstExpr
-{
-    WasmRef local_;
-
-  public:
-    static const WasmAstExprKind Kind = WasmAstExprKind::GetLocal;
-    explicit WasmAstGetLocal(WasmRef local)
-      : WasmAstExpr(Kind),
-        local_(local)
-    {}
-    WasmRef& local() {
-        return local_;
-    }
-};
-
-class WasmAstSetLocal : public WasmAstExpr
-{
-    WasmRef local_;
-    WasmAstExpr& value_;
-
-  public:
-    static const WasmAstExprKind Kind = WasmAstExprKind::SetLocal;
-    WasmAstSetLocal(WasmRef local, WasmAstExpr& value)
-      : WasmAstExpr(Kind),
-        local_(local),
-        value_(value)
-    {}
-    WasmRef& local() {
-        return local_;
-    }
-    WasmAstExpr& value() const {
-        return value_;
-    }
-};
-
-class WasmAstBlock : public WasmAstExpr
-{
-    Expr expr_;
-    WasmName breakName_;
-    WasmName continueName_;
-    WasmAstExprVector exprs_;
-
-  public:
-    static const WasmAstExprKind Kind = WasmAstExprKind::Block;
-    explicit WasmAstBlock(Expr expr, WasmName breakName,
-                          WasmName continueName, WasmAstExprVector&& exprs)
-      : WasmAstExpr(Kind),
-        expr_(expr),
-        breakName_(breakName),
-        continueName_(continueName),
-        exprs_(Move(exprs))
-    {}
-
-    Expr expr() const { return expr_; }
-    WasmName breakName() const { return breakName_; }
-    WasmName continueName() const { return continueName_; }
-    const WasmAstExprVector& exprs() const { return exprs_; }
-};
-
-class WasmAstBranch : public WasmAstExpr
-{
-    Expr expr_;
-    WasmAstExpr* cond_;
-    WasmRef target_;
-    WasmAstExpr* value_;
-
-  public:
-    static const WasmAstExprKind Kind = WasmAstExprKind::Branch;
-    explicit WasmAstBranch(Expr expr, WasmAstExpr* cond, WasmRef target, WasmAstExpr* value)
-      : WasmAstExpr(Kind),
-        expr_(expr),
-        cond_(cond),
-        target_(target),
-        value_(value)
-    {}
-
-    Expr expr() const { return expr_; }
-    WasmRef& target() { return target_; }
-    WasmAstExpr& cond() const { MOZ_ASSERT(cond_); return *cond_; }
-    WasmAstExpr* maybeValue() const { return value_; }
-};
-
-class WasmAstCall : public WasmAstExpr
-{
-    Expr expr_;
-    WasmRef func_;
-    WasmAstExprVector args_;
-
-  public:
-    static const WasmAstExprKind Kind = WasmAstExprKind::Call;
-    WasmAstCall(Expr expr, WasmRef func, WasmAstExprVector&& args)
-      : WasmAstExpr(Kind), expr_(expr), func_(func), args_(Move(args))
-    {}
-
-    Expr expr() const { return expr_; }
-    WasmRef& func() { return func_; }
-    const WasmAstExprVector& args() const { return args_; }
-};
-
-class WasmAstCallIndirect : public WasmAstExpr
-{
-    WasmRef sig_;
-    WasmAstExpr* index_;
-    WasmAstExprVector args_;
-
-  public:
-    static const WasmAstExprKind Kind = WasmAstExprKind::CallIndirect;
-    WasmAstCallIndirect(WasmRef sig, WasmAstExpr* index, WasmAstExprVector&& args)
-      : WasmAstExpr(Kind), sig_(sig), index_(index), args_(Move(args))
-    {}
-    WasmRef& sig() { return sig_; }
-    WasmAstExpr* index() const { return index_; }
-    const WasmAstExprVector& args() const { return args_; }
-};
-
-class WasmAstReturn : public WasmAstExpr
-{
-    WasmAstExpr* maybeExpr_;
-
-  public:
-    static const WasmAstExprKind Kind = WasmAstExprKind::Return;
-    explicit WasmAstReturn(WasmAstExpr* maybeExpr)
-      : WasmAstExpr(Kind),
-        maybeExpr_(maybeExpr)
-    {}
-    WasmAstExpr* maybeExpr() const { return maybeExpr_; }
-};
-
-class WasmAstIf : public WasmAstExpr
-{
-    WasmAstExpr* cond_;
-    WasmAstExpr* thenBranch_;
-    WasmAstExpr* elseBranch_;
-
-  public:
-    static const WasmAstExprKind Kind = WasmAstExprKind::If;
-    WasmAstIf(WasmAstExpr* cond, WasmAstExpr* thenBranch, WasmAstExpr* elseBranch)
-      : WasmAstExpr(Kind),
-        cond_(cond),
-        thenBranch_(thenBranch),
-        elseBranch_(elseBranch)
-    {}
-
-    WasmAstExpr& cond() const { return *cond_; }
-    WasmAstExpr& thenBranch() const { return *thenBranch_; }
-    bool hasElse() const { return !!elseBranch_; }
-    WasmAstExpr& elseBranch() const { MOZ_ASSERT(hasElse()); return *elseBranch_; }
-};
-
-class WasmAstLoadStoreAddress
-{
-    WasmAstExpr* base_;
-    int32_t flags_;
-    int32_t offset_;
-
-  public:
-    explicit WasmAstLoadStoreAddress(WasmAstExpr* base, int32_t flags, int32_t offset)
-      : base_(base),
-        flags_(flags),
-        offset_(offset)
-    {}
-
-    WasmAstExpr& base() const { return *base_; }
-    int32_t flags() const { return flags_; }
-    int32_t offset() const { return offset_; }
-};
-
-class WasmAstLoad : public WasmAstExpr
-{
-    Expr expr_;
-    WasmAstLoadStoreAddress address_;
-
-  public:
-    static const WasmAstExprKind Kind = WasmAstExprKind::Load;
-    explicit WasmAstLoad(Expr expr, const WasmAstLoadStoreAddress &address)
-      : WasmAstExpr(Kind),
-        expr_(expr),
-        address_(address)
-    {}
-
-    Expr expr() const { return expr_; }
-    const WasmAstLoadStoreAddress& address() const { return address_; }
-};
-
-class WasmAstStore : public WasmAstExpr
-{
-    Expr expr_;
-    WasmAstLoadStoreAddress address_;
-    WasmAstExpr* value_;
-
-  public:
-    static const WasmAstExprKind Kind = WasmAstExprKind::Store;
-    explicit WasmAstStore(Expr expr, const WasmAstLoadStoreAddress &address,
-                          WasmAstExpr* value)
-      : WasmAstExpr(Kind),
-        expr_(expr),
-        address_(address),
-        value_(value)
-    {}
-
-    Expr expr() const { return expr_; }
-    const WasmAstLoadStoreAddress& address() const { return address_; }
-    WasmAstExpr& value() const { return *value_; }
-};
-
-class WasmAstBranchTable : public WasmAstExpr
-{
-    WasmAstExpr& index_;
-    WasmRef default_;
-    WasmRefVector table_;
-    WasmAstExpr* value_;
-
-  public:
-    static const WasmAstExprKind Kind = WasmAstExprKind::BranchTable;
-    explicit WasmAstBranchTable(WasmAstExpr& index, WasmRef def, WasmRefVector&& table,
-                                WasmAstExpr* maybeValue)
-      : WasmAstExpr(Kind),
-        index_(index),
-        default_(def),
-        table_(Move(table)),
-        value_(maybeValue)
-    {}
-    WasmAstExpr& index() const { return index_; }
-    WasmRef& def() { return default_; }
-    WasmRefVector& table() { return table_; }
-    WasmAstExpr* maybeValue() { return value_; }
-};
-
-class WasmAstFunc : public WasmAstNode
-{
-    WasmName name_;
-    WasmRef sig_;
-    WasmAstValTypeVector vars_;
-    WasmNameVector localNames_;
-    WasmAstExprVector body_;
-
-  public:
-    WasmAstFunc(WasmName name, WasmRef sig, WasmAstValTypeVector&& vars,
-                WasmNameVector&& locals, WasmAstExprVector&& body)
-      : name_(name),
-        sig_(sig),
-        vars_(Move(vars)),
-        localNames_(Move(locals)),
-        body_(Move(body))
-    {}
-    WasmRef& sig() { return sig_; }
-    const WasmAstValTypeVector& vars() const { return vars_; }
-    const WasmNameVector& locals() const { return localNames_; }
-    const WasmAstExprVector& body() const { return body_; }
-    WasmName name() const { return name_; }
-};
-
-class WasmAstImport : public WasmAstNode
-{
-    WasmName name_;
-    WasmName module_;
-    WasmName func_;
-    uint32_t sigIndex_;
-
-  public:
-    WasmAstImport(WasmName name, WasmName module, WasmName func, uint32_t sigIndex)
-      : name_(name), module_(module), func_(func), sigIndex_(sigIndex)
-    {}
-    WasmName name() const { return name_; }
-    WasmName module() const { return module_; }
-    WasmName func() const { return func_; }
-    uint32_t sigIndex() const { return sigIndex_; }
-};
-
-enum class WasmAstExportKind { Func, Memory };
-
-class WasmAstExport : public WasmAstNode
-{
-    WasmName name_;
-    WasmAstExportKind kind_;
-    WasmRef func_;
-
-  public:
-    WasmAstExport(WasmName name, WasmRef func)
-      : name_(name), kind_(WasmAstExportKind::Func), func_(func)
-    {}
-    explicit WasmAstExport(WasmName name)
-      : name_(name), kind_(WasmAstExportKind::Memory)
-    {}
-    WasmName name() const { return name_; }
-    WasmAstExportKind kind() const { return kind_; }
-    WasmRef& func() { return func_; }
-};
-
-typedef WasmAstVector<WasmRef> WasmAstTableElemVector;
-
-class WasmAstTable : public WasmAstNode
-{
-    WasmAstTableElemVector elems_;
-
-  public:
-    explicit WasmAstTable(WasmAstTableElemVector&& elems) : elems_(Move(elems)) {}
-    WasmAstTableElemVector& elems() { return elems_; }
-};
-
-class WasmAstSegment : public WasmAstNode
-{
-    uint32_t offset_;
-    WasmName text_;
-
-  public:
-    WasmAstSegment(uint32_t offset, WasmName text)
-      : offset_(offset), text_(text)
-    {}
-    uint32_t offset() const { return offset_; }
-    WasmName text() const { return text_; }
-};
-
-typedef WasmAstVector<WasmAstSegment*> WasmAstSegmentVector;
-
-class WasmAstMemory : public WasmAstNode
-{
-    uint32_t initialSize_;
-    Maybe<uint32_t> maxSize_;
-    WasmAstSegmentVector segments_;
-
-  public:
-    explicit WasmAstMemory(uint32_t initialSize, Maybe<uint32_t> maxSize,
-                           WasmAstSegmentVector&& segments)
-      : initialSize_(initialSize),
-        maxSize_(maxSize),
-        segments_(Move(segments))
-    {}
-    uint32_t initialSize() const { return initialSize_; }
-    const Maybe<uint32_t>& maxSize() const { return maxSize_; }
-    const WasmAstSegmentVector& segments() const { return segments_; }
-};
-
-class WasmAstModule : public WasmAstNode
-{
-    typedef WasmAstVector<WasmAstFunc*> FuncVector;
-    typedef WasmAstVector<WasmAstImport*> ImportVector;
-    typedef WasmAstVector<WasmAstExport*> ExportVector;
-    typedef WasmAstVector<WasmAstSig*> SigVector;
-    typedef WasmAstHashMap<WasmAstSig*, uint32_t, WasmAstSig> SigMap;
-
-    LifoAlloc& lifo_;
-    WasmAstMemory* memory_;
-    SigVector sigs_;
-    SigMap sigMap_;
-    ImportVector imports_;
-    ExportVector exports_;
-    WasmAstTable* table_;
-    FuncVector funcs_;
-
-  public:
-    explicit WasmAstModule(LifoAlloc& lifo)
-      : lifo_(lifo),
-        memory_(nullptr),
-        sigs_(lifo),
-        sigMap_(lifo),
-        imports_(lifo),
-        exports_(lifo),
-        table_(nullptr),
-        funcs_(lifo)
-    {}
-    bool init() {
-        return sigMap_.init();
-    }
-    bool setMemory(WasmAstMemory* memory) {
-        if (memory_)
-            return false;
-        memory_ = memory;
-        return true;
-    }
-    WasmAstMemory* maybeMemory() const {
-        return memory_;
-    }
-    bool declare(WasmAstSig&& sig, uint32_t* sigIndex) {
-        SigMap::AddPtr p = sigMap_.lookupForAdd(sig);
-        if (p) {
-            *sigIndex = p->value();
-            return true;
-        }
-        *sigIndex = sigs_.length();
-        auto* lifoSig = new (lifo_) WasmAstSig(WasmName(), Move(sig));
-        return lifoSig &&
-               sigs_.append(lifoSig) &&
-               sigMap_.add(p, sigs_.back(), *sigIndex);
-    }
-    bool append(WasmAstSig* sig) {
-        uint32_t sigIndex = sigs_.length();
-        if (!sigs_.append(sig))
-            return false;
-        SigMap::AddPtr p = sigMap_.lookupForAdd(*sig);
-        return p || sigMap_.add(p, sig, sigIndex);
-    }
-    const SigVector& sigs() const {
-        return sigs_;
-    }
-    bool append(WasmAstFunc* func) {
-        return funcs_.append(func);
-    }
-    const FuncVector& funcs() const {
-        return funcs_;
-    }
-    const ImportVector& imports() const {
-        return imports_;
-    }
-    bool append(WasmAstImport* imp) {
-        return imports_.append(imp);
-    }
-    bool append(WasmAstExport* exp) {
-        return exports_.append(exp);
-    }
-    const ExportVector& exports() const {
-        return exports_;
-    }
-    bool initTable(WasmAstTable* table) {
-        if (table_)
-            return false;
-        table_ = table;
-        return true;
-    }
-    WasmAstTable* maybeTable() const {
-        return table_;
-    }
-};
-
-class WasmAstUnaryOperator final : public WasmAstExpr
-{
-    Expr expr_;
-    WasmAstExpr* op_;
-
-  public:
-    static const WasmAstExprKind Kind = WasmAstExprKind::UnaryOperator;
-    explicit WasmAstUnaryOperator(Expr expr, WasmAstExpr* op)
-      : WasmAstExpr(Kind),
-        expr_(expr), op_(op)
-    {}
-
-    Expr expr() const { return expr_; }
-    WasmAstExpr* op() const { return op_; }
-};
-
-class WasmAstBinaryOperator final : public WasmAstExpr
-{
-    Expr expr_;
-    WasmAstExpr* lhs_;
-    WasmAstExpr* rhs_;
-
-  public:
-    static const WasmAstExprKind Kind = WasmAstExprKind::BinaryOperator;
-    explicit WasmAstBinaryOperator(Expr expr, WasmAstExpr* lhs, WasmAstExpr* rhs)
-      : WasmAstExpr(Kind),
-        expr_(expr), lhs_(lhs), rhs_(rhs)
-    {}
-
-    Expr expr() const { return expr_; }
-    WasmAstExpr* lhs() const { return lhs_; }
-    WasmAstExpr* rhs() const { return rhs_; }
-};
-
-class WasmAstTernaryOperator : public WasmAstExpr
-{
-    Expr expr_;
-    WasmAstExpr* op0_;
-    WasmAstExpr* op1_;
-    WasmAstExpr* op2_;
-
-  public:
-    static const WasmAstExprKind Kind = WasmAstExprKind::TernaryOperator;
-    WasmAstTernaryOperator(Expr expr, WasmAstExpr* op0, WasmAstExpr* op1, WasmAstExpr* op2)
-      : WasmAstExpr(Kind),
-        expr_(expr), op0_(op0), op1_(op1), op2_(op2)
-    {}
-
-    Expr expr() const { return expr_; }
-    WasmAstExpr* op0() const { return op0_; }
-    WasmAstExpr* op1() const { return op1_; }
-    WasmAstExpr* op2() const { return op2_; }
-};
-
-class WasmAstComparisonOperator final : public WasmAstExpr
-{
-    Expr expr_;
-    WasmAstExpr* lhs_;
-    WasmAstExpr* rhs_;
-
-  public:
-    static const WasmAstExprKind Kind = WasmAstExprKind::ComparisonOperator;
-    explicit WasmAstComparisonOperator(Expr expr, WasmAstExpr* lhs, WasmAstExpr* rhs)
-      : WasmAstExpr(Kind),
-        expr_(expr), lhs_(lhs), rhs_(rhs)
-    {}
-
-    Expr expr() const { return expr_; }
-    WasmAstExpr* lhs() const { return lhs_; }
-    WasmAstExpr* rhs() const { return rhs_; }
-};
-
-class WasmAstConversionOperator final : public WasmAstExpr
-{
-    Expr expr_;
-    WasmAstExpr* op_;
-
-  public:
-    static const WasmAstExprKind Kind = WasmAstExprKind::ConversionOperator;
-    explicit WasmAstConversionOperator(Expr expr, WasmAstExpr* op)
-      : WasmAstExpr(Kind),
-        expr_(expr), op_(op)
-    {}
-
-    Expr expr() const { return expr_; }
-    WasmAstExpr* op() const { return op_; }
-};
-
-} // end anonymous namespace
-
 /*****************************************************************************/
 // wasm text token stream
 
 namespace {
 
 class WasmToken
 {
   public:
@@ -926,25 +197,25 @@ class WasmToken
         return kind_;
     }
     const char16_t* begin() const {
         return begin_;
     }
     const char16_t* end() const {
         return end_;
     }
-    WasmName text() const {
+    AstName text() const {
         MOZ_ASSERT(kind_ == Text);
         MOZ_ASSERT(begin_[0] == '"');
         MOZ_ASSERT(end_[-1] == '"');
         MOZ_ASSERT(end_ - begin_ >= 2);
-        return WasmName(begin_ + 1, end_ - begin_ - 2);
+        return AstName(begin_ + 1, end_ - begin_ - 2);
     }
-    WasmName name() const {
-        return WasmName(begin_, end_ - begin_);
+    AstName name() const {
+        return AstName(begin_, end_ - begin_);
     }
     uint32_t index() const {
         MOZ_ASSERT(kind_ == Index);
         return u.index_;
     }
     uint64_t uint() const {
         MOZ_ASSERT(kind_ == UnsignedInteger);
         return u.uint_;
@@ -1224,47 +495,47 @@ class WasmTokenStream
         return false;
     }
     bool getIf(WasmToken::Kind kind) {
         WasmToken token;
         if (getIf(kind, &token))
             return true;
         return false;
     }
-    WasmName getIfName() {
+    AstName getIfName() {
         WasmToken token;
         if (getIf(WasmToken::Name, &token))
             return token.name();
-        return WasmName();
+        return AstName();
     }
-    bool getIfRef(WasmRef* ref) {
+    bool getIfRef(AstRef* ref) {
         WasmToken token = peek();
         if (token.kind() == WasmToken::Name || token.kind() == WasmToken::Index)
             return matchRef(ref, nullptr);
         return false;
     }
     bool match(WasmToken::Kind expect, WasmToken* token, UniqueChars* error) {
         *token = get();
         if (token->kind() == expect)
             return true;
         generateError(*token, error);
         return false;
     }
     bool match(WasmToken::Kind expect, UniqueChars* error) {
         WasmToken token;
         return match(expect, &token, error);
     }
-    bool matchRef(WasmRef* ref, UniqueChars* error) {
+    bool matchRef(AstRef* ref, UniqueChars* error) {
         WasmToken token = get();
         switch (token.kind()) {
           case WasmToken::Name:
-            *ref = WasmRef(token.name(), WasmNoIndex);
+            *ref = AstRef(token.name(), AstNoIndex);
             break;
           case WasmToken::Index:
-            *ref = WasmRef(WasmName(), token.index());
+            *ref = AstRef(AstName(), token.index());
             break;
           default:
             generateError(token, error);
             return false;
         }
         return true;
     }
 };
@@ -2075,138 +1346,138 @@ struct WasmParseContext
     }
     ~WasmParseContext() {
         DestroyDtoaState(dtoaState);
     }
 };
 
 } // end anonymous namespace
 
-static WasmAstExpr*
+static AstExpr*
 ParseExprInsideParens(WasmParseContext& c);
 
-static WasmAstExpr*
+static AstExpr*
 ParseExpr(WasmParseContext& c)
 {
     if (!c.ts.match(WasmToken::OpenParen, c.error))
         return nullptr;
 
-    WasmAstExpr* expr = ParseExprInsideParens(c);
+    AstExpr* expr = ParseExprInsideParens(c);
     if (!expr)
         return nullptr;
 
     if (!c.ts.match(WasmToken::CloseParen, c.error))
         return nullptr;
 
     return expr;
 }
 
-static WasmAstBlock*
+static AstBlock*
 ParseBlock(WasmParseContext& c, Expr expr)
 {
-    WasmAstExprVector exprs(c.lifo);
-
-    WasmName breakName = c.ts.getIfName();
-
-    WasmName continueName;
+    AstExprVector exprs(c.lifo);
+
+    AstName breakName = c.ts.getIfName();
+
+    AstName continueName;
     if (expr == Expr::Loop)
         continueName = c.ts.getIfName();
 
     while (c.ts.getIf(WasmToken::OpenParen)) {
-        WasmAstExpr* expr = ParseExprInsideParens(c);
+        AstExpr* expr = ParseExprInsideParens(c);
         if (!expr || !exprs.append(expr))
             return nullptr;
         if (!c.ts.match(WasmToken::CloseParen, c.error))
             return nullptr;
     }
 
-    return new(c.lifo) WasmAstBlock(expr, breakName, continueName, Move(exprs));
+    return new(c.lifo) AstBlock(expr, breakName, continueName, Move(exprs));
 }
 
-static WasmAstBranch*
+static AstBranch*
 ParseBranch(WasmParseContext& c, Expr expr)
 {
     MOZ_ASSERT(expr == Expr::Br || expr == Expr::BrIf);
 
-    WasmRef target;
+    AstRef target;
     if (!c.ts.matchRef(&target, c.error))
         return nullptr;
 
-    WasmAstExpr* value = nullptr;
+    AstExpr* value = nullptr;
     if (c.ts.getIf(WasmToken::OpenParen)) {
         value = ParseExprInsideParens(c);
         if (!value)
             return nullptr;
         if (!c.ts.match(WasmToken::CloseParen, c.error))
             return nullptr;
     }
 
-    WasmAstExpr* cond = nullptr;
+    AstExpr* cond = nullptr;
     if (expr == Expr::BrIf) {
         if (c.ts.getIf(WasmToken::OpenParen)) {
             cond = ParseExprInsideParens(c);
             if (!cond)
                 return nullptr;
             if (!c.ts.match(WasmToken::CloseParen, c.error))
                 return nullptr;
         } else {
             cond = value;
             value = nullptr;
         }
     }
 
-    return new(c.lifo) WasmAstBranch(expr, cond, target, value);
+    return new(c.lifo) AstBranch(expr, cond, target, value);
 }
 
 static bool
-ParseArgs(WasmParseContext& c, WasmAstExprVector* args)
+ParseArgs(WasmParseContext& c, AstExprVector* args)
 {
     while (c.ts.getIf(WasmToken::OpenParen)) {
-        WasmAstExpr* arg = ParseExprInsideParens(c);
+        AstExpr* arg = ParseExprInsideParens(c);
         if (!arg || !args->append(arg))
             return false;
         if (!c.ts.match(WasmToken::CloseParen, c.error))
             return false;
     }
 
     return true;
 }
 
-static WasmAstCall*
+static AstCall*
 ParseCall(WasmParseContext& c, Expr expr)
 {
     MOZ_ASSERT(expr == Expr::Call || expr == Expr::CallImport);
 
-    WasmRef func;
+    AstRef func;
     if (!c.ts.matchRef(&func, c.error))
         return nullptr;
 
-    WasmAstExprVector args(c.lifo);
+    AstExprVector args(c.lifo);
     if (!ParseArgs(c, &args))
         return nullptr;
 
-    return new(c.lifo) WasmAstCall(expr, func, Move(args));
+    return new(c.lifo) AstCall(expr, func, Move(args));
 }
 
-static WasmAstCallIndirect*
+static AstCallIndirect*
 ParseCallIndirect(WasmParseContext& c)
 {
-    WasmRef sig;
+    AstRef sig;
     if (!c.ts.matchRef(&sig, c.error))
         return nullptr;
 
-    WasmAstExpr* index = ParseExpr(c);
+    AstExpr* index = ParseExpr(c);
     if (!index)
         return nullptr;
 
-    WasmAstExprVector args(c.lifo);
+    AstExprVector args(c.lifo);
     if (!ParseArgs(c, &args))
         return nullptr;
 
-    return new(c.lifo) WasmAstCallIndirect(sig, index, Move(args));
+    return new(c.lifo) AstCallIndirect(sig, index, Move(args));
 }
 
 static uint_fast8_t
 CountLeadingZeroes4(uint8_t x)
 {
     MOZ_ASSERT((x & -0x10) == 0);
     return CountLeadingZeroes32(x) - 28;
 }
@@ -2465,201 +1736,201 @@ ParseFloatLiteral(WasmParseContext& c, W
     }
 
     if (isNegated)
         *result = -*result;
 
     return true;
 }
 
-static WasmAstConst*
+static AstConst*
 ParseConst(WasmParseContext& c, WasmToken constToken)
 {
     WasmToken val = c.ts.get();
     switch (constToken.valueType()) {
       case ValType::I32: {
         switch (val.kind()) {
           case WasmToken::Index:
-            return new(c.lifo) WasmAstConst(Val(val.index()));
+            return new(c.lifo) AstConst(Val(val.index()));
           case WasmToken::SignedInteger: {
             CheckedInt<int32_t> sint = val.sint();
             if (!sint.isValid())
                 break;
-            return new(c.lifo) WasmAstConst(Val(uint32_t(sint.value())));
+            return new(c.lifo) AstConst(Val(uint32_t(sint.value())));
           }
           case WasmToken::NegativeZero:
-            return new(c.lifo) WasmAstConst(Val(uint32_t(0)));
+            return new(c.lifo) AstConst(Val(uint32_t(0)));
           default:
             break;
         }
         break;
       }
       case ValType::I64: {
         switch (val.kind()) {
           case WasmToken::Index:
-            return new(c.lifo) WasmAstConst(Val(uint64_t(val.index())));
+            return new(c.lifo) AstConst(Val(uint64_t(val.index())));
           case WasmToken::UnsignedInteger:
-            return new(c.lifo) WasmAstConst(Val(val.uint()));
+            return new(c.lifo) AstConst(Val(val.uint()));
           case WasmToken::SignedInteger:
-            return new(c.lifo) WasmAstConst(Val(uint64_t(val.sint())));
+            return new(c.lifo) AstConst(Val(uint64_t(val.sint())));
           case WasmToken::NegativeZero:
-            return new(c.lifo) WasmAstConst(Val(uint64_t(0)));
+            return new(c.lifo) AstConst(Val(uint64_t(0)));
           default:
             break;
         }
         break;
       }
       case ValType::F32: {
         float result;
         if (!ParseFloatLiteral(c, val, &result))
             break;
-        return new(c.lifo) WasmAstConst(Val(result));
+        return new(c.lifo) AstConst(Val(result));
       }
       case ValType::F64: {
         double result;
         if (!ParseFloatLiteral(c, val, &result))
             break;
-        return new(c.lifo) WasmAstConst(Val(result));
+        return new(c.lifo) AstConst(Val(result));
       }
       default:
         break;
     }
     c.ts.generateError(constToken, c.error);
     return nullptr;
 }
 
-static WasmAstGetLocal*
+static AstGetLocal*
 ParseGetLocal(WasmParseContext& c)
 {
-    WasmRef local;
+    AstRef local;
     if (!c.ts.matchRef(&local, c.error))
         return nullptr;
 
-    return new(c.lifo) WasmAstGetLocal(local);
+    return new(c.lifo) AstGetLocal(local);
 }
 
-static WasmAstSetLocal*
+static AstSetLocal*
 ParseSetLocal(WasmParseContext& c)
 {
-    WasmRef local;
+    AstRef local;
     if (!c.ts.matchRef(&local, c.error))
         return nullptr;
 
-    WasmAstExpr* value = ParseExpr(c);
+    AstExpr* value = ParseExpr(c);
     if (!value)
         return nullptr;
 
-    return new(c.lifo) WasmAstSetLocal(local, *value);
+    return new(c.lifo) AstSetLocal(local, *value);
 }
 
-static WasmAstReturn*
+static AstReturn*
 ParseReturn(WasmParseContext& c)
 {
-    WasmAstExpr* maybeExpr = nullptr;
+    AstExpr* maybeExpr = nullptr;
 
     if (c.ts.peek().kind() != WasmToken::CloseParen) {
         maybeExpr = ParseExpr(c);
         if (!maybeExpr)
             return nullptr;
     }
 
-    return new(c.lifo) WasmAstReturn(maybeExpr);
+    return new(c.lifo) AstReturn(maybeExpr);
 }
 
-static WasmAstUnaryOperator*
+static AstUnaryOperator*
 ParseUnaryOperator(WasmParseContext& c, Expr expr)
 {
-    WasmAstExpr* op = ParseExpr(c);
+    AstExpr* op = ParseExpr(c);
     if (!op)
         return nullptr;
 
-    return new(c.lifo) WasmAstUnaryOperator(expr, op);
+    return new(c.lifo) AstUnaryOperator(expr, op);
 }
 
-static WasmAstBinaryOperator*
+static AstBinaryOperator*
 ParseBinaryOperator(WasmParseContext& c, Expr expr)
 {
-    WasmAstExpr* lhs = ParseExpr(c);
+    AstExpr* lhs = ParseExpr(c);
     if (!lhs)
         return nullptr;
 
-    WasmAstExpr* rhs = ParseExpr(c);
+    AstExpr* rhs = ParseExpr(c);
     if (!rhs)
         return nullptr;
 
-    return new(c.lifo) WasmAstBinaryOperator(expr, lhs, rhs);
+    return new(c.lifo) AstBinaryOperator(expr, lhs, rhs);
 }
 
-static WasmAstComparisonOperator*
+static AstComparisonOperator*
 ParseComparisonOperator(WasmParseContext& c, Expr expr)
 {
-    WasmAstExpr* lhs = ParseExpr(c);
+    AstExpr* lhs = ParseExpr(c);
     if (!lhs)
         return nullptr;
 
-    WasmAstExpr* rhs = ParseExpr(c);
+    AstExpr* rhs = ParseExpr(c);
     if (!rhs)
         return nullptr;
 
-    return new(c.lifo) WasmAstComparisonOperator(expr, lhs, rhs);
+    return new(c.lifo) AstComparisonOperator(expr, lhs, rhs);
 }
 
-static WasmAstTernaryOperator*
+static AstTernaryOperator*
 ParseTernaryOperator(WasmParseContext& c, Expr expr)
 {
-    WasmAstExpr* op0 = ParseExpr(c);
+    AstExpr* op0 = ParseExpr(c);
     if (!op0)
         return nullptr;
 
-    WasmAstExpr* op1 = ParseExpr(c);
+    AstExpr* op1 = ParseExpr(c);
     if (!op1)
         return nullptr;
 
-    WasmAstExpr* op2 = ParseExpr(c);
+    AstExpr* op2 = ParseExpr(c);
     if (!op2)
         return nullptr;
 
-    return new(c.lifo) WasmAstTernaryOperator(expr, op0, op1, op2);
+    return new(c.lifo) AstTernaryOperator(expr, op0, op1, op2);
 }
 
-static WasmAstConversionOperator*
+static AstConversionOperator*
 ParseConversionOperator(WasmParseContext& c, Expr expr)
 {
-    WasmAstExpr* op = ParseExpr(c);
+    AstExpr* op = ParseExpr(c);
     if (!op)
         return nullptr;
 
-    return new(c.lifo) WasmAstConversionOperator(expr, op);
+    return new(c.lifo) AstConversionOperator(expr, op);
 }
 
-static WasmAstIf*
+static AstIf*
 ParseIf(WasmParseContext& c)
 {
-    WasmAstExpr* cond = ParseExpr(c);
+    AstExpr* cond = ParseExpr(c);
     if (!cond)
         return nullptr;
 
-    WasmAstExpr* thenBranch = ParseExpr(c);
+    AstExpr* thenBranch = ParseExpr(c);
     if (!thenBranch)
         return nullptr;
 
-    WasmAstExpr* elseBranch = nullptr;
+    AstExpr* elseBranch = nullptr;
     if (c.ts.getIf(WasmToken::OpenParen)) {
         elseBranch = ParseExprInsideParens(c);
         if (!elseBranch)
             return nullptr;
         if (!c.ts.match(WasmToken::CloseParen, c.error))
             return nullptr;
     }
 
-    return new(c.lifo) WasmAstIf(cond, thenBranch, elseBranch);
+    return new(c.lifo) AstIf(cond, thenBranch, elseBranch);
 }
 
 static bool
-ParseLoadStoreAddress(WasmParseContext& c, int32_t* offset, uint32_t* alignLog2, WasmAstExpr** base)
+ParseLoadStoreAddress(WasmParseContext& c, int32_t* offset, uint32_t* alignLog2, AstExpr** base)
 {
     *offset = 0;
     if (c.ts.getIf(WasmToken::Offset)) {
         if (!c.ts.match(WasmToken::Equal, c.error))
             return false;
         WasmToken val = c.ts.get();
         switch (val.kind()) {
           case WasmToken::Index:
@@ -2692,22 +1963,22 @@ ParseLoadStoreAddress(WasmParseContext& 
 
     *base = ParseExpr(c);
     if (!*base)
         return false;
 
     return true;
 }
 
-static WasmAstLoad*
+static AstLoad*
 ParseLoad(WasmParseContext& c, Expr expr)
 {
     int32_t offset;
     uint32_t alignLog2;
-    WasmAstExpr* base;
+    AstExpr* base;
     if (!ParseLoadStoreAddress(c, &offset, &alignLog2, &base))
         return nullptr;
 
     if (alignLog2 == UINT32_MAX) {
         switch (expr) {
           case Expr::I32Load8S:
           case Expr::I32Load8U:
           case Expr::I64Load8S:
@@ -2732,25 +2003,25 @@ ParseLoad(WasmParseContext& c, Expr expr
             break;
           default:
             MOZ_CRASH("Bad load expr");
         }
     }
 
     uint32_t flags = alignLog2;
 
-    return new(c.lifo) WasmAstLoad(expr, WasmAstLoadStoreAddress(base, flags, offset));
+    return new(c.lifo) AstLoad(expr, AstLoadStoreAddress(base, flags, offset));
 }
 
-static WasmAstStore*
+static AstStore*
 ParseStore(WasmParseContext& c, Expr expr)
 {
     int32_t offset;
     uint32_t alignLog2;
-    WasmAstExpr* base;
+    AstExpr* base;
     if (!ParseLoadStoreAddress(c, &offset, &alignLog2, &base))
         return nullptr;
 
     if (alignLog2 == UINT32_MAX) {
         switch (expr) {
           case Expr::I32Store8:
           case Expr::I64Store8:
             alignLog2 = 0;
@@ -2768,70 +2039,70 @@ ParseStore(WasmParseContext& c, Expr exp
           case Expr::F64Store:
             alignLog2 = 3;
             break;
           default:
             MOZ_CRASH("Bad load expr");
         }
     }
 
-    WasmAstExpr* value = ParseExpr(c);
+    AstExpr* value = ParseExpr(c);
     if (!value)
         return nullptr;
 
     uint32_t flags = alignLog2;
 
-    return new(c.lifo) WasmAstStore(expr, WasmAstLoadStoreAddress(base, flags, offset), value);
+    return new(c.lifo) AstStore(expr, AstLoadStoreAddress(base, flags, offset), value);
 }
 
-static WasmAstBranchTable*
+static AstBranchTable*
 ParseBranchTable(WasmParseContext& c, WasmToken brTable)
 {
-    WasmRefVector table(c.lifo);
-
-    WasmRef target;
+    AstRefVector table(c.lifo);
+
+    AstRef target;
     while (c.ts.getIfRef(&target)) {
         if (!table.append(target))
             return nullptr;
     }
 
     if (table.empty()) {
         c.ts.generateError(brTable, c.error);
         return nullptr;
     }
 
-    WasmRef def = table.popCopy();
-
-    WasmAstExpr* index = ParseExpr(c);
+    AstRef def = table.popCopy();
+
+    AstExpr* index = ParseExpr(c);
     if (!index)
         return nullptr;
 
-    WasmAstExpr* value = nullptr;
+    AstExpr* value = nullptr;
     if (c.ts.getIf(WasmToken::OpenParen)) {
         value = index;
         index = ParseExprInsideParens(c);
         if (!index)
             return nullptr;
         if (!c.ts.match(WasmToken::CloseParen, c.error))
             return nullptr;
     }
 
-    return new(c.lifo) WasmAstBranchTable(*index, def, Move(table), value);
+    return new(c.lifo) AstBranchTable(*index, def, Move(table), value);
 }
 
-static WasmAstExpr*
+static AstExpr*
 ParseExprInsideParens(WasmParseContext& c)
 {
     WasmToken token = c.ts.get();
 
     switch (token.kind()) {
       case WasmToken::Nop:
-        return new(c.lifo) WasmAstNop;
+        return new(c.lifo) AstNop;
       case WasmToken::Unreachable:
-        return new(c.lifo) WasmAstUnreachable;
+        return new(c.lifo) AstUnreachable;
       case WasmToken::BinaryOpcode:
         return ParseBinaryOperator(c, token.expr());
       case WasmToken::Block:
         return ParseBlock(c, Expr::Block);
       case WasmToken::Br:
         return ParseBranch(c, Expr::Br);
       case WasmToken::BrIf:
         return ParseBranch(c, Expr::BrIf);
@@ -2869,25 +2140,25 @@ ParseExprInsideParens(WasmParseContext& 
         return ParseUnaryOperator(c, token.expr());
       default:
         c.ts.generateError(token, c.error);
         return nullptr;
     }
 }
 
 static bool
-ParseValueType(WasmParseContext& c, WasmAstValTypeVector* vec)
+ParseValueType(WasmParseContext& c, AstValTypeVector* vec)
 {
     WasmToken token;
     return c.ts.match(WasmToken::ValueType, &token, c.error) &&
            vec->append(token.valueType());
 }
 
 static bool
-ParseValueTypeList(WasmParseContext& c, WasmAstValTypeVector* vec)
+ParseValueTypeList(WasmParseContext& c, AstValTypeVector* vec)
 {
     WasmToken token;
     while (c.ts.getIf(WasmToken::ValueType, &token)) {
         if (!vec->append(token.valueType()))
             return false;
     }
 
     return true;
@@ -2905,56 +2176,56 @@ ParseResult(WasmParseContext& c, ExprTyp
     if (!c.ts.match(WasmToken::ValueType, &token, c.error))
         return false;
 
     *result = ToExprType(token.valueType());
     return true;
 }
 
 static bool
-ParseLocal(WasmParseContext& c, WasmNameVector* locals, WasmAstValTypeVector* localTypes)
+ParseLocal(WasmParseContext& c, AstNameVector* locals, AstValTypeVector* localTypes)
 {
     return locals->append(c.ts.getIfName()) &&
            ParseValueType(c, localTypes);
 }
 
 static bool
-ParseParam(WasmParseContext& c, WasmNameVector* locals, WasmAstValTypeVector* args)
+ParseParam(WasmParseContext& c, AstNameVector* locals, AstValTypeVector* args)
 {
     if (c.ts.peek().kind() == WasmToken::Name)
         return ParseLocal(c, locals, args);
 
-    return locals->append(WasmName()) &&
+    return locals->append(AstName()) &&
            ParseValueTypeList(c, args);
 }
 
-static WasmAstFunc*
-ParseFunc(WasmParseContext& c, WasmAstModule* module)
+static AstFunc*
+ParseFunc(WasmParseContext& c, AstModule* module)
 {
-    WasmAstValTypeVector vars(c.lifo);
-    WasmAstValTypeVector args(c.lifo);
-    WasmNameVector locals(c.lifo);
-
-    WasmName funcName = c.ts.getIfName();
-
-    WasmRef sig;
+    AstValTypeVector vars(c.lifo);
+    AstValTypeVector args(c.lifo);
+    AstNameVector locals(c.lifo);
+
+    AstName funcName = c.ts.getIfName();
+
+    AstRef sig;
 
     WasmToken openParen;
     if (c.ts.getIf(WasmToken::OpenParen, &openParen)) {
         if (c.ts.getIf(WasmToken::Type)) {
             if (!c.ts.matchRef(&sig, c.error))
                 return nullptr;
             if (!c.ts.match(WasmToken::CloseParen, c.error))
                 return nullptr;
         } else {
             c.ts.unget(openParen);
         }
     }
 
-    WasmAstExprVector body(c.lifo);
+    AstExprVector body(c.lifo);
     ExprType result = ExprType::Void;
 
     while (c.ts.getIf(WasmToken::OpenParen)) {
         WasmToken token = c.ts.get();
         switch (token.kind()) {
           case WasmToken::Local:
             if (!ParseLocal(c, &locals, &vars))
                 return nullptr;
@@ -2968,39 +2239,39 @@ ParseFunc(WasmParseContext& c, WasmAstMo
                 return nullptr;
             break;
           case WasmToken::Result:
             if (!ParseResult(c, &result))
                 return nullptr;
             break;
           default:
             c.ts.unget(token);
-            WasmAstExpr* expr = ParseExprInsideParens(c);
+            AstExpr* expr = ParseExprInsideParens(c);
             if (!expr || !body.append(expr))
                 return nullptr;
             break;
         }
         if (!c.ts.match(WasmToken::CloseParen, c.error))
             return nullptr;
     }
 
     if (sig.isInvalid()) {
         uint32_t sigIndex;
-        if (!module->declare(WasmAstSig(Move(args), result), &sigIndex))
+        if (!module->declare(AstSig(Move(args), result), &sigIndex))
             return nullptr;
         sig.setIndex(sigIndex);
     }
 
-    return new(c.lifo) WasmAstFunc(funcName, sig, Move(vars), Move(locals), Move(body));
+    return new(c.lifo) AstFunc(funcName, sig, Move(vars), Move(locals), Move(body));
 }
 
 static bool
-ParseFuncType(WasmParseContext& c, WasmAstSig* sig)
+ParseFuncType(WasmParseContext& c, AstSig* sig)
 {
-    WasmAstValTypeVector args(c.lifo);
+    AstValTypeVector args(c.lifo);
     ExprType result = ExprType::Void;
 
     while (c.ts.getIf(WasmToken::OpenParen)) {
         WasmToken token = c.ts.get();
         switch (token.kind()) {
           case WasmToken::Param:
             if (!ParseValueTypeList(c, &args))
                 return false;
@@ -3012,205 +2283,205 @@ ParseFuncType(WasmParseContext& c, WasmA
           default:
             c.ts.generateError(token, c.error);
             return false;
         }
         if (!c.ts.match(WasmToken::CloseParen, c.error))
             return false;
     }
 
-    *sig = WasmAstSig(Move(args), result);
+    *sig = AstSig(Move(args), result);
     return true;
 }
 
-static WasmAstSig*
+static AstSig*
 ParseTypeDef(WasmParseContext& c)
 {
-    WasmName name = c.ts.getIfName();
+    AstName name = c.ts.getIfName();
 
     if (!c.ts.match(WasmToken::OpenParen, c.error))
         return nullptr;
     if (!c.ts.match(WasmToken::Func, c.error))
         return nullptr;
 
-    WasmAstSig sig(c.lifo);
+    AstSig sig(c.lifo);
     if (!ParseFuncType(c, &sig))
         return nullptr;
 
     if (!c.ts.match(WasmToken::CloseParen, c.error))
         return nullptr;
 
-    return new(c.lifo) WasmAstSig(name, Move(sig));
+    return new(c.lifo) AstSig(name, Move(sig));
 }
 
-static WasmAstSegment*
+static AstSegment*
 ParseSegment(WasmParseContext& c)
 {
     if (!c.ts.match(WasmToken::Segment, c.error))
         return nullptr;
 
     WasmToken dstOffset;
     if (!c.ts.match(WasmToken::Index, &dstOffset, c.error))
         return nullptr;
 
     WasmToken text;
     if (!c.ts.match(WasmToken::Text, &text, c.error))
         return nullptr;
 
-    return new(c.lifo) WasmAstSegment(dstOffset.index(), text.text());
+    return new(c.lifo) AstSegment(dstOffset.index(), text.text());
 }
 
-static WasmAstMemory*
+static AstMemory*
 ParseMemory(WasmParseContext& c)
 {
     WasmToken initialSize;
     if (!c.ts.match(WasmToken::Index, &initialSize, c.error))
         return nullptr;
 
     Maybe<uint32_t> maxSize;
     WasmToken token;
     if (c.ts.getIf(WasmToken::Index, &token))
         maxSize.emplace(token.index());
 
-    WasmAstSegmentVector segments(c.lifo);
+    AstSegmentVector segments(c.lifo);
     while (c.ts.getIf(WasmToken::OpenParen)) {
-        WasmAstSegment* segment = ParseSegment(c);
+        AstSegment* segment = ParseSegment(c);
         if (!segment || !segments.append(segment))
             return nullptr;
         if (!c.ts.match(WasmToken::CloseParen, c.error))
             return nullptr;
     }
 
-    return new(c.lifo) WasmAstMemory(initialSize.index(), maxSize, Move(segments));
+    return new(c.lifo) AstMemory(initialSize.index(), maxSize, Move(segments));
 }
 
-static WasmAstImport*
-ParseImport(WasmParseContext& c, WasmAstModule* module)
+static AstImport*
+ParseImport(WasmParseContext& c, AstModule* module)
 {
-    WasmName name = c.ts.getIfName();
+    AstName name = c.ts.getIfName();
 
     WasmToken moduleName;
     if (!c.ts.match(WasmToken::Text, &moduleName, c.error))
         return nullptr;
 
     WasmToken funcName;
     if (!c.ts.match(WasmToken::Text, &funcName, c.error))
         return nullptr;
 
-    WasmAstSig sig(c.lifo);
+    AstSig sig(c.lifo);
     if (!ParseFuncType(c, &sig))
         return nullptr;
 
     uint32_t sigIndex;
     if (!module->declare(Move(sig), &sigIndex))
         return nullptr;
 
-    return new(c.lifo) WasmAstImport(name, moduleName.text(), funcName.text(), sigIndex);
+    return new(c.lifo) AstImport(name, moduleName.text(), funcName.text(), sigIndex);
 }
 
-static WasmAstExport*
+static AstExport*
 ParseExport(WasmParseContext& c)
 {
     WasmToken name;
     if (!c.ts.match(WasmToken::Text, &name, c.error))
         return nullptr;
 
     WasmToken exportee = c.ts.get();
     switch (exportee.kind()) {
       case WasmToken::Index:
-        return new(c.lifo) WasmAstExport(name.text(), WasmRef(WasmName(), exportee.index()));
+        return new(c.lifo) AstExport(name.text(), AstRef(AstName(), exportee.index()));
       case WasmToken::Name:
-        return new(c.lifo) WasmAstExport(name.text(), WasmRef(exportee.name(), WasmNoIndex));
+        return new(c.lifo) AstExport(name.text(), AstRef(exportee.name(), AstNoIndex));
       case WasmToken::Memory:
-        if (name.text() != WasmName(MOZ_UTF16("memory"), 6)) {
+        if (name.text() != AstName(MOZ_UTF16("memory"), 6)) {
             c.ts.generateError(exportee, c.error);
             return nullptr;
         }
-        return new(c.lifo) WasmAstExport(name.text());
+        return new(c.lifo) AstExport(name.text());
       default:
         break;
     }
 
     c.ts.generateError(exportee, c.error);
     return nullptr;
 
 }
 
-static WasmAstTable*
+static AstTable*
 ParseTable(WasmParseContext& c)
 {
-    WasmAstTableElemVector elems(c.lifo);
-
-    WasmRef elem;
+    AstTableElemVector elems(c.lifo);
+
+    AstRef elem;
     while (c.ts.getIfRef(&elem)) {
         if (!elems.append(elem))
             return nullptr;
     }
 
-    return new(c.lifo) WasmAstTable(Move(elems));
+    return new(c.lifo) AstTable(Move(elems));
 }
 
-static WasmAstModule*
+static AstModule*
 ParseModule(const char16_t* text, LifoAlloc& lifo, UniqueChars* error)
 {
     WasmParseContext c(text, lifo, error);
 
     if (!c.ts.match(WasmToken::OpenParen, c.error))
         return nullptr;
     if (!c.ts.match(WasmToken::Module, c.error))
         return nullptr;
 
-    auto module = new(c.lifo) WasmAstModule(c.lifo);
+    auto module = new(c.lifo) AstModule(c.lifo);
     if (!module || !module->init())
         return nullptr;
 
     while (c.ts.getIf(WasmToken::OpenParen)) {
         WasmToken section = c.ts.get();
 
         switch (section.kind()) {
           case WasmToken::Type: {
-            WasmAstSig* sig = ParseTypeDef(c);
+            AstSig* sig = ParseTypeDef(c);
             if (!sig || !module->append(sig))
                 return nullptr;
             break;
           }
           case WasmToken::Memory: {
-            WasmAstMemory* memory = ParseMemory(c);
+            AstMemory* memory = ParseMemory(c);
             if (!memory)
                 return nullptr;
             if (!module->setMemory(memory)) {
                 c.ts.generateError(section, c.error);
                 return nullptr;
             }
             break;
           }
           case WasmToken::Import: {
-            WasmAstImport* imp = ParseImport(c, module);
+            AstImport* imp = ParseImport(c, module);
             if (!imp || !module->append(imp))
                 return nullptr;
             break;
           }
           case WasmToken::Export: {
-            WasmAstExport* exp = ParseExport(c);
+            AstExport* exp = ParseExport(c);
             if (!exp || !module->append(exp))
                 return nullptr;
             break;
           }
           case WasmToken::Table: {
-            WasmAstTable* table = ParseTable(c);
+            AstTable* table = ParseTable(c);
             if (!table)
                 return nullptr;
             if (!module->initTable(table)) {
                 c.ts.generateError(section, c.error);
                 return nullptr;
             }
             break;
           }
           case WasmToken::Func: {
-            WasmAstFunc* func = ParseFunc(c, module);
+            AstFunc* func = ParseFunc(c, module);
             if (!func || !module->append(func))
                 return nullptr;
             break;
           }
           default:
             c.ts.generateError(section, c.error);
             return nullptr;
         }
@@ -3230,49 +2501,49 @@ ParseModule(const char16_t* text, LifoAl
 /*****************************************************************************/
 // wasm name resolution
 
 namespace {
 
 class Resolver
 {
     UniqueChars* error_;
-    WasmNameMap varMap_;
-    WasmNameMap sigMap_;
-    WasmNameMap funcMap_;
-    WasmNameMap importMap_;
-    WasmNameVector targetStack_;
-
-    bool registerName(WasmNameMap& map, WasmName name, size_t index) {
-        WasmNameMap::AddPtr p = map.lookupForAdd(name);
+    AstNameMap varMap_;
+    AstNameMap sigMap_;
+    AstNameMap funcMap_;
+    AstNameMap importMap_;
+    AstNameVector targetStack_;
+
+    bool registerName(AstNameMap& map, AstName name, size_t index) {
+        AstNameMap::AddPtr p = map.lookupForAdd(name);
         if (!p) {
             if (!map.add(p, name, index))
                 return false;
         } else {
             return false;
         }
         return true;
     }
-    bool resolveName(WasmNameMap& map, WasmName name, size_t* index) {
-        WasmNameMap::Ptr p = map.lookup(name);
+    bool resolveName(AstNameMap& map, AstName name, size_t* index) {
+        AstNameMap::Ptr p = map.lookup(name);
         if (p) {
             *index = p->value();
             return true;
         }
         return false;
     }
-    bool resolveRef(WasmNameMap& map, WasmRef& ref) {
-        WasmNameMap::Ptr p = map.lookup(ref.name());
+    bool resolveRef(AstNameMap& map, AstRef& ref) {
+        AstNameMap::Ptr p = map.lookup(ref.name());
         if (p) {
             ref.setIndex(p->value());
             return true;
         }
         return false;
     }
-    bool failResolveLabel(const char* kind, WasmName name) {
+    bool failResolveLabel(const char* kind, AstName name) {
         Vector<char16_t, 0, SystemAllocPolicy> nameWithNull;
         if (!nameWithNull.append(name.begin(), name.length()))
             return false;
         if (!nameWithNull.append(0))
             return false;
         error_->reset(JS_smprintf("%s label '%hs' not found", kind, nameWithNull.begin()));
         return false;
     }
@@ -3288,57 +2559,57 @@ class Resolver
     {}
     bool init() {
         return sigMap_.init() && funcMap_.init() && importMap_.init() && varMap_.init();
     }
     void beginFunc() {
         varMap_.clear();
         MOZ_ASSERT(targetStack_.empty());
     }
-    bool registerSigName(WasmName name, size_t index) {
+    bool registerSigName(AstName name, size_t index) {
         return name.empty() || registerName(sigMap_, name, index);
     }
-    bool registerFuncName(WasmName name, size_t index) {
+    bool registerFuncName(AstName name, size_t index) {
         return name.empty() || registerName(funcMap_, name, index);
     }
-    bool registerImportName(WasmName name, size_t index) {
+    bool registerImportName(AstName name, size_t index) {
         return name.empty() || registerName(importMap_, name, index);
     }
-    bool registerVarName(WasmName name, size_t index) {
+    bool registerVarName(AstName name, size_t index) {
         return name.empty() || registerName(varMap_, name, index);
     }
-    bool pushTarget(WasmName name) {
+    bool pushTarget(AstName name) {
         return targetStack_.append(name);
     }
-    void popTarget(WasmName name) {
+    void popTarget(AstName name) {
         MOZ_ASSERT(targetStack_.back() == name);
         targetStack_.popBack();
     }
 
-    bool resolveSignature(WasmRef& ref) {
+    bool resolveSignature(AstRef& ref) {
         if (!ref.name().empty() && !resolveRef(sigMap_, ref))
             return failResolveLabel("signature", ref.name());
         return true;
     }
-    bool resolveFunction(WasmRef& ref) {
+    bool resolveFunction(AstRef& ref) {
         if (!ref.name().empty() && !resolveRef(funcMap_, ref))
             return failResolveLabel("function", ref.name());
         return true;
     }
-    bool resolveImport(WasmRef& ref) {
+    bool resolveImport(AstRef& ref) {
         if (!ref.name().empty() && !resolveRef(importMap_, ref))
             return failResolveLabel("import", ref.name());
         return true;
     }
-    bool resolveLocal(WasmRef& ref) {
+    bool resolveLocal(AstRef& ref) {
         if (!ref.name().empty() && !resolveRef(varMap_, ref))
             return failResolveLabel("local", ref.name());
         return true;
     }
-    bool resolveBranchTarget(WasmRef& ref) {
+    bool resolveBranchTarget(AstRef& ref) {
         if (ref.name().empty())
             return true;
         for (size_t i = 0, e = targetStack_.length(); i < e; i++) {
             if (targetStack_[e - i - 1] == ref.name()) {
                 ref.setIndex(i);
                 return true;
             }
         }
@@ -3349,20 +2620,20 @@ class Resolver
         error_->reset(JS_smprintf("%s", message));
         return false;
     }
 };
 
 } // end anonymous namespace
 
 static bool
-ResolveExpr(Resolver& r, WasmAstExpr& expr);
+ResolveExpr(Resolver& r, AstExpr& expr);
 
 static bool
-ResolveBlock(Resolver& r, WasmAstBlock& b)
+ResolveBlock(Resolver& r, AstBlock& b)
 {
     if (!r.pushTarget(b.breakName()))
         return false;
 
     if (b.expr() == Expr::Loop) {
         if (!r.pushTarget(b.continueName()))
             return false;
     }
@@ -3375,45 +2646,45 @@ ResolveBlock(Resolver& r, WasmAstBlock& 
 
     if (b.expr() == Expr::Loop)
         r.popTarget(b.continueName());
     r.popTarget(b.breakName());
     return true;
 }
 
 static bool
-ResolveBranch(Resolver& r, WasmAstBranch& br)
+ResolveBranch(Resolver& r, AstBranch& br)
 {
     if (!r.resolveBranchTarget(br.target()))
         return false;
 
     if (br.maybeValue() && !ResolveExpr(r, *br.maybeValue()))
         return false;
 
     if (br.expr() == Expr::BrIf) {
         if (!ResolveExpr(r, br.cond()))
             return false;
     }
 
     return true;
 }
 
 static bool
-ResolveArgs(Resolver& r, const WasmAstExprVector& args)
+ResolveArgs(Resolver& r, const AstExprVector& args)
 {
-    for (WasmAstExpr* arg : args) {
+    for (AstExpr* arg : args) {
         if (!ResolveExpr(r, *arg))
             return false;
     }
 
     return true;
 }
 
 static bool
-ResolveCall(Resolver& r, WasmAstCall& c)
+ResolveCall(Resolver& r, AstCall& c)
 {
     if (!ResolveArgs(r, c.args()))
         return false;
 
     if (c.expr() == Expr::Call) {
         if (!r.resolveFunction(c.func()))
             return false;
     } else {
@@ -3421,262 +2692,262 @@ ResolveCall(Resolver& r, WasmAstCall& c)
         if (!r.resolveImport(c.func()))
             return false;
     }
 
     return true;
 }
 
 static bool
-ResolveCallIndirect(Resolver& r, WasmAstCallIndirect& c)
+ResolveCallIndirect(Resolver& r, AstCallIndirect& c)
 {
     if (!ResolveExpr(r, *c.index()))
         return false;
 
     if (!ResolveArgs(r, c.args()))
         return false;
 
     if (!r.resolveSignature(c.sig()))
         return false;
 
     return true;
 }
 
 static bool
-ResolveGetLocal(Resolver& r, WasmAstGetLocal& gl)
+ResolveGetLocal(Resolver& r, AstGetLocal& gl)
 {
     return r.resolveLocal(gl.local());
 }
 
 static bool
-ResolveSetLocal(Resolver& r, WasmAstSetLocal& sl)
+ResolveSetLocal(Resolver& r, AstSetLocal& sl)
 {
     if (!ResolveExpr(r, sl.value()))
         return false;
 
     if (!r.resolveLocal(sl.local()))
         return false;
 
     return true;
 }
 
 static bool
-ResolveUnaryOperator(Resolver& r, WasmAstUnaryOperator& b)
+ResolveUnaryOperator(Resolver& r, AstUnaryOperator& b)
 {
     return ResolveExpr(r, *b.op());
 }
 
 static bool
-ResolveBinaryOperator(Resolver& r, WasmAstBinaryOperator& b)
+ResolveBinaryOperator(Resolver& r, AstBinaryOperator& b)
 {
     return ResolveExpr(r, *b.lhs()) &&
            ResolveExpr(r, *b.rhs());
 }
 
 static bool
-ResolveTernaryOperator(Resolver& r, WasmAstTernaryOperator& b)
+ResolveTernaryOperator(Resolver& r, AstTernaryOperator& b)
 {
     return ResolveExpr(r, *b.op0()) &&
            ResolveExpr(r, *b.op1()) &&
            ResolveExpr(r, *b.op2());
 }
 
 static bool
-ResolveComparisonOperator(Resolver& r, WasmAstComparisonOperator& b)
+ResolveComparisonOperator(Resolver& r, AstComparisonOperator& b)
 {
     return ResolveExpr(r, *b.lhs()) &&
            ResolveExpr(r, *b.rhs());
 }
 
 static bool
-ResolveConversionOperator(Resolver& r, WasmAstConversionOperator& b)
+ResolveConversionOperator(Resolver& r, AstConversionOperator& b)
 {
     return ResolveExpr(r, *b.op());
 }
 
 static bool
-ResolveIfElse(Resolver& r, WasmAstIf& i)
+ResolveIfElse(Resolver& r, AstIf& i)
 {
     if (!ResolveExpr(r, i.cond()))
         return false;
-    if (!r.pushTarget(WasmName()))
+    if (!r.pushTarget(AstName()))
         return false;
     if (!ResolveExpr(r, i.thenBranch()))
         return false;
     if (i.hasElse()) {
         if (!ResolveExpr(r, i.elseBranch()))
             return false;
     }
-    r.popTarget(WasmName());
+    r.popTarget(AstName());
     return true;
 }
 
 static bool
-ResolveLoadStoreAddress(Resolver& r, const WasmAstLoadStoreAddress &address)
+ResolveLoadStoreAddress(Resolver& r, const AstLoadStoreAddress &address)
 {
     return ResolveExpr(r, address.base());
 }
 
 static bool
-ResolveLoad(Resolver& r, WasmAstLoad& l)
+ResolveLoad(Resolver& r, AstLoad& l)
 {
     return ResolveLoadStoreAddress(r, l.address());
 }
 
 static bool
-ResolveStore(Resolver& r, WasmAstStore& s)
+ResolveStore(Resolver& r, AstStore& s)
 {
     return ResolveLoadStoreAddress(r, s.address()) &&
            ResolveExpr(r, s.value());
 }
 
 static bool
-ResolveReturn(Resolver& r, WasmAstReturn& ret)
+ResolveReturn(Resolver& r, AstReturn& ret)
 {
     return !ret.maybeExpr() || ResolveExpr(r, *ret.maybeExpr());
 }
 
 static bool
-ResolveBranchTable(Resolver& r, WasmAstBranchTable& bt)
+ResolveBranchTable(Resolver& r, AstBranchTable& bt)
 {
     if (!r.resolveBranchTarget(bt.def()))
         return false;
 
-    for (WasmRef& elem : bt.table()) {
+    for (AstRef& elem : bt.table()) {
         if (!r.resolveBranchTarget(elem))
             return false;
     }
 
     return ResolveExpr(r, bt.index());
 }
 
 static bool
-ResolveExpr(Resolver& r, WasmAstExpr& expr)
+ResolveExpr(Resolver& r, AstExpr& expr)
 {
     switch (expr.kind()) {
-      case WasmAstExprKind::Nop:
-      case WasmAstExprKind::Unreachable:
+      case AstExprKind::Nop:
+      case AstExprKind::Unreachable:
         return true;
-      case WasmAstExprKind::BinaryOperator:
-        return ResolveBinaryOperator(r, expr.as<WasmAstBinaryOperator>());
-      case WasmAstExprKind::Block:
-        return ResolveBlock(r, expr.as<WasmAstBlock>());
-      case WasmAstExprKind::Branch:
-        return ResolveBranch(r, expr.as<WasmAstBranch>());
-      case WasmAstExprKind::Call:
-        return ResolveCall(r, expr.as<WasmAstCall>());
-      case WasmAstExprKind::CallIndirect:
-        return ResolveCallIndirect(r, expr.as<WasmAstCallIndirect>());
-      case WasmAstExprKind::ComparisonOperator:
-        return ResolveComparisonOperator(r, expr.as<WasmAstComparisonOperator>());
-      case WasmAstExprKind::Const:
+      case AstExprKind::BinaryOperator:
+        return ResolveBinaryOperator(r, expr.as<AstBinaryOperator>());
+      case AstExprKind::Block:
+        return ResolveBlock(r, expr.as<AstBlock>());
+      case AstExprKind::Branch:
+        return ResolveBranch(r, expr.as<AstBranch>());
+      case AstExprKind::Call:
+        return ResolveCall(r, expr.as<AstCall>());
+      case AstExprKind::CallIndirect:
+        return ResolveCallIndirect(r, expr.as<AstCallIndirect>());
+      case AstExprKind::ComparisonOperator:
+        return ResolveComparisonOperator(r, expr.as<AstComparisonOperator>());
+      case AstExprKind::Const:
         return true;
-      case WasmAstExprKind::ConversionOperator:
-        return ResolveConversionOperator(r, expr.as<WasmAstConversionOperator>());
-      case WasmAstExprKind::GetLocal:
-        return ResolveGetLocal(r, expr.as<WasmAstGetLocal>());
-      case WasmAstExprKind::If:
-        return ResolveIfElse(r, expr.as<WasmAstIf>());
-      case WasmAstExprKind::Load:
-        return ResolveLoad(r, expr.as<WasmAstLoad>());
-      case WasmAstExprKind::Return:
-        return ResolveReturn(r, expr.as<WasmAstReturn>());
-      case WasmAstExprKind::SetLocal:
-        return ResolveSetLocal(r, expr.as<WasmAstSetLocal>());
-      case WasmAstExprKind::Store:
-        return ResolveStore(r, expr.as<WasmAstStore>());
-      case WasmAstExprKind::BranchTable:
-        return ResolveBranchTable(r, expr.as<WasmAstBranchTable>());
-      case WasmAstExprKind::TernaryOperator:
-        return ResolveTernaryOperator(r, expr.as<WasmAstTernaryOperator>());
-      case WasmAstExprKind::UnaryOperator:
-        return ResolveUnaryOperator(r, expr.as<WasmAstUnaryOperator>());
+      case AstExprKind::ConversionOperator:
+        return ResolveConversionOperator(r, expr.as<AstConversionOperator>());
+      case AstExprKind::GetLocal:
+        return ResolveGetLocal(r, expr.as<AstGetLocal>());
+      case AstExprKind::If:
+        return ResolveIfElse(r, expr.as<AstIf>());
+      case AstExprKind::Load:
+        return ResolveLoad(r, expr.as<AstLoad>());
+      case AstExprKind::Return:
+        return ResolveReturn(r, expr.as<AstReturn>());
+      case AstExprKind::SetLocal:
+        return ResolveSetLocal(r, expr.as<AstSetLocal>());
+      case AstExprKind::Store:
+        return ResolveStore(r, expr.as<AstStore>());
+      case AstExprKind::BranchTable:
+        return ResolveBranchTable(r, expr.as<AstBranchTable>());
+      case AstExprKind::TernaryOperator:
+        return ResolveTernaryOperator(r, expr.as<AstTernaryOperator>());
+      case AstExprKind::UnaryOperator:
+        return ResolveUnaryOperator(r, expr.as<AstUnaryOperator>());
     }
     MOZ_CRASH("Bad expr kind");
 }
 
 static bool
-ResolveFunc(Resolver& r, WasmAstFunc& func)
+ResolveFunc(Resolver& r, AstFunc& func)
 {
     r.beginFunc();
 
     size_t numVars = func.locals().length();
     for (size_t i = 0; i < numVars; i++) {
         if (!r.registerVarName(func.locals()[i], i))
             return r.fail("duplicate var");
     }
 
-    for (WasmAstExpr* expr : func.body()) {
+    for (AstExpr* expr : func.body()) {
         if (!ResolveExpr(r, *expr))
             return false;
     }
     return true;
 }
 
 static bool
-ResolveModule(LifoAlloc& lifo, WasmAstModule* module, UniqueChars* error)
+ResolveModule(LifoAlloc& lifo, AstModule* module, UniqueChars* error)
 {
     Resolver r(lifo, error);
 
     if (!r.init())
         return false;
 
     size_t numSigs = module->sigs().length();
     for (size_t i = 0; i < numSigs; i++) {
-        WasmAstSig* sig = module->sigs()[i];
+        AstSig* sig = module->sigs()[i];
         if (!r.registerSigName(sig->name(), i))
             return r.fail("duplicate signature");
     }
 
     size_t numFuncs = module->funcs().length();
     for (size_t i = 0; i < numFuncs; i++) {
-        WasmAstFunc* func = module->funcs()[i];
+        AstFunc* func = module->funcs()[i];
         if (!r.resolveSignature(func->sig()))
             return false;
         if (!r.registerFuncName(func->name(), i))
             return r.fail("duplicate function");
     }
 
     if (module->maybeTable()) {
-        for (WasmRef& ref : module->maybeTable()->elems()) {
+        for (AstRef& ref : module->maybeTable()->elems()) {
             if (!r.resolveFunction(ref))
                 return false;
         }
     }
 
     size_t numImports = module->imports().length();
     for (size_t i = 0; i < numImports; i++) {
-        WasmAstImport* imp = module->imports()[i];
+        AstImport* imp = module->imports()[i];
         if (!r.registerImportName(imp->name(), i))
             return r.fail("duplicate import");
     }
 
-    for (WasmAstExport* export_ : module->exports()) {
-        if (export_->kind() != WasmAstExportKind::Func)
+    for (AstExport* export_ : module->exports()) {
+        if (export_->kind() != AstExportKind::Func)
             continue;
         if (!r.resolveFunction(export_->func()))
             return false;
     }
 
-    for (WasmAstFunc* func : module->funcs()) {
+    for (AstFunc* func : module->funcs()) {
         if (!ResolveFunc(r, *func))
             return false;
     }
 
     return true;
 }
 
 /*****************************************************************************/
 // wasm function body serialization
 
 static bool
-EncodeExpr(Encoder& e, WasmAstExpr& expr);
+EncodeExpr(Encoder& e, AstExpr& expr);
 
 static bool
-EncodeBlock(Encoder& e, WasmAstBlock& b)
+EncodeBlock(Encoder& e, AstBlock& b)
 {
     if (!e.writeExpr(b.expr()))
         return false;
 
     size_t numExprs = b.exprs().length();
 
     for (size_t i = 0; i < numExprs; i++) {
         if (!EncodeExpr(e, *b.exprs()[i]))
@@ -3685,17 +2956,17 @@ EncodeBlock(Encoder& e, WasmAstBlock& b)
 
     if (!e.writeExpr(Expr::End))
         return false;
 
     return true;
 }
 
 static bool
-EncodeBranch(Encoder& e, WasmAstBranch& br)
+EncodeBranch(Encoder& e, AstBranch& br)
 {
     MOZ_ASSERT(br.expr() == Expr::Br || br.expr() == Expr::BrIf);
 
     uint32_t arity = 0;
     if (br.maybeValue()) {
         arity = 1;
         if (!EncodeExpr(e, *br.maybeValue()))
             return false;
@@ -3714,28 +2985,28 @@ EncodeBranch(Encoder& e, WasmAstBranch& 
 
     if (!e.writeVarU32(br.target().index()))
         return false;
 
     return true;
 }
 
 static bool
-EncodeArgs(Encoder& e, const WasmAstExprVector& args)
+EncodeArgs(Encoder& e, const AstExprVector& args)
 {
-    for (WasmAstExpr* arg : args) {
+    for (AstExpr* arg : args) {
         if (!EncodeExpr(e, *arg))
             return false;
     }
 
     return true;
 }
 
 static bool
-EncodeCall(Encoder& e, WasmAstCall& c)
+EncodeCall(Encoder& e, AstCall& c)
 {
     if (!EncodeArgs(e, c.args()))
         return false;
 
     if (!e.writeExpr(c.expr()))
         return false;
 
     if (!e.writeVarU32(c.args().length()))
@@ -3743,17 +3014,17 @@ EncodeCall(Encoder& e, WasmAstCall& c)
 
     if (!e.writeVarU32(c.func().index()))
         return false;
 
     return true;
 }
 
 static bool
-EncodeCallIndirect(Encoder& e, WasmAstCallIndirect& c)
+EncodeCallIndirect(Encoder& e, AstCallIndirect& c)
 {
     if (!EncodeExpr(e, *c.index()))
         return false;
 
     if (!EncodeArgs(e, c.args()))
         return false;
 
     if (!e.writeExpr(Expr::CallIndirect))
@@ -3764,17 +3035,17 @@ EncodeCallIndirect(Encoder& e, WasmAstCa
 
     if (!e.writeVarU32(c.sig().index()))
         return false;
 
     return true;
 }
 
 static bool
-EncodeConst(Encoder& e, WasmAstConst& c)
+EncodeConst(Encoder& e, AstConst& c)
 {
     switch (c.val().type()) {
       case ValType::I32:
         return e.writeExpr(Expr::I32Const) &&
                e.writeVarS32(c.val().i32());
       case ValType::I64:
         return e.writeExpr(Expr::I64Const) &&
                e.writeVarS64(c.val().i64());
@@ -3786,113 +3057,113 @@ EncodeConst(Encoder& e, WasmAstConst& c)
                e.writeFixedF64(c.val().f64());
       default:
         break;
     }
     MOZ_CRASH("Bad value type");
 }
 
 static bool
-EncodeGetLocal(Encoder& e, WasmAstGetLocal& gl)
+EncodeGetLocal(Encoder& e, AstGetLocal& gl)
 {
     return e.writeExpr(Expr::GetLocal) &&
            e.writeVarU32(gl.local().index());
 }
 
 static bool
-EncodeSetLocal(Encoder& e, WasmAstSetLocal& sl)
+EncodeSetLocal(Encoder& e, AstSetLocal& sl)
 {
     return EncodeExpr(e, sl.value()) &&
            e.writeExpr(Expr::SetLocal) &&
            e.writeVarU32(sl.local().index());
 }
 
 static bool
-EncodeUnaryOperator(Encoder& e, WasmAstUnaryOperator& b)
+EncodeUnaryOperator(Encoder& e, AstUnaryOperator& b)
 {
     return EncodeExpr(e, *b.op()) &&
            e.writeExpr(b.expr());
 }
 
 static bool
-EncodeBinaryOperator(Encoder& e, WasmAstBinaryOperator& b)
+EncodeBinaryOperator(Encoder& e, AstBinaryOperator& b)
 {
     return EncodeExpr(e, *b.lhs()) &&
            EncodeExpr(e, *b.rhs()) &&
            e.writeExpr(b.expr());
 }
 
 static bool
-EncodeTernaryOperator(Encoder& e, WasmAstTernaryOperator& b)
+EncodeTernaryOperator(Encoder& e, AstTernaryOperator& b)
 {
     return EncodeExpr(e, *b.op0()) &&
            EncodeExpr(e, *b.op1()) &&
            EncodeExpr(e, *b.op2()) &&
            e.writeExpr(b.expr());
 }
 
 static bool
-EncodeComparisonOperator(Encoder& e, WasmAstComparisonOperator& b)
+EncodeComparisonOperator(Encoder& e, AstComparisonOperator& b)
 {
     return EncodeExpr(e, *b.lhs()) &&
            EncodeExpr(e, *b.rhs()) &&
            e.writeExpr(b.expr());
 }
 
 static bool
-EncodeConversionOperator(Encoder& e, WasmAstConversionOperator& b)
+EncodeConversionOperator(Encoder& e, AstConversionOperator& b)
 {
     return EncodeExpr(e, *b.op()) &&
            e.writeExpr(b.expr());
 }
 
 static bool
-EmitIf(Encoder& e, WasmAstIf& i)
+EmitIf(Encoder& e, AstIf& i)
 {
     return EncodeExpr(e, i.cond()) &&
            e.writeExpr(Expr::If) &&
            EncodeExpr(e, i.thenBranch()) &&
            (!i.hasElse() ||
             (e.writeExpr(Expr::Else) &&
              EncodeExpr(e, i.elseBranch()))) &&
            e.writeExpr(Expr::End);
 }
 
 static bool
-EncodeLoadStoreAddress(Encoder &e, const WasmAstLoadStoreAddress &address)
+EncodeLoadStoreAddress(Encoder &e, const AstLoadStoreAddress &address)
 {
     return EncodeExpr(e, address.base());
 }
 
 static bool
-EncodeLoadStoreFlags(Encoder &e, const WasmAstLoadStoreAddress &address)
+EncodeLoadStoreFlags(Encoder &e, const AstLoadStoreAddress &address)
 {
     return e.writeVarU32(address.flags()) &&
            e.writeVarU32(address.offset());
 }
 
 static bool
-EncodeLoad(Encoder& e, WasmAstLoad& l)
+EncodeLoad(Encoder& e, AstLoad& l)
 {
     return EncodeLoadStoreAddress(e, l.address()) &&
            e.writeExpr(l.expr()) &&
            EncodeLoadStoreFlags(e, l.address());
 }
 
 static bool
-EncodeStore(Encoder& e, WasmAstStore& s)
+EncodeStore(Encoder& e, AstStore& s)
 {
     return EncodeLoadStoreAddress(e, s.address()) &&
            EncodeExpr(e, s.value()) &&
            e.writeExpr(s.expr()) &&
            EncodeLoadStoreFlags(e, s.address());
 }
 
 static bool
-EncodeReturn(Encoder& e, WasmAstReturn& r)
+EncodeReturn(Encoder& e, AstReturn& r)
 {
     uint32_t arity = 0;
     if (r.maybeExpr()) {
         arity = 1;
         if (!EncodeExpr(e, *r.maybeExpr()))
            return false;
     }
 
@@ -3901,17 +3172,17 @@ EncodeReturn(Encoder& e, WasmAstReturn& 
 
     if (!e.writeVarU32(arity))
         return false;
 
     return true;
 }
 
 static bool
-EncodeBranchTable(Encoder& e, WasmAstBranchTable& bt)
+EncodeBranchTable(Encoder& e, AstBranchTable& bt)
 {
     uint32_t arity = 0;
     if (bt.maybeValue()) {
         arity = 1;
         if (!EncodeExpr(e, *bt.maybeValue()))
             return false;
     }
 
@@ -3922,90 +3193,90 @@ EncodeBranchTable(Encoder& e, WasmAstBra
         return false;
 
     if (!e.writeVarU32(arity))
         return false;
 
     if (!e.writeVarU32(bt.table().length()))
         return false;
 
-    for (const WasmRef& elem : bt.table()) {
+    for (const AstRef& elem : bt.table()) {
         if (!e.writeFixedU32(elem.index()))
             return false;
     }
 
     if (!e.writeFixedU32(bt.def().index()))
         return false;
 
     return true;
 }
 
 static bool
-EncodeExpr(Encoder& e, WasmAstExpr& expr)
+EncodeExpr(Encoder& e, AstExpr& expr)
 {
     switch (expr.kind()) {
-      case WasmAstExprKind::Nop:
+      case AstExprKind::Nop:
         return e.writeExpr(Expr::Nop);
-      case WasmAstExprKind::Unreachable:
+      case AstExprKind::Unreachable:
         return e.writeExpr(Expr::Unreachable);
-      case WasmAstExprKind::BinaryOperator:
-        return EncodeBinaryOperator(e, expr.as<WasmAstBinaryOperator>());
-      case WasmAstExprKind::Block:
-        return EncodeBlock(e, expr.as<WasmAstBlock>());
-      case WasmAstExprKind::Branch:
-        return EncodeBranch(e, expr.as<WasmAstBranch>());
-      case WasmAstExprKind::Call:
-        return EncodeCall(e, expr.as<WasmAstCall>());
-      case WasmAstExprKind::CallIndirect:
-        return EncodeCallIndirect(e, expr.as<WasmAstCallIndirect>());
-      case WasmAstExprKind::ComparisonOperator:
-        return EncodeComparisonOperator(e, expr.as<WasmAstComparisonOperator>());
-      case WasmAstExprKind::Const:
-        return EncodeConst(e, expr.as<WasmAstConst>());
-      case WasmAstExprKind::ConversionOperator:
-        return EncodeConversionOperator(e, expr.as<WasmAstConversionOperator>());
-      case WasmAstExprKind::GetLocal:
-        return EncodeGetLocal(e, expr.as<WasmAstGetLocal>());
-      case WasmAstExprKind::If:
-        return EmitIf(e, expr.as<WasmAstIf>());
-      case WasmAstExprKind::Load:
-        return EncodeLoad(e, expr.as<WasmAstLoad>());
-      case WasmAstExprKind::Return:
-        return EncodeReturn(e, expr.as<WasmAstReturn>());
-      case WasmAstExprKind::SetLocal:
-        return EncodeSetLocal(e, expr.as<WasmAstSetLocal>());
-      case WasmAstExprKind::Store:
-        return EncodeStore(e, expr.as<WasmAstStore>());
-      case WasmAstExprKind::BranchTable:
-        return EncodeBranchTable(e, expr.as<WasmAstBranchTable>());
-      case WasmAstExprKind::TernaryOperator:
-        return EncodeTernaryOperator(e, expr.as<WasmAstTernaryOperator>());
-      case WasmAstExprKind::UnaryOperator:
-        return EncodeUnaryOperator(e, expr.as<WasmAstUnaryOperator>());
+      case AstExprKind::BinaryOperator:
+        return EncodeBinaryOperator(e, expr.as<AstBinaryOperator>());
+      case AstExprKind::Block:
+        return EncodeBlock(e, expr.as<AstBlock>());
+      case AstExprKind::Branch:
+        return EncodeBranch(e, expr.as<AstBranch>());
+      case AstExprKind::Call:
+        return EncodeCall(e, expr.as<AstCall>());
+      case AstExprKind::CallIndirect:
+        return EncodeCallIndirect(e, expr.as<AstCallIndirect>());
+      case AstExprKind::ComparisonOperator:
+        return EncodeComparisonOperator(e, expr.as<AstComparisonOperator>());
+      case AstExprKind::Const:
+        return EncodeConst(e, expr.as<AstConst>());
+      case AstExprKind::ConversionOperator:
+        return EncodeConversionOperator(e, expr.as<AstConversionOperator>());
+      case AstExprKind::GetLocal:
+        return EncodeGetLocal(e, expr.as<AstGetLocal>());
+      case AstExprKind::If:
+        return EmitIf(e, expr.as<AstIf>());
+      case AstExprKind::Load:
+        return EncodeLoad(e, expr.as<AstLoad>());
+      case AstExprKind::Return:
+        return EncodeReturn(e, expr.as<AstReturn>());
+      case AstExprKind::SetLocal:
+        return EncodeSetLocal(e, expr.as<AstSetLocal>());
+      case AstExprKind::Store:
+        return EncodeStore(e, expr.as<AstStore>());
+      case AstExprKind::BranchTable:
+        return EncodeBranchTable(e, expr.as<AstBranchTable>());
+      case AstExprKind::TernaryOperator:
+        return EncodeTernaryOperator(e, expr.as<AstTernaryOperator>());
+      case AstExprKind::UnaryOperator:
+        return EncodeUnaryOperator(e, expr.as<AstUnaryOperator>());
     }
     MOZ_CRASH("Bad expr kind");
 }
 
 /*****************************************************************************/
 // wasm AST binary serialization
 
 static bool
-EncodeTypeSection(Encoder& e, WasmAstModule& module)
+EncodeTypeSection(Encoder& e, AstModule& module)
 {
     if (module.sigs().empty())
         return true;
 
     size_t offset;
     if (!e.startSection(TypeSectionId, &offset))
         return false;
 
     if (!e.writeVarU32(module.sigs().length()))
         return false;
 
-    for (WasmAstSig* sig : module.sigs()) {
+    for (AstSig* sig : module.sigs()) {
         if (!e.writeVarU32(uint32_t(TypeConstructor::Function)))
             return false;
 
         if (!e.writeVarU32(sig->args().length()))
             return false;
 
         for (ValType t : sig->args()) {
             if (!e.writeValType(t))
@@ -4021,237 +3292,237 @@ EncodeTypeSection(Encoder& e, WasmAstMod
         }
     }
 
     e.finishSection(offset);
     return true;
 }
 
 static bool
-EncodeFunctionSection(Encoder& e, WasmAstModule& module)
+EncodeFunctionSection(Encoder& e, AstModule& module)
 {
     if (module.funcs().empty())
         return true;
 
     size_t offset;
     if (!e.startSection(FunctionSectionId, &offset))
         return false;
 
     if (!e.writeVarU32(module.funcs().length()))
         return false;
 
-    for (WasmAstFunc* func : module.funcs()) {
+    for (AstFunc* func : module.funcs()) {
         if (!e.writeVarU32(func->sig().index()))
             return false;
     }
 
     e.finishSection(offset);
     return true;
 }
 
 static bool
-EncodeBytes(Encoder& e, WasmName wasmName)
+EncodeBytes(Encoder& e, AstName wasmName)
 {
     TwoByteChars range(wasmName.begin(), wasmName.length());
     UniqueChars utf8(JS::CharsToNewUTF8CharsZ(nullptr, range).c_str());
     return utf8 && e.writeBytes(utf8.get(), strlen(utf8.get()));
 }
 
 static bool
-EncodeImport(Encoder& e, WasmAstImport& imp)
+EncodeImport(Encoder& e, AstImport& imp)
 {
     if (!e.writeVarU32(imp.sigIndex()))
         return false;
 
     if (!EncodeBytes(e, imp.module()))
         return false;
 
     if (!EncodeBytes(e, imp.func()))
         return false;
 
     return true;
 }
 
 static bool
-EncodeImportSection(Encoder& e, WasmAstModule& module)
+EncodeImportSection(Encoder& e, AstModule& module)
 {
     if (module.imports().empty())
         return true;
 
     size_t offset;
     if (!e.startSection(ImportSectionId, &offset))
         return false;
 
     if (!e.writeVarU32(module.imports().length()))
         return false;
 
-    for (WasmAstImport* imp : module.imports()) {
+    for (AstImport* imp : module.imports()) {
         if (!EncodeImport(e, *imp))
             return false;
     }
 
     e.finishSection(offset);
     return true;
 }
 
 static bool
-EncodeMemorySection(Encoder& e, WasmAstModule& module)
+EncodeMemorySection(Encoder& e, AstModule& module)
 {
     if (!module.maybeMemory())
         return true;
 
     size_t offset;
     if (!e.startSection(MemorySectionId, &offset))
         return false;
 
-    WasmAstMemory& memory = *module.maybeMemory();
+    AstMemory& memory = *module.maybeMemory();
 
     if (!e.writeVarU32(memory.initialSize()))
         return false;
 
     uint32_t maxSize = memory.maxSize() ? *memory.maxSize() : memory.initialSize();
     if (!e.writeVarU32(maxSize))
         return false;
 
     uint8_t exported = 0;
-    for (WasmAstExport* exp : module.exports()) {
-        if (exp->kind() == WasmAstExportKind::Memory) {
+    for (AstExport* exp : module.exports()) {
+        if (exp->kind() == AstExportKind::Memory) {
             exported = 1;
             break;
         }
     }
 
     if (!e.writeFixedU8(exported))
         return false;
 
     e.finishSection(offset);
     return true;
 }
 
 static bool
-EncodeFunctionExport(Encoder& e, WasmAstExport& exp)
+EncodeFunctionExport(Encoder& e, AstExport& exp)
 {
     if (!e.writeVarU32(exp.func().index()))
         return false;
 
     if (!EncodeBytes(e, exp.name()))
         return false;
 
     return true;
 }
 
 static bool
-EncodeExportSection(Encoder& e, WasmAstModule& module)
+EncodeExportSection(Encoder& e, AstModule& module)
 {
     uint32_t numFuncExports = 0;
-    for (WasmAstExport* exp : module.exports()) {
-        if (exp->kind() == WasmAstExportKind::Func)
+    for (AstExport* exp : module.exports()) {
+        if (exp->kind() == AstExportKind::Func)
             numFuncExports++;
     }
 
     if (!numFuncExports)
         return true;
 
     size_t offset;
     if (!e.startSection(ExportSectionId, &offset))
         return false;
 
     if (!e.writeVarU32(numFuncExports))
         return false;
 
-    for (WasmAstExport* exp : module.exports()) {
+    for (AstExport* exp : module.exports()) {
         switch (exp->kind()) {
-          case WasmAstExportKind::Func:
+          case AstExportKind::Func:
             if (!EncodeFunctionExport(e, *exp))
                 return false;
             break;
-          case WasmAstExportKind::Memory:
+          case AstExportKind::Memory:
             continue;
         }
     }
 
     e.finishSection(offset);
     return true;
 }
 
 static bool
-EncodeTableSection(Encoder& e, WasmAstModule& module)
+EncodeTableSection(Encoder& e, AstModule& module)
 {
     if (!module.maybeTable())
         return true;
 
     size_t offset;
     if (!e.startSection(TableSectionId, &offset))
         return false;
 
     if (!e.writeVarU32(module.maybeTable()->elems().length()))
         return false;
 
-    for (WasmRef& ref : module.maybeTable()->elems()) {
+    for (AstRef& ref : module.maybeTable()->elems()) {
         if (!e.writeVarU32(ref.index()))
             return false;
     }
 
     e.finishSection(offset);
     return true;
 }
 
 static bool
-EncodeFunctionBody(Encoder& e, WasmAstFunc& func)
+EncodeFunctionBody(Encoder& e, AstFunc& func)
 {
     size_t bodySizeAt;
     if (!e.writePatchableVarU32(&bodySizeAt))
         return false;
 
     size_t beforeBody = e.currentOffset();
 
     ValTypeVector varTypes;
     if (!varTypes.appendAll(func.vars()))
         return false;
     if (!EncodeLocalEntries(e, varTypes))
         return false;
 
-    for (WasmAstExpr* expr : func.body()) {
+    for (AstExpr* expr : func.body()) {
         if (!EncodeExpr(e, *expr))
             return false;
     }
 
     e.patchVarU32(bodySizeAt, e.currentOffset() - beforeBody);
     return true;
 }
 
 static bool
-EncodeCodeSection(Encoder& e, WasmAstModule& module)
+EncodeCodeSection(Encoder& e, AstModule& module)
 {
     if (module.funcs().empty())
         return true;
 
     size_t offset;
     if (!e.startSection(CodeSectionId, &offset))
         return false;
 
     if (!e.writeVarU32(module.funcs().length()))
         return false;
 
-    for (WasmAstFunc* func : module.funcs()) {
+    for (AstFunc* func : module.funcs()) {
         if (!EncodeFunctionBody(e, *func))
             return false;
     }
 
     e.finishSection(offset);
     return true;
 }
 
 static bool
-EncodeDataSegment(Encoder& e, WasmAstSegment& segment)
+EncodeDataSegment(Encoder& e, AstSegment& segment)
 {
     if (!e.writeVarU32(segment.offset()))
         return false;
 
-    WasmName text = segment.text();
+    AstName text = segment.text();
 
     Vector<uint8_t, 0, SystemAllocPolicy> bytes;
     if (!bytes.reserve(text.length()))
         return false;
 
     const char16_t* cur = text.begin();
     const char16_t* end = text.end();
     while (cur != end) {
@@ -4262,41 +3533,41 @@ EncodeDataSegment(Encoder& e, WasmAstSeg
 
     if (!e.writeBytes(bytes.begin(), bytes.length()))
         return false;
 
     return true;
 }
 
 static bool
-EncodeDataSection(Encoder& e, WasmAstModule& module)
+EncodeDataSection(Encoder& e, AstModule& module)
 {
     if (!module.maybeMemory() || module.maybeMemory()->segments().empty())
         return true;
 
-    const WasmAstSegmentVector& segments = module.maybeMemory()->segments();
+    const AstSegmentVector& segments = module.maybeMemory()->segments();
 
     size_t offset;
     if (!e.startSection(DataSectionId, &offset))
         return false;
 
     if (!e.writeVarU32(segments.length()))
         return false;
 
-    for (WasmAstSegment* segment : segments) {
+    for (AstSegment* segment : segments) {
         if (!EncodeDataSegment(e, *segment))
             return false;
     }
 
     e.finishSection(offset);
     return true;
 }
 
 static bool
-EncodeModule(WasmAstModule& module, Bytes* bytes)
+EncodeModule(AstModule& module, Bytes* bytes)
 {
     Encoder e(*bytes);
 
     if (!e.writeFixedU32(MagicNumber))
         return false;
 
     if (!e.writeFixedU32(EncodingVersion))
         return false;
@@ -4329,17 +3600,17 @@ EncodeModule(WasmAstModule& module, Byte
 }
 
 /*****************************************************************************/
 
 bool
 wasm::TextToBinary(const char16_t* text, Bytes* bytes, UniqueChars* error)
 {
     LifoAlloc lifo(AST_LIFO_DEFAULT_CHUNK_SIZE);
-    WasmAstModule* module = ParseModule(text, lifo, error);
+    AstModule* module = ParseModule(text, lifo, error);
     if (!module)
         return false;
 
     if (!ResolveModule(lifo, module, error))
         return false;
 
     return EncodeModule(*module, bytes);
 }
--- a/js/src/jit-test/tests/wasm/totext1.js
+++ b/js/src/jit-test/tests/wasm/totext1.js
@@ -1,14 +1,11 @@
 if (!wasmIsSupported())
      quit();
 
-// FIXME: Enable this test once binary-to-text is implemented again.
-quit();
-
 load(libdir + "asserts.js");
 
 var caught = false;
 try {
     wasmBinaryToText(new Int8Array(1));
 } catch (e) {
     caught = true;
 }
--- a/js/src/moz.build
+++ b/js/src/moz.build
@@ -152,16 +152,17 @@ EXPORTS.js += [
     '../public/WeakMapPtr.h',
 ]
 
 UNIFIED_SOURCES += [
     'asmjs/AsmJS.cpp',
     'asmjs/Wasm.cpp',
     'asmjs/WasmBinary.cpp',
     'asmjs/WasmBinaryIterator.cpp',
+    'asmjs/WasmBinaryToAST.cpp',
     'asmjs/WasmBinaryToText.cpp',
     'asmjs/WasmFrameIterator.cpp',
     'asmjs/WasmGenerator.cpp',
     'asmjs/WasmIonCompile.cpp',
     'asmjs/WasmModule.cpp',
     'asmjs/WasmSignalHandlers.cpp',
     'asmjs/WasmStubs.cpp',
     'asmjs/WasmTextToBinary.cpp',