2068 lines
56 KiB
C++
2068 lines
56 KiB
C++
/* -*- 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, §ionStart, §ionSize, "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, §ionStart, §ionSize, "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, §ionStart, §ionSize, "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, §ionStart, §ionSize, "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, §ionStart, §ionSize, "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, §ionStart, §ionSize, "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;
|
|
}
|