summaryrefslogtreecommitdiffstats
path: root/js/src/wasm/WasmBinaryToAST.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'js/src/wasm/WasmBinaryToAST.cpp')
-rw-r--r--js/src/wasm/WasmBinaryToAST.cpp2067
1 files changed, 2067 insertions, 0 deletions
diff --git a/js/src/wasm/WasmBinaryToAST.cpp b/js/src/wasm/WasmBinaryToAST.cpp
new file mode 100644
index 000000000..3582a1176
--- /dev/null
+++ b/js/src/wasm/WasmBinaryToAST.cpp
@@ -0,0 +1,2067 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
+ * vim: set ts=8 sts=4 et sw=4 tw=99:
+ *
+ * Copyright 2016 Mozilla Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "wasm/WasmBinaryToAST.h"
+
+#include "mozilla/CheckedInt.h"
+#include "mozilla/MathAlgorithms.h"
+#include "mozilla/Sprintf.h"
+
+#include "jscntxt.h"
+
+#include "wasm/WasmBinaryFormat.h"
+#include "wasm/WasmBinaryIterator.h"
+
+using namespace js;
+using namespace js::wasm;
+
+using mozilla::CheckedInt;
+using mozilla::FloorLog2;
+
+enum AstDecodeTerminationKind
+{
+ Unknown,
+ End,
+ Else
+};
+
+struct AstDecodeStackItem
+{
+ AstExpr* expr;
+ AstDecodeTerminationKind terminationKind;
+ ExprType type;
+
+ explicit AstDecodeStackItem()
+ : expr(nullptr),
+ terminationKind(AstDecodeTerminationKind::Unknown),
+ type(ExprType::Limit)
+ {}
+ explicit AstDecodeStackItem(AstDecodeTerminationKind terminationKind, ExprType type)
+ : expr(nullptr),
+ terminationKind(terminationKind),
+ type(type)
+ {}
+ explicit AstDecodeStackItem(AstExpr* expr)
+ : expr(expr),
+ terminationKind(AstDecodeTerminationKind::Unknown),
+ type(ExprType::Limit)
+ {}
+};
+
+// We don't define a Value type because OpIter doesn't push void values, which
+// we actually need here because we're building an AST, so we maintain our own
+// stack.
+struct AstDecodePolicy : OpIterPolicy
+{
+ // Enable validation because we can be called from wasmBinaryToText on bytes
+ // which are not necessarily valid, and we shouldn't run the decoder in
+ // non-validating mode on invalid code.
+ static const bool Validate = true;
+
+ static const bool Output = true;
+};
+
+typedef OpIter<AstDecodePolicy> AstDecodeOpIter;
+
+class AstDecodeContext
+{
+ public:
+ typedef AstVector<uint32_t> AstIndexVector;
+ typedef AstVector<AstDecodeStackItem> AstDecodeStack;
+ typedef AstVector<uint32_t> DepthStack;
+
+ JSContext* cx;
+ LifoAlloc& lifo;
+ Decoder& d;
+ bool generateNames;
+
+ private:
+ AstModule& module_;
+ AstIndexVector funcDefSigs_;
+ AstDecodeOpIter *iter_;
+ AstDecodeStack exprs_;
+ DepthStack depths_;
+ const ValTypeVector* locals_;
+ GlobalDescVector globals_;
+ AstNameVector blockLabels_;
+ uint32_t currentLabelIndex_;
+ ExprType retType_;
+
+ public:
+ AstDecodeContext(JSContext* cx, LifoAlloc& lifo, Decoder& d, AstModule& module,
+ bool generateNames)
+ : cx(cx),
+ lifo(lifo),
+ d(d),
+ generateNames(generateNames),
+ module_(module),
+ funcDefSigs_(lifo),
+ iter_(nullptr),
+ exprs_(lifo),
+ depths_(lifo),
+ locals_(nullptr),
+ blockLabels_(lifo),
+ currentLabelIndex_(0),
+ retType_(ExprType::Limit)
+ {}
+
+ AstModule& module() { return module_; }
+ AstIndexVector& funcDefSigs() { return funcDefSigs_; }
+ AstDecodeOpIter& iter() { return *iter_; }
+ AstDecodeStack& exprs() { return exprs_; }
+ DepthStack& depths() { return depths_; }
+
+ AstNameVector& blockLabels() { return blockLabels_; }
+
+ ExprType retType() const { return retType_; }
+ const ValTypeVector& locals() const { return *locals_; }
+
+ bool addGlobalDesc(ValType type, bool isMutable, bool isImport) {
+ if (isImport)
+ return globals_.append(GlobalDesc(type, isMutable, globals_.length()));
+ // No need to have the precise init expr value; we just need the right
+ // type.
+ Val dummy;
+ switch (type) {
+ case ValType::I32: dummy = Val(uint32_t(0)); break;
+ case ValType::I64: dummy = Val(uint64_t(0)); break;
+ case ValType::F32: dummy = Val(RawF32(0.f)); break;
+ case ValType::F64: dummy = Val(RawF64(0.0)); break;
+ default: return false;
+ }
+ return globals_.append(GlobalDesc(InitExpr(dummy), isMutable));
+ }
+ const GlobalDescVector& globalDescs() const { return globals_; }
+
+ void popBack() { return exprs().popBack(); }
+ AstDecodeStackItem popCopy() { return exprs().popCopy(); }
+ AstDecodeStackItem& top() { return exprs().back(); }
+ MOZ_MUST_USE bool push(AstDecodeStackItem item) { return exprs().append(item); }
+
+ bool needFirst() {
+ for (size_t i = depths().back(); i < exprs().length(); ++i) {
+ if (!exprs()[i].expr->isVoid())
+ return true;
+ }
+ return false;
+ }
+
+ AstExpr* handleVoidExpr(AstExpr* voidNode)
+ {
+ MOZ_ASSERT(voidNode->isVoid());
+
+ // To attach a node that "returns void" to the middle of an AST, wrap it
+ // in a first node next to the node it should accompany.
+ if (needFirst()) {
+ AstExpr *prev = popCopy().expr;
+
+ // If the previous/A node is already a First, reuse it.
+ if (prev->kind() == AstExprKind::First) {
+ if (!prev->as<AstFirst>().exprs().append(voidNode))
+ return nullptr;
+ return prev;
+ }
+
+ AstExprVector exprs(lifo);
+ if (!exprs.append(prev))
+ return nullptr;
+ if (!exprs.append(voidNode))
+ return nullptr;
+
+ return new(lifo) AstFirst(Move(exprs));
+ }
+
+ return voidNode;
+ }
+
+ void startFunction(AstDecodeOpIter* iter, const ValTypeVector* locals, ExprType retType)
+ {
+ iter_ = iter;
+ locals_ = locals;
+ currentLabelIndex_ = 0;
+ retType_ = retType;
+ }
+ void endFunction()
+ {
+ iter_ = nullptr;
+ locals_ = nullptr;
+ retType_ = ExprType::Limit;
+ MOZ_ASSERT(blockLabels_.length() == 0);
+ }
+ uint32_t nextLabelIndex()
+ {
+ return currentLabelIndex_++;
+ }
+};
+
+static bool
+GenerateName(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(u'$'))
+ return false;
+ if (!result.append(prefix.begin(), prefix.length()))
+ return false;
+
+ uint32_t tmp = index;
+ do {
+ if (!result.append(u'0'))
+ return false;
+ tmp /= 10;
+ } while (tmp);
+
+ if (index) {
+ char16_t* p = result.end();
+ for (tmp = index; tmp; tmp /= 10)
+ *(--p) = u'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
+GenerateRef(AstDecodeContext& c, const AstName& prefix, uint32_t index, AstRef* ref)
+{
+ MOZ_ASSERT(index != AstNoIndex);
+
+ if (!c.generateNames) {
+ *ref = AstRef(index);
+ return true;
+ }
+
+ AstName name;
+ if (!GenerateName(c, prefix, index, &name))
+ return false;
+ MOZ_ASSERT(!name.empty());
+
+ *ref = AstRef(name);
+ ref->setIndex(index);
+ return true;
+}
+
+static bool
+AstDecodeCallArgs(AstDecodeContext& c, const AstSig& sig, AstExprVector* funcArgs)
+{
+ MOZ_ASSERT(c.iter().inReachableCode());
+
+ 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, nullptr))
+ return false;
+ (*funcArgs)[i] = c.exprs()[c.exprs().length() - numArgs + i].expr;
+ }
+ c.exprs().shrinkBy(numArgs);
+
+ 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
+AstDecodeDrop(AstDecodeContext& c)
+{
+ if (!c.iter().readDrop())
+ return false;
+
+ AstDecodeStackItem value = c.popCopy();
+
+ AstExpr* tmp = new(c.lifo) AstDrop(*value.expr);
+ if (!tmp)
+ return false;
+
+ tmp = c.handleVoidExpr(tmp);
+ if (!tmp)
+ return false;
+
+ if (!c.push(AstDecodeStackItem(tmp)))
+ return false;
+
+ return true;
+}
+
+static bool
+AstDecodeCall(AstDecodeContext& c)
+{
+ uint32_t funcIndex;
+ if (!c.iter().readCall(&funcIndex))
+ return false;
+
+ if (!c.iter().inReachableCode())
+ return true;
+
+ uint32_t sigIndex;
+ AstRef funcRef;
+ if (funcIndex < c.module().numFuncImports()) {
+ AstImport* import = c.module().imports()[funcIndex];
+ sigIndex = import->funcSig().index();
+ funcRef = AstRef(import->name());
+ } else {
+ uint32_t funcDefIndex = funcIndex - c.module().numFuncImports();
+ if (funcDefIndex >= c.funcDefSigs().length())
+ return c.iter().fail("callee index out of range");
+
+ sigIndex = c.funcDefSigs()[funcDefIndex];
+
+ if (!GenerateRef(c, AstName(u"func"), funcIndex, &funcRef))
+ return false;
+ }
+
+ const AstSig* sig = c.module().sigs()[sigIndex];
+
+ AstExprVector args(c.lifo);
+ if (!AstDecodeCallArgs(c, *sig, &args))
+ return false;
+
+ if (!AstDecodeCallReturn(c, *sig))
+ return false;
+
+ AstCall* call = new(c.lifo) AstCall(Op::Call, sig->ret(), funcRef, Move(args));
+ if (!call)
+ return false;
+
+ AstExpr* result = call;
+ if (IsVoid(sig->ret()))
+ result = c.handleVoidExpr(call);
+
+ if (!c.push(AstDecodeStackItem(result)))
+ return false;
+
+ return true;
+}
+
+static bool
+AstDecodeCallIndirect(AstDecodeContext& c)
+{
+ uint32_t sigIndex;
+ if (!c.iter().readCallIndirect(&sigIndex, nullptr))
+ return false;
+
+ if (!c.iter().inReachableCode())
+ return true;
+
+ if (sigIndex >= c.module().sigs().length())
+ return c.iter().fail("signature index out of range");
+
+ AstDecodeStackItem index = c.popCopy();
+
+ AstRef sigRef;
+ if (!GenerateRef(c, AstName(u"type"), sigIndex, &sigRef))
+ return false;
+
+ const AstSig* sig = c.module().sigs()[sigIndex];
+ AstExprVector args(c.lifo);
+ if (!AstDecodeCallArgs(c, *sig, &args))
+ return false;
+
+ if (!AstDecodeCallReturn(c, *sig))
+ return false;
+
+ AstCallIndirect* call = new(c.lifo) AstCallIndirect(sigRef, sig->ret(),
+ Move(args), index.expr);
+ if (!call)
+ return false;
+
+ AstExpr* result = call;
+ if (IsVoid(sig->ret()))
+ result = c.handleVoidExpr(call);
+
+ if (!c.push(AstDecodeStackItem(result)))
+ return false;
+
+ return true;
+}
+
+static bool
+AstDecodeGetBlockRef(AstDecodeContext& c, uint32_t depth, AstRef* ref)
+{
+ if (!c.generateNames || depth >= c.blockLabels().length()) {
+ // Also ignoring if it's a function body label.
+ *ref = AstRef(depth);
+ return true;
+ }
+
+ uint32_t index = c.blockLabels().length() - depth - 1;
+ if (c.blockLabels()[index].empty()) {
+ if (!GenerateName(c, AstName(u"label"), c.nextLabelIndex(), &c.blockLabels()[index]))
+ return false;
+ }
+ *ref = AstRef(c.blockLabels()[index]);
+ ref->setIndex(depth);
+ return true;
+}
+
+static bool
+AstDecodeBrTable(AstDecodeContext& c)
+{
+ uint32_t tableLength;
+ ExprType type;
+ if (!c.iter().readBrTable(&tableLength, &type, nullptr, nullptr))
+ 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, nullptr, &depth))
+ return false;
+ if (!AstDecodeGetBlockRef(c, depth, &table[i]))
+ return false;
+ }
+
+ // Read the default label.
+ if (!c.iter().readBrTableDefault(&type, nullptr, &depth))
+ return false;
+
+ AstDecodeStackItem index = c.popCopy();
+ AstDecodeStackItem value;
+ if (!IsVoid(type))
+ value = c.popCopy();
+
+ AstRef def;
+ if (!AstDecodeGetBlockRef(c, depth, &def))
+ return false;
+
+ AstBranchTable* branchTable = new(c.lifo) AstBranchTable(*index.expr,
+ def, Move(table), value.expr);
+ if (!branchTable)
+ return false;
+
+ if (!c.push(AstDecodeStackItem(branchTable)))
+ return false;
+
+ return true;
+}
+
+static bool
+AstDecodeBlock(AstDecodeContext& c, Op op)
+{
+ MOZ_ASSERT(op == Op::Block || op == Op::Loop);
+
+ if (!c.blockLabels().append(AstName()))
+ return false;
+
+ if (op == Op::Loop) {
+ if (!c.iter().readLoop())
+ return false;
+ } else {
+ if (!c.iter().readBlock())
+ return false;
+ }
+
+ if (!c.depths().append(c.exprs().length()))
+ return false;
+
+ ExprType type;
+ while (true) {
+ if (!AstDecodeExpr(c))
+ return false;
+
+ const AstDecodeStackItem& item = c.top();
+ if (!item.expr) { // Op::End was found
+ type = item.type;
+ c.popBack();
+ break;
+ }
+ }
+
+ AstExprVector exprs(c.lifo);
+ for (auto i = c.exprs().begin() + c.depths().back(), e = c.exprs().end();
+ i != e; ++i) {
+ if (!exprs.append(i->expr))
+ return false;
+ }
+ c.exprs().shrinkTo(c.depths().popCopy());
+
+ AstName name = c.blockLabels().popCopy();
+ AstBlock* block = new(c.lifo) AstBlock(op, type, name, Move(exprs));
+ if (!block)
+ return false;
+
+ AstExpr* result = block;
+ if (IsVoid(type))
+ result = c.handleVoidExpr(block);
+
+ if (!c.push(AstDecodeStackItem(result)))
+ return false;
+
+ return true;
+}
+
+static bool
+AstDecodeIf(AstDecodeContext& c)
+{
+ if (!c.iter().readIf(nullptr))
+ return false;
+
+ AstDecodeStackItem cond = c.popCopy();
+
+ bool hasElse = false;
+
+ if (!c.depths().append(c.exprs().length()))
+ return false;
+
+ if (!c.blockLabels().append(AstName()))
+ return false;
+
+ ExprType type;
+ while (true) {
+ if (!AstDecodeExpr(c))
+ return false;
+
+ const AstDecodeStackItem& item = c.top();
+ if (!item.expr) { // Op::End was found
+ hasElse = item.terminationKind == AstDecodeTerminationKind::Else;
+ type = item.type;
+ c.popBack();
+ break;
+ }
+ }
+
+ AstExprVector thenExprs(c.lifo);
+ for (auto i = c.exprs().begin() + c.depths().back(), e = c.exprs().end();
+ i != e; ++i) {
+ if (!thenExprs.append(i->expr))
+ return false;
+ }
+ c.exprs().shrinkTo(c.depths().back());
+
+ AstExprVector elseExprs(c.lifo);
+ if (hasElse) {
+ while (true) {
+ if (!AstDecodeExpr(c))
+ return false;
+
+ const AstDecodeStackItem& item = c.top();
+ if (!item.expr) { // Op::End was found
+ c.popBack();
+ break;
+ }
+ }
+
+ for (auto i = c.exprs().begin() + c.depths().back(), e = c.exprs().end();
+ i != e; ++i) {
+ if (!elseExprs.append(i->expr))
+ return false;
+ }
+ c.exprs().shrinkTo(c.depths().back());
+ }
+
+ c.depths().popBack();
+
+ AstName name = c.blockLabels().popCopy();
+
+ AstIf* if_ = new(c.lifo) AstIf(type, cond.expr, name, Move(thenExprs), Move(elseExprs));
+ if (!if_)
+ return false;
+
+ AstExpr* result = if_;
+ if (IsVoid(type))
+ result = c.handleVoidExpr(if_);
+
+ if (!c.push(AstDecodeStackItem(result)))
+ return false;
+
+ return true;
+}
+
+static bool
+AstDecodeEnd(AstDecodeContext& c)
+{
+ LabelKind kind;
+ ExprType type;
+ if (!c.iter().readEnd(&kind, &type, nullptr))
+ return false;
+
+ if (!c.push(AstDecodeStackItem(AstDecodeTerminationKind::End, type)))
+ return false;
+
+ return true;
+}
+
+static bool
+AstDecodeElse(AstDecodeContext& c)
+{
+ ExprType type;
+
+ if (!c.iter().readElse(&type, nullptr))
+ return false;
+
+ if (!c.push(AstDecodeStackItem(AstDecodeTerminationKind::Else, type)))
+ return false;
+
+ return true;
+}
+
+static bool
+AstDecodeNop(AstDecodeContext& c)
+{
+ if (!c.iter().readNop())
+ return false;
+
+ AstExpr* tmp = new(c.lifo) AstNop();
+ if (!tmp)
+ return false;
+
+ tmp = c.handleVoidExpr(tmp);
+ if (!tmp)
+ return false;
+
+ if (!c.push(AstDecodeStackItem(tmp)))
+ return false;
+
+ return true;
+}
+
+static bool
+AstDecodeUnary(AstDecodeContext& c, ValType type, Op op)
+{
+ if (!c.iter().readUnary(type, nullptr))
+ return false;
+
+ AstDecodeStackItem operand = c.popCopy();
+
+ AstUnaryOperator* unary = new(c.lifo) AstUnaryOperator(op, operand.expr);
+ if (!unary)
+ return false;
+
+ if (!c.push(AstDecodeStackItem(unary)))
+ return false;
+
+ return true;
+}
+
+static bool
+AstDecodeBinary(AstDecodeContext& c, ValType type, Op op)
+{
+ if (!c.iter().readBinary(type, nullptr, nullptr))
+ return false;
+
+ AstDecodeStackItem rhs = c.popCopy();
+ AstDecodeStackItem lhs = c.popCopy();
+
+ AstBinaryOperator* binary = new(c.lifo) AstBinaryOperator(op, lhs.expr, rhs.expr);
+ if (!binary)
+ return false;
+
+ if (!c.push(AstDecodeStackItem(binary)))
+ return false;
+
+ return true;
+}
+
+static bool
+AstDecodeSelect(AstDecodeContext& c)
+{
+ ValType type;
+ if (!c.iter().readSelect(&type, nullptr, nullptr, nullptr))
+ return false;
+
+ AstDecodeStackItem selectFalse = c.popCopy();
+ AstDecodeStackItem selectTrue = c.popCopy();
+ AstDecodeStackItem cond = c.popCopy();
+
+ AstTernaryOperator* ternary = new(c.lifo) AstTernaryOperator(Op::Select, cond.expr, selectTrue.expr, selectFalse.expr);
+ if (!ternary)
+ return false;
+
+ if (!c.push(AstDecodeStackItem(ternary)))
+ return false;
+
+ return true;
+}
+
+static bool
+AstDecodeComparison(AstDecodeContext& c, ValType type, Op op)
+{
+ if (!c.iter().readComparison(type, nullptr, nullptr))
+ return false;
+
+ AstDecodeStackItem rhs = c.popCopy();
+ AstDecodeStackItem lhs = c.popCopy();
+
+ AstComparisonOperator* comparison = new(c.lifo) AstComparisonOperator(op, lhs.expr, rhs.expr);
+ if (!comparison)
+ return false;
+
+ if (!c.push(AstDecodeStackItem(comparison)))
+ return false;
+
+ return true;
+}
+
+static bool
+AstDecodeConversion(AstDecodeContext& c, ValType fromType, ValType toType, Op op)
+{
+ if (!c.iter().readConversion(fromType, toType, nullptr))
+ return false;
+
+ AstDecodeStackItem operand = c.popCopy();
+
+ AstConversionOperator* conversion = new(c.lifo) AstConversionOperator(op, operand.expr);
+ if (!conversion)
+ return false;
+
+ if (!c.push(AstDecodeStackItem(conversion)))
+ return false;
+
+ return true;
+}
+
+static AstLoadStoreAddress
+AstDecodeLoadStoreAddress(const LinearMemoryAddress<Nothing>& addr, const AstDecodeStackItem& item)
+{
+ uint32_t flags = FloorLog2(addr.align);
+ return AstLoadStoreAddress(item.expr, flags, addr.offset);
+}
+
+static bool
+AstDecodeLoad(AstDecodeContext& c, ValType type, uint32_t byteSize, Op op)
+{
+ LinearMemoryAddress<Nothing> addr;
+ if (!c.iter().readLoad(type, byteSize, &addr))
+ return false;
+
+ AstDecodeStackItem item = c.popCopy();
+
+ AstLoad* load = new(c.lifo) AstLoad(op, AstDecodeLoadStoreAddress(addr, item));
+ if (!load)
+ return false;
+
+ if (!c.push(AstDecodeStackItem(load)))
+ return false;
+
+ return true;
+}
+
+static bool
+AstDecodeStore(AstDecodeContext& c, ValType type, uint32_t byteSize, Op op)
+{
+ LinearMemoryAddress<Nothing> addr;
+ if (!c.iter().readStore(type, byteSize, &addr, nullptr))
+ return false;
+
+ AstDecodeStackItem value = c.popCopy();
+ AstDecodeStackItem item = c.popCopy();
+
+ AstStore* store = new(c.lifo) AstStore(op, AstDecodeLoadStoreAddress(addr, item), value.expr);
+ if (!store)
+ return false;
+
+ AstExpr* wrapped = c.handleVoidExpr(store);
+ if (!wrapped)
+ return false;
+
+ if (!c.push(AstDecodeStackItem(wrapped)))
+ return false;
+
+ return true;
+}
+
+static bool
+AstDecodeCurrentMemory(AstDecodeContext& c)
+{
+ if (!c.iter().readCurrentMemory())
+ return false;
+
+ AstCurrentMemory* gm = new(c.lifo) AstCurrentMemory();
+ if (!gm)
+ return false;
+
+ if (!c.push(AstDecodeStackItem(gm)))
+ return false;
+
+ return true;
+}
+
+static bool
+AstDecodeGrowMemory(AstDecodeContext& c)
+{
+ if (!c.iter().readGrowMemory(nullptr))
+ return false;
+
+ AstDecodeStackItem operand = c.popCopy();
+
+ AstGrowMemory* gm = new(c.lifo) AstGrowMemory(operand.expr);
+ if (!gm)
+ return false;
+
+ if (!c.push(AstDecodeStackItem(gm)))
+ return false;
+
+ return true;
+}
+
+static bool
+AstDecodeBranch(AstDecodeContext& c, Op op)
+{
+ MOZ_ASSERT(op == Op::Br || op == Op::BrIf);
+
+ uint32_t depth;
+ ExprType type;
+ AstDecodeStackItem value;
+ AstDecodeStackItem cond;
+ if (op == Op::Br) {
+ if (!c.iter().readBr(&depth, &type, nullptr))
+ return false;
+ if (!IsVoid(type))
+ value = c.popCopy();
+ } else {
+ if (!c.iter().readBrIf(&depth, &type, nullptr, nullptr))
+ return false;
+ if (!IsVoid(type))
+ value = c.popCopy();
+ cond = c.popCopy();
+ }
+
+ AstRef depthRef;
+ if (!AstDecodeGetBlockRef(c, depth, &depthRef))
+ return false;
+
+ if (op == Op::Br || !value.expr)
+ type = ExprType::Void;
+ AstBranch* branch = new(c.lifo) AstBranch(op, type, cond.expr, depthRef, value.expr);
+ if (!branch)
+ return false;
+
+ if (!c.push(AstDecodeStackItem(branch)))
+ return false;
+
+ return true;
+}
+
+static bool
+AstDecodeGetLocal(AstDecodeContext& c)
+{
+ uint32_t getLocalId;
+ if (!c.iter().readGetLocal(c.locals(), &getLocalId))
+ return false;
+
+ AstRef localRef;
+ if (!GenerateRef(c, AstName(u"var"), getLocalId, &localRef))
+ return false;
+
+ AstGetLocal* getLocal = new(c.lifo) AstGetLocal(localRef);
+ if (!getLocal)
+ return false;
+
+ if (!c.push(AstDecodeStackItem(getLocal)))
+ return false;
+
+ return true;
+}
+
+static bool
+AstDecodeSetLocal(AstDecodeContext& c)
+{
+ uint32_t setLocalId;
+ if (!c.iter().readSetLocal(c.locals(), &setLocalId, nullptr))
+ return false;
+
+ AstDecodeStackItem setLocalValue = c.popCopy();
+
+ AstRef localRef;
+ if (!GenerateRef(c, AstName(u"var"), setLocalId, &localRef))
+ return false;
+
+ AstSetLocal* setLocal = new(c.lifo) AstSetLocal(localRef, *setLocalValue.expr);
+ if (!setLocal)
+ return false;
+
+ AstExpr* expr = c.handleVoidExpr(setLocal);
+ if (!expr)
+ return false;
+
+ if (!c.push(AstDecodeStackItem(expr)))
+ return false;
+
+ return true;
+}
+
+static bool
+AstDecodeTeeLocal(AstDecodeContext& c)
+{
+ uint32_t teeLocalId;
+ if (!c.iter().readTeeLocal(c.locals(), &teeLocalId, nullptr))
+ return false;
+
+ AstDecodeStackItem teeLocalValue = c.popCopy();
+
+ AstRef localRef;
+ if (!GenerateRef(c, AstName(u"var"), teeLocalId, &localRef))
+ return false;
+
+ AstTeeLocal* teeLocal = new(c.lifo) AstTeeLocal(localRef, *teeLocalValue.expr);
+ if (!teeLocal)
+ return false;
+
+ if (!c.push(AstDecodeStackItem(teeLocal)))
+ return false;
+
+ return true;
+}
+
+static bool
+AstDecodeGetGlobal(AstDecodeContext& c)
+{
+ uint32_t globalId;
+ if (!c.iter().readGetGlobal(c.globalDescs(), &globalId))
+ return false;
+
+ AstRef globalRef;
+ if (!GenerateRef(c, AstName(u"global"), globalId, &globalRef))
+ return false;
+
+ auto* getGlobal = new(c.lifo) AstGetGlobal(globalRef);
+ if (!getGlobal)
+ return false;
+
+ if (!c.push(AstDecodeStackItem(getGlobal)))
+ return false;
+
+ return true;
+}
+
+static bool
+AstDecodeSetGlobal(AstDecodeContext& c)
+{
+ uint32_t globalId;
+ if (!c.iter().readSetGlobal(c.globalDescs(), &globalId, nullptr))
+ return false;
+
+ AstDecodeStackItem value = c.popCopy();
+
+ AstRef globalRef;
+ if (!GenerateRef(c, AstName(u"global"), globalId, &globalRef))
+ return false;
+
+ auto* setGlobal = new(c.lifo) AstSetGlobal(globalRef, *value.expr);
+ if (!setGlobal)
+ return false;
+
+ AstExpr* expr = c.handleVoidExpr(setGlobal);
+ if (!expr)
+ return false;
+
+ if (!c.push(AstDecodeStackItem(expr)))
+ return false;
+
+ return true;
+}
+
+static bool
+AstDecodeReturn(AstDecodeContext& c)
+{
+ if (!c.iter().readReturn(nullptr))
+ return false;
+
+ AstDecodeStackItem result;
+ if (!IsVoid(c.retType()))
+ result = c.popCopy();
+
+ AstReturn* ret = new(c.lifo) AstReturn(result.expr);
+ if (!ret)
+ return false;
+
+ if (!c.push(AstDecodeStackItem(ret)))
+ return false;
+
+ return true;
+}
+
+static bool
+AstDecodeExpr(AstDecodeContext& c)
+{
+ uint32_t exprOffset = c.iter().currentOffset();
+ uint16_t op;
+ if (!c.iter().readOp(&op))
+ return false;
+
+ AstExpr* tmp;
+ switch (op) {
+ case uint16_t(Op::Nop):
+ if (!AstDecodeNop(c))
+ return false;
+ break;
+ case uint16_t(Op::Drop):
+ if (!AstDecodeDrop(c))
+ return false;
+ break;
+ case uint16_t(Op::Call):
+ if (!AstDecodeCall(c))
+ return false;
+ break;
+ case uint16_t(Op::CallIndirect):
+ if (!AstDecodeCallIndirect(c))
+ return false;
+ break;
+ case uint16_t(Op::I32Const):
+ int32_t i32;
+ if (!c.iter().readI32Const(&i32))
+ return false;
+ tmp = new(c.lifo) AstConst(Val((uint32_t)i32));
+ if (!tmp || !c.push(AstDecodeStackItem(tmp)))
+ return false;
+ break;
+ case uint16_t(Op::I64Const):
+ int64_t i64;
+ if (!c.iter().readI64Const(&i64))
+ return false;
+ tmp = new(c.lifo) AstConst(Val((uint64_t)i64));
+ if (!tmp || !c.push(AstDecodeStackItem(tmp)))
+ return false;
+ break;
+ case uint16_t(Op::F32Const): {
+ RawF32 f32;
+ if (!c.iter().readF32Const(&f32))
+ return false;
+ tmp = new(c.lifo) AstConst(Val(f32));
+ if (!tmp || !c.push(AstDecodeStackItem(tmp)))
+ return false;
+ break;
+ }
+ case uint16_t(Op::F64Const): {
+ RawF64 f64;
+ if (!c.iter().readF64Const(&f64))
+ return false;
+ tmp = new(c.lifo) AstConst(Val(f64));
+ if (!tmp || !c.push(AstDecodeStackItem(tmp)))
+ return false;
+ break;
+ }
+ case uint16_t(Op::GetLocal):
+ if (!AstDecodeGetLocal(c))
+ return false;
+ break;
+ case uint16_t(Op::SetLocal):
+ if (!AstDecodeSetLocal(c))
+ return false;
+ break;
+ case uint16_t(Op::TeeLocal):
+ if (!AstDecodeTeeLocal(c))
+ return false;
+ break;
+ case uint16_t(Op::Select):
+ if (!AstDecodeSelect(c))
+ return false;
+ break;
+ case uint16_t(Op::Block):
+ case uint16_t(Op::Loop):
+ if (!AstDecodeBlock(c, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::If):
+ if (!AstDecodeIf(c))
+ return false;
+ break;
+ case uint16_t(Op::Else):
+ if (!AstDecodeElse(c))
+ return false;
+ break;
+ case uint16_t(Op::End):
+ if (!AstDecodeEnd(c))
+ return false;
+ break;
+ case uint16_t(Op::I32Clz):
+ case uint16_t(Op::I32Ctz):
+ case uint16_t(Op::I32Popcnt):
+ if (!AstDecodeUnary(c, ValType::I32, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::I64Clz):
+ case uint16_t(Op::I64Ctz):
+ case uint16_t(Op::I64Popcnt):
+ if (!AstDecodeUnary(c, ValType::I64, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::F32Abs):
+ case uint16_t(Op::F32Neg):
+ case uint16_t(Op::F32Ceil):
+ case uint16_t(Op::F32Floor):
+ case uint16_t(Op::F32Sqrt):
+ case uint16_t(Op::F32Trunc):
+ case uint16_t(Op::F32Nearest):
+ if (!AstDecodeUnary(c, ValType::F32, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::F64Abs):
+ case uint16_t(Op::F64Neg):
+ case uint16_t(Op::F64Ceil):
+ case uint16_t(Op::F64Floor):
+ case uint16_t(Op::F64Sqrt):
+ case uint16_t(Op::F64Trunc):
+ case uint16_t(Op::F64Nearest):
+ if (!AstDecodeUnary(c, ValType::F64, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::I32Add):
+ case uint16_t(Op::I32Sub):
+ case uint16_t(Op::I32Mul):
+ case uint16_t(Op::I32DivS):
+ case uint16_t(Op::I32DivU):
+ case uint16_t(Op::I32RemS):
+ case uint16_t(Op::I32RemU):
+ case uint16_t(Op::I32And):
+ case uint16_t(Op::I32Or):
+ case uint16_t(Op::I32Xor):
+ case uint16_t(Op::I32Shl):
+ case uint16_t(Op::I32ShrS):
+ case uint16_t(Op::I32ShrU):
+ case uint16_t(Op::I32Rotl):
+ case uint16_t(Op::I32Rotr):
+ if (!AstDecodeBinary(c, ValType::I32, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::I64Add):
+ case uint16_t(Op::I64Sub):
+ case uint16_t(Op::I64Mul):
+ case uint16_t(Op::I64DivS):
+ case uint16_t(Op::I64DivU):
+ case uint16_t(Op::I64RemS):
+ case uint16_t(Op::I64RemU):
+ case uint16_t(Op::I64And):
+ case uint16_t(Op::I64Or):
+ case uint16_t(Op::I64Xor):
+ case uint16_t(Op::I64Shl):
+ case uint16_t(Op::I64ShrS):
+ case uint16_t(Op::I64ShrU):
+ case uint16_t(Op::I64Rotl):
+ case uint16_t(Op::I64Rotr):
+ if (!AstDecodeBinary(c, ValType::I64, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::F32Add):
+ case uint16_t(Op::F32Sub):
+ case uint16_t(Op::F32Mul):
+ case uint16_t(Op::F32Div):
+ case uint16_t(Op::F32Min):
+ case uint16_t(Op::F32Max):
+ case uint16_t(Op::F32CopySign):
+ if (!AstDecodeBinary(c, ValType::F32, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::F64Add):
+ case uint16_t(Op::F64Sub):
+ case uint16_t(Op::F64Mul):
+ case uint16_t(Op::F64Div):
+ case uint16_t(Op::F64Min):
+ case uint16_t(Op::F64Max):
+ case uint16_t(Op::F64CopySign):
+ if (!AstDecodeBinary(c, ValType::F64, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::I32Eq):
+ case uint16_t(Op::I32Ne):
+ case uint16_t(Op::I32LtS):
+ case uint16_t(Op::I32LtU):
+ case uint16_t(Op::I32LeS):
+ case uint16_t(Op::I32LeU):
+ case uint16_t(Op::I32GtS):
+ case uint16_t(Op::I32GtU):
+ case uint16_t(Op::I32GeS):
+ case uint16_t(Op::I32GeU):
+ if (!AstDecodeComparison(c, ValType::I32, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::I64Eq):
+ case uint16_t(Op::I64Ne):
+ case uint16_t(Op::I64LtS):
+ case uint16_t(Op::I64LtU):
+ case uint16_t(Op::I64LeS):
+ case uint16_t(Op::I64LeU):
+ case uint16_t(Op::I64GtS):
+ case uint16_t(Op::I64GtU):
+ case uint16_t(Op::I64GeS):
+ case uint16_t(Op::I64GeU):
+ if (!AstDecodeComparison(c, ValType::I64, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::F32Eq):
+ case uint16_t(Op::F32Ne):
+ case uint16_t(Op::F32Lt):
+ case uint16_t(Op::F32Le):
+ case uint16_t(Op::F32Gt):
+ case uint16_t(Op::F32Ge):
+ if (!AstDecodeComparison(c, ValType::F32, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::F64Eq):
+ case uint16_t(Op::F64Ne):
+ case uint16_t(Op::F64Lt):
+ case uint16_t(Op::F64Le):
+ case uint16_t(Op::F64Gt):
+ case uint16_t(Op::F64Ge):
+ if (!AstDecodeComparison(c, ValType::F64, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::I32Eqz):
+ if (!AstDecodeConversion(c, ValType::I32, ValType::I32, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::I64Eqz):
+ case uint16_t(Op::I32WrapI64):
+ if (!AstDecodeConversion(c, ValType::I64, ValType::I32, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::I32TruncSF32):
+ case uint16_t(Op::I32TruncUF32):
+ case uint16_t(Op::I32ReinterpretF32):
+ if (!AstDecodeConversion(c, ValType::F32, ValType::I32, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::I32TruncSF64):
+ case uint16_t(Op::I32TruncUF64):
+ if (!AstDecodeConversion(c, ValType::F64, ValType::I32, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::I64ExtendSI32):
+ case uint16_t(Op::I64ExtendUI32):
+ if (!AstDecodeConversion(c, ValType::I32, ValType::I64, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::I64TruncSF32):
+ case uint16_t(Op::I64TruncUF32):
+ if (!AstDecodeConversion(c, ValType::F32, ValType::I64, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::I64TruncSF64):
+ case uint16_t(Op::I64TruncUF64):
+ case uint16_t(Op::I64ReinterpretF64):
+ if (!AstDecodeConversion(c, ValType::F64, ValType::I64, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::F32ConvertSI32):
+ case uint16_t(Op::F32ConvertUI32):
+ case uint16_t(Op::F32ReinterpretI32):
+ if (!AstDecodeConversion(c, ValType::I32, ValType::F32, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::F32ConvertSI64):
+ case uint16_t(Op::F32ConvertUI64):
+ if (!AstDecodeConversion(c, ValType::I64, ValType::F32, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::F32DemoteF64):
+ if (!AstDecodeConversion(c, ValType::F64, ValType::F32, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::F64ConvertSI32):
+ case uint16_t(Op::F64ConvertUI32):
+ if (!AstDecodeConversion(c, ValType::I32, ValType::F64, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::F64ConvertSI64):
+ case uint16_t(Op::F64ConvertUI64):
+ case uint16_t(Op::F64ReinterpretI64):
+ if (!AstDecodeConversion(c, ValType::I64, ValType::F64, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::F64PromoteF32):
+ if (!AstDecodeConversion(c, ValType::F32, ValType::F64, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::I32Load8S):
+ case uint16_t(Op::I32Load8U):
+ if (!AstDecodeLoad(c, ValType::I32, 1, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::I32Load16S):
+ case uint16_t(Op::I32Load16U):
+ if (!AstDecodeLoad(c, ValType::I32, 2, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::I32Load):
+ if (!AstDecodeLoad(c, ValType::I32, 4, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::I64Load8S):
+ case uint16_t(Op::I64Load8U):
+ if (!AstDecodeLoad(c, ValType::I64, 1, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::I64Load16S):
+ case uint16_t(Op::I64Load16U):
+ if (!AstDecodeLoad(c, ValType::I64, 2, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::I64Load32S):
+ case uint16_t(Op::I64Load32U):
+ if (!AstDecodeLoad(c, ValType::I64, 4, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::I64Load):
+ if (!AstDecodeLoad(c, ValType::I64, 8, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::F32Load):
+ if (!AstDecodeLoad(c, ValType::F32, 4, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::F64Load):
+ if (!AstDecodeLoad(c, ValType::F64, 8, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::I32Store8):
+ if (!AstDecodeStore(c, ValType::I32, 1, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::I32Store16):
+ if (!AstDecodeStore(c, ValType::I32, 2, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::I32Store):
+ if (!AstDecodeStore(c, ValType::I32, 4, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::I64Store8):
+ if (!AstDecodeStore(c, ValType::I64, 1, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::I64Store16):
+ if (!AstDecodeStore(c, ValType::I64, 2, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::I64Store32):
+ if (!AstDecodeStore(c, ValType::I64, 4, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::I64Store):
+ if (!AstDecodeStore(c, ValType::I64, 8, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::F32Store):
+ if (!AstDecodeStore(c, ValType::F32, 4, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::F64Store):
+ if (!AstDecodeStore(c, ValType::F64, 8, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::CurrentMemory):
+ if (!AstDecodeCurrentMemory(c))
+ return false;
+ break;
+ case uint16_t(Op::GrowMemory):
+ if (!AstDecodeGrowMemory(c))
+ return false;
+ break;
+ case uint16_t(Op::SetGlobal):
+ if (!AstDecodeSetGlobal(c))
+ return false;
+ break;
+ case uint16_t(Op::GetGlobal):
+ if (!AstDecodeGetGlobal(c))
+ return false;
+ break;
+ case uint16_t(Op::Br):
+ case uint16_t(Op::BrIf):
+ if (!AstDecodeBranch(c, Op(op)))
+ return false;
+ break;
+ case uint16_t(Op::BrTable):
+ if (!AstDecodeBrTable(c))
+ return false;
+ break;
+ case uint16_t(Op::Return):
+ if (!AstDecodeReturn(c))
+ return false;
+ break;
+ case uint16_t(Op::Unreachable):
+ if (!c.iter().readUnreachable())
+ return false;
+ tmp = new(c.lifo) AstUnreachable();
+ if (!tmp)
+ return false;
+ if (!c.push(AstDecodeStackItem(tmp)))
+ return false;
+ break;
+ default:
+ return c.iter().unrecognizedOpcode(op);
+ }
+
+ AstExpr* lastExpr = c.top().expr;
+ if (lastExpr)
+ lastExpr->setOffset(exprOffset);
+ return true;
+}
+
+/*****************************************************************************/
+// wasm decoding and generation
+
+static bool
+AstDecodeTypeSection(AstDecodeContext& c, SigWithIdVector* sigs)
+{
+ if (!DecodeTypeSection(c.d, sigs))
+ return false;
+
+ for (size_t sigIndex = 0; sigIndex < sigs->length(); sigIndex++) {
+ const Sig& sig = (*sigs)[sigIndex];
+
+ AstValTypeVector args(c.lifo);
+ if (!args.appendAll(sig.args()))
+ return false;
+
+ AstSig sigNoName(Move(args), sig.ret());
+ AstName sigName;
+ if (!GenerateName(c, AstName(u"type"), sigIndex, &sigName))
+ return false;
+
+ AstSig* astSig = new(c.lifo) AstSig(sigName, Move(sigNoName));
+ if (!astSig || !c.module().append(astSig))
+ return false;
+ }
+
+ return true;
+}
+
+static AstName
+ToAstName(AstDecodeContext& c, const UniqueChars& name)
+{
+ size_t len = strlen(name.get());
+ char16_t* buffer = static_cast<char16_t *>(c.lifo.alloc(len * sizeof(char16_t)));
+ if (!buffer)
+ return AstName();
+
+ for (size_t i = 0; i < len; i++)
+ buffer[i] = name.get()[i];
+
+ return AstName(buffer, len);
+}
+
+static bool
+AstDecodeImportSection(AstDecodeContext& c, const SigWithIdVector& sigs)
+{
+ Uint32Vector funcSigIndices;
+ GlobalDescVector globals;
+ TableDescVector tables;
+ Maybe<Limits> memory;
+ ImportVector imports;
+ if (!DecodeImportSection(c.d, sigs, &funcSigIndices, &globals, &tables, &memory, &imports))
+ return false;
+
+ size_t lastFunc = 0;
+ size_t lastGlobal = 0;
+ size_t lastTable = 0;
+ size_t lastMemory = 0;
+
+ for (size_t importIndex = 0; importIndex < imports.length(); importIndex++) {
+ const Import& import = imports[importIndex];
+
+ AstName moduleName = ToAstName(c, import.module);
+ AstName fieldName = ToAstName(c, import.field);
+
+ AstImport* ast = nullptr;
+ switch (import.kind) {
+ case DefinitionKind::Function: {
+ AstName importName;
+ if (!GenerateName(c, AstName(u"import"), lastFunc, &importName))
+ return false;
+
+ AstRef sigRef;
+ if (!GenerateRef(c, AstName(u"type"), funcSigIndices[lastFunc], &sigRef))
+ return false;
+
+ ast = new(c.lifo) AstImport(importName, moduleName, fieldName, sigRef);
+ lastFunc++;
+ break;
+ }
+ case DefinitionKind::Global: {
+ AstName importName;
+ if (!GenerateName(c, AstName(u"global"), lastGlobal, &importName))
+ return false;
+
+ const GlobalDesc& global = globals[lastGlobal];
+ ValType type = global.type();
+ bool isMutable = global.isMutable();
+
+ if (!c.addGlobalDesc(type, isMutable, /* import */ true))
+ return false;
+
+ ast = new(c.lifo) AstImport(importName, moduleName, fieldName,
+ AstGlobal(importName, type, isMutable));
+ lastGlobal++;
+ break;
+ }
+ case DefinitionKind::Table: {
+ AstName importName;
+ if (!GenerateName(c, AstName(u"table"), lastTable, &importName))
+ return false;
+
+ ast = new(c.lifo) AstImport(importName, moduleName, fieldName, DefinitionKind::Table,
+ tables[lastTable].limits);
+ lastTable++;
+ break;
+ }
+ case DefinitionKind::Memory: {
+ AstName importName;
+ if (!GenerateName(c, AstName(u"memory"), lastMemory, &importName))
+ return false;
+
+ ast = new(c.lifo) AstImport(importName, moduleName, fieldName, DefinitionKind::Memory,
+ *memory);
+ lastMemory++;
+ break;
+ }
+ }
+
+ if (!ast || !c.module().append(ast))
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+AstDecodeFunctionSection(AstDecodeContext& c, const SigWithIdVector& sigs)
+{
+ Uint32Vector funcSigIndexes;
+ if (!DecodeFunctionSection(c.d, sigs, c.module().numFuncImports(), &funcSigIndexes))
+ return false;
+
+ return c.funcDefSigs().appendAll(funcSigIndexes);
+}
+
+static bool
+AstDecodeTableSection(AstDecodeContext& c)
+{
+ uint32_t sectionStart, sectionSize;
+ if (!c.d.startSection(SectionId::Table, &sectionStart, &sectionSize, "table"))
+ return false;
+ if (sectionStart == Decoder::NotStarted)
+ return true;
+
+ uint32_t numTables;
+ if (!c.d.readVarU32(&numTables))
+ return c.d.fail("failed to read number of tables");
+
+ if (numTables != 1)
+ return c.d.fail("the number of tables must be exactly one");
+
+ uint32_t typeConstructorValue;
+ if (!c.d.readVarU32(&typeConstructorValue))
+ return c.d.fail("expected type constructor kind");
+
+ if (typeConstructorValue != uint32_t(TypeCode::AnyFunc))
+ return c.d.fail("unknown type constructor kind");
+
+ Limits table;
+ if (!DecodeLimits(c.d, &table))
+ return false;
+
+ if (table.initial > MaxTableElems)
+ return c.d.fail("too many table elements");
+
+ if (c.module().hasTable())
+ return c.d.fail("already have a table");
+
+ AstName name;
+ if (!GenerateName(c, AstName(u"table"), c.module().tables().length(), &name))
+ return false;
+
+ if (!c.module().addTable(name, table))
+ return false;
+
+ if (!c.d.finishSection(sectionStart, sectionSize, "table"))
+ return false;
+
+ return true;
+}
+
+static bool
+AstDecodeName(AstDecodeContext& c, AstName* name)
+{
+ uint32_t length;
+ if (!c.d.readVarU32(&length))
+ return false;
+
+ const uint8_t* bytes;
+ if (!c.d.readBytes(length, &bytes))
+ return false;
+
+ 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
+AstDecodeMemorySection(AstDecodeContext& c)
+{
+ bool present;
+ Limits memory;
+ if (!DecodeMemorySection(c.d, c.module().hasMemory(), &memory, &present))
+ return false;
+
+ if (present) {
+ AstName name;
+ if (!GenerateName(c, AstName(u"memory"), c.module().memories().length(), &name))
+ return false;
+ if (!c.module().addMemory(name, memory))
+ return false;
+ }
+
+ return true;
+}
+
+static AstExpr*
+ToAstExpr(AstDecodeContext& c, const InitExpr& initExpr)
+{
+ switch (initExpr.kind()) {
+ case InitExpr::Kind::Constant: {
+ return new(c.lifo) AstConst(Val(initExpr.val()));
+ }
+ case InitExpr::Kind::GetGlobal: {
+ AstRef globalRef;
+ if (!GenerateRef(c, AstName(u"global"), initExpr.globalIndex(), &globalRef))
+ return nullptr;
+ return new(c.lifo) AstGetGlobal(globalRef);
+ }
+ }
+ return nullptr;
+}
+
+static bool
+AstDecodeInitializerExpression(AstDecodeContext& c, ValType type, AstExpr** init)
+{
+ InitExpr initExpr;
+ if (!DecodeInitializerExpression(c.d, c.globalDescs(), type, &initExpr))
+ return false;
+
+ *init = ToAstExpr(c, initExpr);
+ return !!*init;
+}
+
+static bool
+AstDecodeGlobal(AstDecodeContext& c, uint32_t i, AstGlobal* global)
+{
+ AstName name;
+ if (!GenerateName(c, AstName(u"global"), i, &name))
+ return false;
+
+ ValType type;
+ bool isMutable;
+ if (!DecodeGlobalType(c.d, &type, &isMutable))
+ return false;
+
+ AstExpr* init;
+ if (!AstDecodeInitializerExpression(c, type, &init))
+ return false;
+
+ if (!c.addGlobalDesc(type, isMutable, /* import */ false))
+ return false;
+
+ *global = AstGlobal(name, type, isMutable, Some(init));
+ return true;
+}
+
+static bool
+AstDecodeGlobalSection(AstDecodeContext& c)
+{
+ uint32_t sectionStart, sectionSize;
+ if (!c.d.startSection(SectionId::Global, &sectionStart, &sectionSize, "global"))
+ return false;
+ if (sectionStart == Decoder::NotStarted)
+ return true;
+
+ uint32_t numGlobals;
+ if (!c.d.readVarU32(&numGlobals))
+ return c.d.fail("expected number of globals");
+
+ uint32_t numImported = c.globalDescs().length();
+
+ for (uint32_t i = 0; i < numGlobals; i++) {
+ auto* global = new(c.lifo) AstGlobal;
+ if (!AstDecodeGlobal(c, i + numImported, global))
+ return false;
+ if (!c.module().append(global))
+ return false;
+ }
+
+ if (!c.d.finishSection(sectionStart, sectionSize, "global"))
+ return false;
+
+ return true;
+}
+
+static bool
+AstDecodeExport(AstDecodeContext& c, AstExport** export_)
+{
+ AstName fieldName;
+ if (!AstDecodeName(c, &fieldName))
+ return c.d.fail("expected export name");
+
+ uint32_t kindValue;
+ if (!c.d.readVarU32(&kindValue))
+ return c.d.fail("expected export kind");
+
+ uint32_t index;
+ if (!c.d.readVarU32(&index))
+ return c.d.fail("expected export internal index");
+
+ *export_ = new(c.lifo) AstExport(fieldName, DefinitionKind(kindValue), AstRef(index));
+ if (!*export_)
+ return false;
+
+ return true;
+}
+
+static bool
+AstDecodeExportSection(AstDecodeContext& c)
+{
+ uint32_t sectionStart, sectionSize;
+ if (!c.d.startSection(SectionId::Export, &sectionStart, &sectionSize, "export"))
+ return false;
+ if (sectionStart == Decoder::NotStarted)
+ return true;
+
+ uint32_t numExports;
+ if (!c.d.readVarU32(&numExports))
+ return c.d.fail("failed to read number of exports");
+
+ if (numExports > MaxExports)
+ return c.d.fail("too many exports");
+
+ for (uint32_t i = 0; i < numExports; i++) {
+ AstExport* export_ = nullptr;
+ if (!AstDecodeExport(c, &export_))
+ return false;
+ if (!c.module().append(export_))
+ return false;
+ }
+
+ if (!c.d.finishSection(sectionStart, sectionSize, "export"))
+ return false;
+
+ return true;
+}
+
+static bool
+AstDecodeFunctionBody(AstDecodeContext &c, uint32_t funcDefIndex, AstFunc** func)
+{
+ uint32_t offset = c.d.currentOffset();
+ uint32_t bodySize;
+ if (!c.d.readVarU32(&bodySize))
+ return c.d.fail("expected number of function body bytes");
+
+ if (c.d.bytesRemain() < bodySize)
+ return c.d.fail("function body length too big");
+
+ const uint8_t* bodyBegin = c.d.currentPosition();
+ const uint8_t* bodyEnd = bodyBegin + bodySize;
+
+ AstDecodeOpIter iter(c.d);
+
+ uint32_t sigIndex = c.funcDefSigs()[funcDefIndex];
+ 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, ModuleKind::Wasm, &locals))
+ return c.d.fail("failed decoding local entries");
+
+ c.startFunction(&iter, &locals, sig->ret());
+
+ AstName funcName;
+ if (!GenerateName(c, AstName(u"func"), c.module().numFuncImports() + funcDefIndex, &funcName))
+ return false;
+
+ 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 (!GenerateName(c, AstName(u"var"), i, &varName))
+ return false;
+ if (!localsNames.append(varName))
+ return false;
+ }
+
+ if (!c.iter().readFunctionStart(sig->ret()))
+ return false;
+
+ if (!c.depths().append(c.exprs().length()))
+ return false;
+
+ while (c.d.currentPosition() < bodyEnd) {
+ if (!AstDecodeExpr(c))
+ return false;
+
+ const AstDecodeStackItem& item = c.top();
+ if (!item.expr) { // Op::End was found
+ c.popBack();
+ break;
+ }
+ }
+
+ for (auto i = c.exprs().begin() + c.depths().back(), e = c.exprs().end();
+ i != e; ++i) {
+ if (!body.append(i->expr))
+ return false;
+ }
+ c.exprs().shrinkTo(c.depths().popCopy());
+
+ if (!c.iter().readFunctionEnd())
+ return false;
+
+ c.endFunction();
+
+ if (c.d.currentPosition() != bodyEnd)
+ return c.d.fail("function body length mismatch");
+
+ AstRef sigRef;
+ if (!GenerateRef(c, AstName(u"type"), sigIndex, &sigRef))
+ return false;
+
+ *func = new(c.lifo) AstFunc(funcName, sigRef, Move(vars), Move(localsNames), Move(body));
+ if (!*func)
+ return false;
+ (*func)->setOffset(offset);
+
+ return true;
+}
+
+static bool
+AstDecodeCodeSection(AstDecodeContext &c)
+{
+ uint32_t sectionStart, sectionSize;
+ if (!c.d.startSection(SectionId::Code, &sectionStart, &sectionSize, "code"))
+ return false;
+
+ if (sectionStart == Decoder::NotStarted) {
+ if (c.funcDefSigs().length() != 0)
+ return c.d.fail("expected function bodies");
+
+ return false;
+ }
+
+ uint32_t numFuncBodies;
+ if (!c.d.readVarU32(&numFuncBodies))
+ return c.d.fail("expected function body count");
+
+ if (numFuncBodies != c.funcDefSigs().length())
+ return c.d.fail("function body count does not match function signature count");
+
+ for (uint32_t funcDefIndex = 0; funcDefIndex < numFuncBodies; funcDefIndex++) {
+ AstFunc* func;
+ if (!AstDecodeFunctionBody(c, funcDefIndex, &func))
+ return false;
+ if (!c.module().append(func))
+ return false;
+ }
+
+ if (!c.d.finishSection(sectionStart, sectionSize, "code"))
+ return false;
+
+ return true;
+}
+
+// Number of bytes to display in a single fragment of a data section (per line).
+static const size_t WRAP_DATA_BYTES = 30;
+
+static bool
+AstDecodeDataSection(AstDecodeContext &c)
+{
+ DataSegmentVector segments;
+ bool hasMemory = c.module().hasMemory();
+
+ MOZ_ASSERT(c.module().memories().length() <= 1, "at most one memory in MVP");
+ uint32_t memByteLength = hasMemory ? c.module().memories()[0].limits.initial : 0;
+
+ if (!DecodeDataSection(c.d, hasMemory, memByteLength, c.globalDescs(), &segments))
+ return false;
+
+ for (DataSegment& s : segments) {
+ const uint8_t* src = c.d.begin() + s.bytecodeOffset;
+ char16_t* buffer = static_cast<char16_t*>(c.lifo.alloc(s.length * sizeof(char16_t)));
+ for (size_t i = 0; i < s.length; i++)
+ buffer[i] = src[i];
+
+ AstExpr* offset = ToAstExpr(c, s.offset);
+ if (!offset)
+ return false;
+
+ AstNameVector fragments(c.lifo);
+ for (size_t start = 0; start < s.length; start += WRAP_DATA_BYTES) {
+ AstName name(buffer + start, Min(WRAP_DATA_BYTES, s.length - start));
+ if (!fragments.append(name))
+ return false;
+ }
+
+ AstDataSegment* segment = new(c.lifo) AstDataSegment(offset, Move(fragments));
+ if (!segment || !c.module().append(segment))
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+AstDecodeElemSection(AstDecodeContext &c)
+{
+ uint32_t sectionStart, sectionSize;
+ if (!c.d.startSection(SectionId::Elem, &sectionStart, &sectionSize, "elem"))
+ return false;
+ if (sectionStart == Decoder::NotStarted)
+ return true;
+
+ uint32_t numElems;
+ if (!c.d.readVarU32(&numElems))
+ return c.d.fail("failed to read number of table elements");
+
+ for (uint32_t i = 0; i < numElems; i++) {
+ uint32_t tableIndex;
+ if (!c.d.readVarU32(&tableIndex))
+ return c.d.fail("expected table index for element");
+
+ if (tableIndex != 0)
+ return c.d.fail("non-zero table index for element");
+
+ AstExpr* offset;
+ if (!AstDecodeInitializerExpression(c, ValType::I32, &offset))
+ return false;
+
+ uint32_t count;
+ if (!c.d.readVarU32(&count))
+ return c.d.fail("expected element count");
+
+ AstRefVector elems(c.lifo);
+ if (!elems.resize(count))
+ return false;
+
+ for (uint32_t i = 0; i < count; i++) {
+ uint32_t index;
+ if (!c.d.readVarU32(&index))
+ return c.d.fail("expected element index");
+
+ elems[i] = AstRef(index);
+ }
+
+ AstElemSegment* segment = new(c.lifo) AstElemSegment(offset, Move(elems));
+ if (!segment || !c.module().append(segment))
+ return false;
+ }
+
+ if (!c.d.finishSection(sectionStart, sectionSize, "elem"))
+ return false;
+
+ return true;
+}
+
+static bool
+AstDecodeStartSection(AstDecodeContext &c)
+{
+ uint32_t sectionStart, sectionSize;
+ if (!c.d.startSection(SectionId::Start, &sectionStart, &sectionSize, "start"))
+ return false;
+ if (sectionStart == Decoder::NotStarted)
+ return true;
+
+ uint32_t funcIndex;
+ if (!c.d.readVarU32(&funcIndex))
+ return c.d.fail("failed to read start func index");
+
+ AstRef funcRef;
+ if (!GenerateRef(c, AstName(u"func"), funcIndex, &funcRef))
+ return false;
+
+ c.module().setStartFunc(AstStartFunc(funcRef));
+
+ if (!c.d.finishSection(sectionStart, sectionSize, "start"))
+ return false;
+
+ 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;
+
+ UniqueChars error;
+ Decoder d(bytes, bytes + length, &error);
+ AstDecodeContext c(cx, lifo, d, *result, true);
+
+ SigWithIdVector sigs;
+ if (!DecodePreamble(d) ||
+ !AstDecodeTypeSection(c, &sigs) ||
+ !AstDecodeImportSection(c, sigs) ||
+ !AstDecodeFunctionSection(c, sigs) ||
+ !AstDecodeTableSection(c) ||
+ !AstDecodeMemorySection(c) ||
+ !AstDecodeGlobalSection(c) ||
+ !AstDecodeExportSection(c) ||
+ !AstDecodeStartSection(c) ||
+ !AstDecodeElemSection(c) ||
+ !AstDecodeCodeSection(c) ||
+ !AstDecodeDataSection(c) ||
+ !DecodeUnknownSections(c.d))
+ {
+ if (error) {
+ JS_ReportErrorNumberASCII(c.cx, GetErrorMessage, nullptr, JSMSG_WASM_COMPILE_ERROR,
+ error.get());
+ return false;
+ }
+ ReportOutOfMemory(c.cx);
+ return false;
+ }
+
+ MOZ_ASSERT(!error, "unreported error in decoding");
+
+ *module = result;
+ return true;
+}