967 lines
30 KiB
C++
967 lines
30 KiB
C++
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
*
|
|
* Copyright 2015 Mozilla Foundation
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
#include "wasm/WasmCompile.h"
|
|
|
|
#include "mozilla/CheckedInt.h"
|
|
|
|
#include "jsprf.h"
|
|
|
|
#include "wasm/WasmBinaryFormat.h"
|
|
#include "wasm/WasmBinaryIterator.h"
|
|
#include "wasm/WasmGenerator.h"
|
|
#include "wasm/WasmSignalHandlers.h"
|
|
|
|
using namespace js;
|
|
using namespace js::jit;
|
|
using namespace js::wasm;
|
|
|
|
using mozilla::CheckedInt;
|
|
using mozilla::IsNaN;
|
|
|
|
namespace {
|
|
|
|
struct ValidatingPolicy : OpIterPolicy
|
|
{
|
|
// Validation is what we're all about here.
|
|
static const bool Validate = true;
|
|
};
|
|
|
|
typedef OpIter<ValidatingPolicy> ValidatingOpIter;
|
|
|
|
class FunctionDecoder
|
|
{
|
|
const ModuleGenerator& mg_;
|
|
const ValTypeVector& locals_;
|
|
ValidatingOpIter iter_;
|
|
|
|
public:
|
|
FunctionDecoder(const ModuleGenerator& mg, const ValTypeVector& locals, Decoder& d)
|
|
: mg_(mg), locals_(locals), iter_(d)
|
|
{}
|
|
const ModuleGenerator& mg() const { return mg_; }
|
|
ValidatingOpIter& iter() { return iter_; }
|
|
const ValTypeVector& locals() const { return locals_; }
|
|
|
|
bool checkHasMemory() {
|
|
if (!mg().usesMemory())
|
|
return iter().fail("can't touch memory without memory");
|
|
return true;
|
|
}
|
|
};
|
|
|
|
} // end anonymous namespace
|
|
|
|
static bool
|
|
DecodeCallArgs(FunctionDecoder& f, const Sig& sig)
|
|
{
|
|
const ValTypeVector& args = sig.args();
|
|
uint32_t numArgs = args.length();
|
|
for (size_t i = 0; i < numArgs; ++i) {
|
|
ValType argType = args[i];
|
|
if (!f.iter().readCallArg(argType, numArgs, i, nullptr))
|
|
return false;
|
|
}
|
|
|
|
return f.iter().readCallArgsEnd(numArgs);
|
|
}
|
|
|
|
static bool
|
|
DecodeCallReturn(FunctionDecoder& f, const Sig& sig)
|
|
{
|
|
return f.iter().readCallReturn(sig.ret());
|
|
}
|
|
|
|
static bool
|
|
DecodeCall(FunctionDecoder& f)
|
|
{
|
|
uint32_t funcIndex;
|
|
if (!f.iter().readCall(&funcIndex))
|
|
return false;
|
|
|
|
if (funcIndex >= f.mg().numFuncs())
|
|
return f.iter().fail("callee index out of range");
|
|
|
|
if (!f.iter().inReachableCode())
|
|
return true;
|
|
|
|
const Sig* sig = &f.mg().funcSig(funcIndex);
|
|
|
|
return DecodeCallArgs(f, *sig) &&
|
|
DecodeCallReturn(f, *sig);
|
|
}
|
|
|
|
static bool
|
|
DecodeCallIndirect(FunctionDecoder& f)
|
|
{
|
|
if (!f.mg().numTables())
|
|
return f.iter().fail("can't call_indirect without a table");
|
|
|
|
uint32_t sigIndex;
|
|
if (!f.iter().readCallIndirect(&sigIndex, nullptr))
|
|
return false;
|
|
|
|
if (sigIndex >= f.mg().numSigs())
|
|
return f.iter().fail("signature index out of range");
|
|
|
|
if (!f.iter().inReachableCode())
|
|
return true;
|
|
|
|
const Sig& sig = f.mg().sig(sigIndex);
|
|
if (!DecodeCallArgs(f, sig))
|
|
return false;
|
|
|
|
return DecodeCallReturn(f, sig);
|
|
}
|
|
|
|
static bool
|
|
DecodeBrTable(FunctionDecoder& f)
|
|
{
|
|
uint32_t tableLength;
|
|
ExprType type = ExprType::Limit;
|
|
if (!f.iter().readBrTable(&tableLength, &type, nullptr, nullptr))
|
|
return false;
|
|
|
|
uint32_t depth;
|
|
for (size_t i = 0, e = tableLength; i < e; ++i) {
|
|
if (!f.iter().readBrTableEntry(&type, nullptr, &depth))
|
|
return false;
|
|
}
|
|
|
|
// Read the default label.
|
|
return f.iter().readBrTableDefault(&type, nullptr, &depth);
|
|
}
|
|
|
|
static bool
|
|
DecodeFunctionBodyExprs(FunctionDecoder& f)
|
|
{
|
|
#define CHECK(c) if (!(c)) return false; break
|
|
|
|
while (true) {
|
|
uint16_t op;
|
|
if (!f.iter().readOp(&op))
|
|
return false;
|
|
|
|
switch (op) {
|
|
case uint16_t(Op::End):
|
|
if (!f.iter().readEnd(nullptr, nullptr, nullptr))
|
|
return false;
|
|
if (f.iter().controlStackEmpty())
|
|
return true;
|
|
break;
|
|
case uint16_t(Op::Nop):
|
|
CHECK(f.iter().readNop());
|
|
case uint16_t(Op::Drop):
|
|
CHECK(f.iter().readDrop());
|
|
case uint16_t(Op::Call):
|
|
CHECK(DecodeCall(f));
|
|
case uint16_t(Op::CallIndirect):
|
|
CHECK(DecodeCallIndirect(f));
|
|
case uint16_t(Op::I32Const):
|
|
CHECK(f.iter().readI32Const(nullptr));
|
|
case uint16_t(Op::I64Const):
|
|
CHECK(f.iter().readI64Const(nullptr));
|
|
case uint16_t(Op::F32Const):
|
|
CHECK(f.iter().readF32Const(nullptr));
|
|
case uint16_t(Op::F64Const):
|
|
CHECK(f.iter().readF64Const(nullptr));
|
|
case uint16_t(Op::GetLocal):
|
|
CHECK(f.iter().readGetLocal(f.locals(), nullptr));
|
|
case uint16_t(Op::SetLocal):
|
|
CHECK(f.iter().readSetLocal(f.locals(), nullptr, nullptr));
|
|
case uint16_t(Op::TeeLocal):
|
|
CHECK(f.iter().readTeeLocal(f.locals(), nullptr, nullptr));
|
|
case uint16_t(Op::GetGlobal):
|
|
CHECK(f.iter().readGetGlobal(f.mg().globals(), nullptr));
|
|
case uint16_t(Op::SetGlobal):
|
|
CHECK(f.iter().readSetGlobal(f.mg().globals(), nullptr, nullptr));
|
|
case uint16_t(Op::Select):
|
|
CHECK(f.iter().readSelect(nullptr, nullptr, nullptr, nullptr));
|
|
case uint16_t(Op::Block):
|
|
CHECK(f.iter().readBlock());
|
|
case uint16_t(Op::Loop):
|
|
CHECK(f.iter().readLoop());
|
|
case uint16_t(Op::If):
|
|
CHECK(f.iter().readIf(nullptr));
|
|
case uint16_t(Op::Else):
|
|
CHECK(f.iter().readElse(nullptr, nullptr));
|
|
case uint16_t(Op::I32Clz):
|
|
case uint16_t(Op::I32Ctz):
|
|
case uint16_t(Op::I32Popcnt):
|
|
CHECK(f.iter().readUnary(ValType::I32, nullptr));
|
|
case uint16_t(Op::I64Clz):
|
|
case uint16_t(Op::I64Ctz):
|
|
case uint16_t(Op::I64Popcnt):
|
|
CHECK(f.iter().readUnary(ValType::I64, nullptr));
|
|
case uint16_t(Op::F32Abs):
|
|
case uint16_t(Op::F32Neg):
|
|
case uint16_t(Op::F32Ceil):
|
|
case uint16_t(Op::F32Floor):
|
|
case uint16_t(Op::F32Sqrt):
|
|
case uint16_t(Op::F32Trunc):
|
|
case uint16_t(Op::F32Nearest):
|
|
CHECK(f.iter().readUnary(ValType::F32, nullptr));
|
|
case uint16_t(Op::F64Abs):
|
|
case uint16_t(Op::F64Neg):
|
|
case uint16_t(Op::F64Ceil):
|
|
case uint16_t(Op::F64Floor):
|
|
case uint16_t(Op::F64Sqrt):
|
|
case uint16_t(Op::F64Trunc):
|
|
case uint16_t(Op::F64Nearest):
|
|
CHECK(f.iter().readUnary(ValType::F64, nullptr));
|
|
case uint16_t(Op::I32Add):
|
|
case uint16_t(Op::I32Sub):
|
|
case uint16_t(Op::I32Mul):
|
|
case uint16_t(Op::I32DivS):
|
|
case uint16_t(Op::I32DivU):
|
|
case uint16_t(Op::I32RemS):
|
|
case uint16_t(Op::I32RemU):
|
|
case uint16_t(Op::I32And):
|
|
case uint16_t(Op::I32Or):
|
|
case uint16_t(Op::I32Xor):
|
|
case uint16_t(Op::I32Shl):
|
|
case uint16_t(Op::I32ShrS):
|
|
case uint16_t(Op::I32ShrU):
|
|
case uint16_t(Op::I32Rotl):
|
|
case uint16_t(Op::I32Rotr):
|
|
CHECK(f.iter().readBinary(ValType::I32, nullptr, nullptr));
|
|
case uint16_t(Op::I64Add):
|
|
case uint16_t(Op::I64Sub):
|
|
case uint16_t(Op::I64Mul):
|
|
case uint16_t(Op::I64DivS):
|
|
case uint16_t(Op::I64DivU):
|
|
case uint16_t(Op::I64RemS):
|
|
case uint16_t(Op::I64RemU):
|
|
case uint16_t(Op::I64And):
|
|
case uint16_t(Op::I64Or):
|
|
case uint16_t(Op::I64Xor):
|
|
case uint16_t(Op::I64Shl):
|
|
case uint16_t(Op::I64ShrS):
|
|
case uint16_t(Op::I64ShrU):
|
|
case uint16_t(Op::I64Rotl):
|
|
case uint16_t(Op::I64Rotr):
|
|
CHECK(f.iter().readBinary(ValType::I64, nullptr, nullptr));
|
|
case uint16_t(Op::F32Add):
|
|
case uint16_t(Op::F32Sub):
|
|
case uint16_t(Op::F32Mul):
|
|
case uint16_t(Op::F32Div):
|
|
case uint16_t(Op::F32Min):
|
|
case uint16_t(Op::F32Max):
|
|
case uint16_t(Op::F32CopySign):
|
|
CHECK(f.iter().readBinary(ValType::F32, nullptr, nullptr));
|
|
case uint16_t(Op::F64Add):
|
|
case uint16_t(Op::F64Sub):
|
|
case uint16_t(Op::F64Mul):
|
|
case uint16_t(Op::F64Div):
|
|
case uint16_t(Op::F64Min):
|
|
case uint16_t(Op::F64Max):
|
|
case uint16_t(Op::F64CopySign):
|
|
CHECK(f.iter().readBinary(ValType::F64, nullptr, nullptr));
|
|
case uint16_t(Op::I32Eq):
|
|
case uint16_t(Op::I32Ne):
|
|
case uint16_t(Op::I32LtS):
|
|
case uint16_t(Op::I32LtU):
|
|
case uint16_t(Op::I32LeS):
|
|
case uint16_t(Op::I32LeU):
|
|
case uint16_t(Op::I32GtS):
|
|
case uint16_t(Op::I32GtU):
|
|
case uint16_t(Op::I32GeS):
|
|
case uint16_t(Op::I32GeU):
|
|
CHECK(f.iter().readComparison(ValType::I32, nullptr, nullptr));
|
|
case uint16_t(Op::I64Eq):
|
|
case uint16_t(Op::I64Ne):
|
|
case uint16_t(Op::I64LtS):
|
|
case uint16_t(Op::I64LtU):
|
|
case uint16_t(Op::I64LeS):
|
|
case uint16_t(Op::I64LeU):
|
|
case uint16_t(Op::I64GtS):
|
|
case uint16_t(Op::I64GtU):
|
|
case uint16_t(Op::I64GeS):
|
|
case uint16_t(Op::I64GeU):
|
|
CHECK(f.iter().readComparison(ValType::I64, nullptr, nullptr));
|
|
case uint16_t(Op::F32Eq):
|
|
case uint16_t(Op::F32Ne):
|
|
case uint16_t(Op::F32Lt):
|
|
case uint16_t(Op::F32Le):
|
|
case uint16_t(Op::F32Gt):
|
|
case uint16_t(Op::F32Ge):
|
|
CHECK(f.iter().readComparison(ValType::F32, nullptr, nullptr));
|
|
case uint16_t(Op::F64Eq):
|
|
case uint16_t(Op::F64Ne):
|
|
case uint16_t(Op::F64Lt):
|
|
case uint16_t(Op::F64Le):
|
|
case uint16_t(Op::F64Gt):
|
|
case uint16_t(Op::F64Ge):
|
|
CHECK(f.iter().readComparison(ValType::F64, nullptr, nullptr));
|
|
case uint16_t(Op::I32Eqz):
|
|
CHECK(f.iter().readConversion(ValType::I32, ValType::I32, nullptr));
|
|
case uint16_t(Op::I64Eqz):
|
|
case uint16_t(Op::I32WrapI64):
|
|
CHECK(f.iter().readConversion(ValType::I64, ValType::I32, nullptr));
|
|
case uint16_t(Op::I32TruncSF32):
|
|
case uint16_t(Op::I32TruncUF32):
|
|
case uint16_t(Op::I32ReinterpretF32):
|
|
CHECK(f.iter().readConversion(ValType::F32, ValType::I32, nullptr));
|
|
case uint16_t(Op::I32TruncSF64):
|
|
case uint16_t(Op::I32TruncUF64):
|
|
CHECK(f.iter().readConversion(ValType::F64, ValType::I32, nullptr));
|
|
case uint16_t(Op::I64ExtendSI32):
|
|
case uint16_t(Op::I64ExtendUI32):
|
|
CHECK(f.iter().readConversion(ValType::I32, ValType::I64, nullptr));
|
|
case uint16_t(Op::I64TruncSF32):
|
|
case uint16_t(Op::I64TruncUF32):
|
|
CHECK(f.iter().readConversion(ValType::F32, ValType::I64, nullptr));
|
|
case uint16_t(Op::I64TruncSF64):
|
|
case uint16_t(Op::I64TruncUF64):
|
|
case uint16_t(Op::I64ReinterpretF64):
|
|
CHECK(f.iter().readConversion(ValType::F64, ValType::I64, nullptr));
|
|
case uint16_t(Op::F32ConvertSI32):
|
|
case uint16_t(Op::F32ConvertUI32):
|
|
case uint16_t(Op::F32ReinterpretI32):
|
|
CHECK(f.iter().readConversion(ValType::I32, ValType::F32, nullptr));
|
|
case uint16_t(Op::F32ConvertSI64):
|
|
case uint16_t(Op::F32ConvertUI64):
|
|
CHECK(f.iter().readConversion(ValType::I64, ValType::F32, nullptr));
|
|
case uint16_t(Op::F32DemoteF64):
|
|
CHECK(f.iter().readConversion(ValType::F64, ValType::F32, nullptr));
|
|
case uint16_t(Op::F64ConvertSI32):
|
|
case uint16_t(Op::F64ConvertUI32):
|
|
CHECK(f.iter().readConversion(ValType::I32, ValType::F64, nullptr));
|
|
case uint16_t(Op::F64ConvertSI64):
|
|
case uint16_t(Op::F64ConvertUI64):
|
|
case uint16_t(Op::F64ReinterpretI64):
|
|
CHECK(f.iter().readConversion(ValType::I64, ValType::F64, nullptr));
|
|
case uint16_t(Op::F64PromoteF32):
|
|
CHECK(f.iter().readConversion(ValType::F32, ValType::F64, nullptr));
|
|
case uint16_t(Op::I32Load8S):
|
|
case uint16_t(Op::I32Load8U):
|
|
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I32, 1, nullptr));
|
|
case uint16_t(Op::I32Load16S):
|
|
case uint16_t(Op::I32Load16U):
|
|
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I32, 2, nullptr));
|
|
case uint16_t(Op::I32Load):
|
|
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I32, 4, nullptr));
|
|
case uint16_t(Op::I64Load8S):
|
|
case uint16_t(Op::I64Load8U):
|
|
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I64, 1, nullptr));
|
|
case uint16_t(Op::I64Load16S):
|
|
case uint16_t(Op::I64Load16U):
|
|
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I64, 2, nullptr));
|
|
case uint16_t(Op::I64Load32S):
|
|
case uint16_t(Op::I64Load32U):
|
|
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I64, 4, nullptr));
|
|
case uint16_t(Op::I64Load):
|
|
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::I64, 8, nullptr));
|
|
case uint16_t(Op::F32Load):
|
|
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::F32, 4, nullptr));
|
|
case uint16_t(Op::F64Load):
|
|
CHECK(f.checkHasMemory() && f.iter().readLoad(ValType::F64, 8, nullptr));
|
|
case uint16_t(Op::I32Store8):
|
|
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I32, 1, nullptr, nullptr));
|
|
case uint16_t(Op::I32Store16):
|
|
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I32, 2, nullptr, nullptr));
|
|
case uint16_t(Op::I32Store):
|
|
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I32, 4, nullptr, nullptr));
|
|
case uint16_t(Op::I64Store8):
|
|
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I64, 1, nullptr, nullptr));
|
|
case uint16_t(Op::I64Store16):
|
|
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I64, 2, nullptr, nullptr));
|
|
case uint16_t(Op::I64Store32):
|
|
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I64, 4, nullptr, nullptr));
|
|
case uint16_t(Op::I64Store):
|
|
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::I64, 8, nullptr, nullptr));
|
|
case uint16_t(Op::F32Store):
|
|
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::F32, 4, nullptr, nullptr));
|
|
case uint16_t(Op::F64Store):
|
|
CHECK(f.checkHasMemory() && f.iter().readStore(ValType::F64, 8, nullptr, nullptr));
|
|
case uint16_t(Op::GrowMemory):
|
|
CHECK(f.checkHasMemory() && f.iter().readGrowMemory(nullptr));
|
|
case uint16_t(Op::CurrentMemory):
|
|
CHECK(f.checkHasMemory() && f.iter().readCurrentMemory());
|
|
case uint16_t(Op::Br):
|
|
CHECK(f.iter().readBr(nullptr, nullptr, nullptr));
|
|
case uint16_t(Op::BrIf):
|
|
CHECK(f.iter().readBrIf(nullptr, nullptr, nullptr, nullptr));
|
|
case uint16_t(Op::BrTable):
|
|
CHECK(DecodeBrTable(f));
|
|
case uint16_t(Op::Return):
|
|
CHECK(f.iter().readReturn(nullptr));
|
|
case uint16_t(Op::Unreachable):
|
|
CHECK(f.iter().readUnreachable());
|
|
default:
|
|
return f.iter().unrecognizedOpcode(op);
|
|
}
|
|
}
|
|
|
|
MOZ_CRASH("unreachable");
|
|
|
|
#undef CHECK
|
|
}
|
|
|
|
static bool
|
|
DecodeImportSection(Decoder& d, ModuleGeneratorData* init, ImportVector* imports)
|
|
{
|
|
Maybe<Limits> memory;
|
|
Uint32Vector funcSigIndices;
|
|
if (!DecodeImportSection(d, init->sigs, &funcSigIndices, &init->globals, &init->tables, &memory,
|
|
imports))
|
|
return false;
|
|
|
|
for (uint32_t sigIndex : funcSigIndices) {
|
|
if (!init->funcSigs.append(&init->sigs[sigIndex]))
|
|
return false;
|
|
}
|
|
|
|
// The global data offsets will be filled in by ModuleGenerator::init.
|
|
if (!init->funcImportGlobalDataOffsets.resize(init->funcSigs.length()))
|
|
return false;
|
|
|
|
if (memory) {
|
|
init->memoryUsage = MemoryUsage::Unshared;
|
|
init->minMemoryLength = memory->initial;
|
|
init->maxMemoryLength = memory->maximum;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
DecodeFunctionSection(Decoder& d, ModuleGeneratorData* init)
|
|
{
|
|
Uint32Vector funcSigIndexes;
|
|
if (!DecodeFunctionSection(d, init->sigs, init->funcSigs.length(), &funcSigIndexes))
|
|
return false;
|
|
|
|
if (!init->funcSigs.reserve(init->funcSigs.length() + funcSigIndexes.length()))
|
|
return false;
|
|
|
|
for (uint32_t sigIndex : funcSigIndexes)
|
|
init->funcSigs.infallibleAppend(&init->sigs[sigIndex]);
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
DecodeTableSection(Decoder& d, ModuleGeneratorData* init)
|
|
{
|
|
uint32_t sectionStart, sectionSize;
|
|
if (!d.startSection(SectionId::Table, §ionStart, §ionSize, "table"))
|
|
return false;
|
|
if (sectionStart == Decoder::NotStarted)
|
|
return true;
|
|
|
|
uint32_t numTables;
|
|
if (!d.readVarU32(&numTables))
|
|
return d.fail("failed to read number of tables");
|
|
|
|
if (numTables != 1)
|
|
return d.fail("the number of tables must be exactly one");
|
|
|
|
if (!DecodeTableLimits(d, &init->tables))
|
|
return false;
|
|
|
|
if (!d.finishSection(sectionStart, sectionSize, "table"))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
DecodeMemorySection(Decoder& d, ModuleGeneratorData* init)
|
|
{
|
|
bool present;
|
|
Limits memory;
|
|
if (!DecodeMemorySection(d, UsesMemory(init->memoryUsage), &memory, &present))
|
|
return false;
|
|
|
|
if (present) {
|
|
init->memoryUsage = MemoryUsage::Unshared;
|
|
init->minMemoryLength = memory.initial;
|
|
init->maxMemoryLength = memory.maximum;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
DecodeGlobalSection(Decoder& d, ModuleGeneratorData* init)
|
|
{
|
|
uint32_t sectionStart, sectionSize;
|
|
if (!d.startSection(SectionId::Global, §ionStart, §ionSize, "global"))
|
|
return false;
|
|
if (sectionStart == Decoder::NotStarted)
|
|
return true;
|
|
|
|
uint32_t numDefs;
|
|
if (!d.readVarU32(&numDefs))
|
|
return d.fail("expected number of globals");
|
|
|
|
CheckedInt<uint32_t> numGlobals = init->globals.length();
|
|
numGlobals += numDefs;
|
|
if (!numGlobals.isValid() || numGlobals.value() > MaxGlobals)
|
|
return d.fail("too many globals");
|
|
|
|
for (uint32_t i = 0; i < numDefs; i++) {
|
|
ValType type;
|
|
bool isMutable;
|
|
if (!DecodeGlobalType(d, &type, &isMutable))
|
|
return false;
|
|
|
|
InitExpr initializer;
|
|
if (!DecodeInitializerExpression(d, init->globals, type, &initializer))
|
|
return false;
|
|
|
|
if (!init->globals.append(GlobalDesc(initializer, isMutable)))
|
|
return false;
|
|
}
|
|
|
|
if (!d.finishSection(sectionStart, sectionSize, "global"))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
typedef HashSet<const char*, CStringHasher, SystemAllocPolicy> CStringSet;
|
|
|
|
static UniqueChars
|
|
DecodeExportName(Decoder& d, CStringSet* dupSet)
|
|
{
|
|
UniqueChars exportName = DecodeName(d);
|
|
if (!exportName) {
|
|
d.fail("expected valid export name");
|
|
return nullptr;
|
|
}
|
|
|
|
CStringSet::AddPtr p = dupSet->lookupForAdd(exportName.get());
|
|
if (p) {
|
|
d.fail("duplicate export");
|
|
return nullptr;
|
|
}
|
|
|
|
if (!dupSet->add(p, exportName.get()))
|
|
return nullptr;
|
|
|
|
return Move(exportName);
|
|
}
|
|
|
|
static bool
|
|
DecodeExport(Decoder& d, ModuleGenerator& mg, CStringSet* dupSet)
|
|
{
|
|
UniqueChars fieldName = DecodeExportName(d, dupSet);
|
|
if (!fieldName)
|
|
return false;
|
|
|
|
uint32_t exportKind;
|
|
if (!d.readVarU32(&exportKind))
|
|
return d.fail("failed to read export kind");
|
|
|
|
switch (DefinitionKind(exportKind)) {
|
|
case DefinitionKind::Function: {
|
|
uint32_t funcIndex;
|
|
if (!d.readVarU32(&funcIndex))
|
|
return d.fail("expected export internal index");
|
|
|
|
if (funcIndex >= mg.numFuncs())
|
|
return d.fail("exported function index out of bounds");
|
|
|
|
return mg.addFuncExport(Move(fieldName), funcIndex);
|
|
}
|
|
case DefinitionKind::Table: {
|
|
uint32_t tableIndex;
|
|
if (!d.readVarU32(&tableIndex))
|
|
return d.fail("expected table index");
|
|
|
|
if (tableIndex >= mg.tables().length())
|
|
return d.fail("exported table index out of bounds");
|
|
|
|
return mg.addTableExport(Move(fieldName));
|
|
}
|
|
case DefinitionKind::Memory: {
|
|
uint32_t memoryIndex;
|
|
if (!d.readVarU32(&memoryIndex))
|
|
return d.fail("expected memory index");
|
|
|
|
if (memoryIndex > 0 || !mg.usesMemory())
|
|
return d.fail("exported memory index out of bounds");
|
|
|
|
return mg.addMemoryExport(Move(fieldName));
|
|
}
|
|
case DefinitionKind::Global: {
|
|
uint32_t globalIndex;
|
|
if (!d.readVarU32(&globalIndex))
|
|
return d.fail("expected global index");
|
|
|
|
if (globalIndex >= mg.globals().length())
|
|
return d.fail("exported global index out of bounds");
|
|
|
|
const GlobalDesc& global = mg.globals()[globalIndex];
|
|
if (!GlobalIsJSCompatible(d, global.type(), global.isMutable()))
|
|
return false;
|
|
|
|
return mg.addGlobalExport(Move(fieldName), globalIndex);
|
|
}
|
|
default:
|
|
return d.fail("unexpected export kind");
|
|
}
|
|
|
|
MOZ_CRASH("unreachable");
|
|
}
|
|
|
|
static bool
|
|
DecodeExportSection(Decoder& d, ModuleGenerator& mg)
|
|
{
|
|
uint32_t sectionStart, sectionSize;
|
|
if (!d.startSection(SectionId::Export, §ionStart, §ionSize, "export"))
|
|
return false;
|
|
if (sectionStart == Decoder::NotStarted)
|
|
return true;
|
|
|
|
CStringSet dupSet;
|
|
if (!dupSet.init())
|
|
return false;
|
|
|
|
uint32_t numExports;
|
|
if (!d.readVarU32(&numExports))
|
|
return d.fail("failed to read number of exports");
|
|
|
|
if (numExports > MaxExports)
|
|
return d.fail("too many exports");
|
|
|
|
for (uint32_t i = 0; i < numExports; i++) {
|
|
if (!DecodeExport(d, mg, &dupSet))
|
|
return false;
|
|
}
|
|
|
|
if (!d.finishSection(sectionStart, sectionSize, "export"))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
DecodeFunctionBody(Decoder& d, ModuleGenerator& mg, uint32_t funcIndex)
|
|
{
|
|
uint32_t bodySize;
|
|
if (!d.readVarU32(&bodySize))
|
|
return d.fail("expected number of function body bytes");
|
|
|
|
if (d.bytesRemain() < bodySize)
|
|
return d.fail("function body length too big");
|
|
|
|
const uint8_t* bodyBegin = d.currentPosition();
|
|
const size_t offsetInModule = d.currentOffset();
|
|
|
|
FunctionGenerator fg;
|
|
if (!mg.startFuncDef(offsetInModule, &fg))
|
|
return false;
|
|
|
|
ValTypeVector locals;
|
|
const Sig& sig = mg.funcSig(funcIndex);
|
|
if (!locals.appendAll(sig.args()))
|
|
return false;
|
|
|
|
if (!DecodeLocalEntries(d, ModuleKind::Wasm, &locals))
|
|
return false;
|
|
|
|
FunctionDecoder f(mg, locals, d);
|
|
|
|
if (!f.iter().readFunctionStart(sig.ret()))
|
|
return false;
|
|
|
|
if (!DecodeFunctionBodyExprs(f))
|
|
return false;
|
|
|
|
if (!f.iter().readFunctionEnd())
|
|
return false;
|
|
|
|
if (d.currentPosition() != bodyBegin + bodySize)
|
|
return d.fail("function body length mismatch");
|
|
|
|
if (!fg.bytes().resize(bodySize))
|
|
return false;
|
|
|
|
memcpy(fg.bytes().begin(), bodyBegin, bodySize);
|
|
|
|
return mg.finishFuncDef(funcIndex, &fg);
|
|
}
|
|
|
|
static bool
|
|
DecodeStartSection(Decoder& d, ModuleGenerator& mg)
|
|
{
|
|
uint32_t sectionStart, sectionSize;
|
|
if (!d.startSection(SectionId::Start, §ionStart, §ionSize, "start"))
|
|
return false;
|
|
if (sectionStart == Decoder::NotStarted)
|
|
return true;
|
|
|
|
uint32_t funcIndex;
|
|
if (!d.readVarU32(&funcIndex))
|
|
return d.fail("failed to read start func index");
|
|
|
|
if (funcIndex >= mg.numFuncs())
|
|
return d.fail("unknown start function");
|
|
|
|
const Sig& sig = mg.funcSig(funcIndex);
|
|
if (!IsVoid(sig.ret()))
|
|
return d.fail("start function must not return anything");
|
|
|
|
if (sig.args().length())
|
|
return d.fail("start function must be nullary");
|
|
|
|
if (!mg.setStartFunction(funcIndex))
|
|
return false;
|
|
|
|
if (!d.finishSection(sectionStart, sectionSize, "start"))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
DecodeCodeSection(Decoder& d, ModuleGenerator& mg)
|
|
{
|
|
if (!mg.startFuncDefs())
|
|
return false;
|
|
|
|
uint32_t sectionStart, sectionSize;
|
|
if (!d.startSection(SectionId::Code, §ionStart, §ionSize, "code"))
|
|
return false;
|
|
|
|
if (sectionStart == Decoder::NotStarted) {
|
|
if (mg.numFuncDefs() != 0)
|
|
return d.fail("expected function bodies");
|
|
|
|
return mg.finishFuncDefs();
|
|
}
|
|
|
|
uint32_t numFuncDefs;
|
|
if (!d.readVarU32(&numFuncDefs))
|
|
return d.fail("expected function body count");
|
|
|
|
if (numFuncDefs != mg.numFuncDefs())
|
|
return d.fail("function body count does not match function signature count");
|
|
|
|
for (uint32_t funcDefIndex = 0; funcDefIndex < numFuncDefs; funcDefIndex++) {
|
|
if (!DecodeFunctionBody(d, mg, mg.numFuncImports() + funcDefIndex))
|
|
return false;
|
|
}
|
|
|
|
if (!d.finishSection(sectionStart, sectionSize, "code"))
|
|
return false;
|
|
|
|
return mg.finishFuncDefs();
|
|
}
|
|
|
|
static bool
|
|
DecodeElemSection(Decoder& d, ModuleGenerator& mg)
|
|
{
|
|
uint32_t sectionStart, sectionSize;
|
|
if (!d.startSection(SectionId::Elem, §ionStart, §ionSize, "elem"))
|
|
return false;
|
|
if (sectionStart == Decoder::NotStarted)
|
|
return true;
|
|
|
|
uint32_t numSegments;
|
|
if (!d.readVarU32(&numSegments))
|
|
return d.fail("failed to read number of elem segments");
|
|
|
|
if (numSegments > MaxElemSegments)
|
|
return d.fail("too many elem segments");
|
|
|
|
for (uint32_t i = 0; i < numSegments; i++) {
|
|
uint32_t tableIndex;
|
|
if (!d.readVarU32(&tableIndex))
|
|
return d.fail("expected table index");
|
|
|
|
MOZ_ASSERT(mg.tables().length() <= 1);
|
|
if (tableIndex >= mg.tables().length())
|
|
return d.fail("table index out of range");
|
|
|
|
InitExpr offset;
|
|
if (!DecodeInitializerExpression(d, mg.globals(), ValType::I32, &offset))
|
|
return false;
|
|
|
|
uint32_t numElems;
|
|
if (!d.readVarU32(&numElems))
|
|
return d.fail("expected segment size");
|
|
|
|
Uint32Vector elemFuncIndices;
|
|
if (!elemFuncIndices.resize(numElems))
|
|
return false;
|
|
|
|
for (uint32_t i = 0; i < numElems; i++) {
|
|
if (!d.readVarU32(&elemFuncIndices[i]))
|
|
return d.fail("failed to read element function index");
|
|
if (elemFuncIndices[i] >= mg.numFuncs())
|
|
return d.fail("table element out of range");
|
|
}
|
|
|
|
if (!mg.addElemSegment(offset, Move(elemFuncIndices)))
|
|
return false;
|
|
}
|
|
|
|
if (!d.finishSection(sectionStart, sectionSize, "elem"))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
static void
|
|
MaybeDecodeNameSectionBody(Decoder& d, ModuleGenerator& mg)
|
|
{
|
|
// For simplicity, ignore all failures, even OOM. Failure will simply result
|
|
// in the names section not being included for this module.
|
|
|
|
uint32_t numFuncNames;
|
|
if (!d.readVarU32(&numFuncNames))
|
|
return;
|
|
|
|
if (numFuncNames > MaxFuncs)
|
|
return;
|
|
|
|
NameInBytecodeVector funcNames;
|
|
if (!funcNames.resize(numFuncNames))
|
|
return;
|
|
|
|
for (uint32_t i = 0; i < numFuncNames; i++) {
|
|
uint32_t numBytes;
|
|
if (!d.readVarU32(&numBytes))
|
|
return;
|
|
|
|
NameInBytecode name;
|
|
name.offset = d.currentOffset();
|
|
name.length = numBytes;
|
|
funcNames[i] = name;
|
|
|
|
if (!d.readBytes(numBytes))
|
|
return;
|
|
|
|
// Skip local names for a function.
|
|
uint32_t numLocals;
|
|
if (!d.readVarU32(&numLocals))
|
|
return;
|
|
for (uint32_t j = 0; j < numLocals; j++) {
|
|
uint32_t numBytes;
|
|
if (!d.readVarU32(&numBytes))
|
|
return;
|
|
if (!d.readBytes(numBytes))
|
|
return;
|
|
}
|
|
}
|
|
|
|
mg.setFuncNames(Move(funcNames));
|
|
}
|
|
|
|
static bool
|
|
DecodeDataSection(Decoder& d, ModuleGenerator& mg)
|
|
{
|
|
DataSegmentVector dataSegments;
|
|
if (!DecodeDataSection(d, mg.usesMemory(), mg.minMemoryLength(), mg.globals(), &dataSegments))
|
|
return false;
|
|
|
|
mg.setDataSegments(Move(dataSegments));
|
|
return true;
|
|
}
|
|
|
|
static bool
|
|
DecodeNameSection(Decoder& d, ModuleGenerator& mg)
|
|
{
|
|
uint32_t sectionStart, sectionSize;
|
|
if (!d.startUserDefinedSection(NameSectionName, §ionStart, §ionSize))
|
|
return false;
|
|
if (sectionStart == Decoder::NotStarted)
|
|
return true;
|
|
|
|
// Once started, user-defined sections do not report validation errors.
|
|
|
|
MaybeDecodeNameSectionBody(d, mg);
|
|
|
|
d.finishUserDefinedSection(sectionStart, sectionSize);
|
|
return true;
|
|
}
|
|
|
|
bool
|
|
CompileArgs::initFromContext(ExclusiveContext* cx, ScriptedCaller&& scriptedCaller)
|
|
{
|
|
alwaysBaseline = cx->options().wasmAlwaysBaseline();
|
|
this->scriptedCaller = Move(scriptedCaller);
|
|
return assumptions.initBuildIdFromContext(cx);
|
|
}
|
|
|
|
SharedModule
|
|
wasm::Compile(const ShareableBytes& bytecode, const CompileArgs& args, UniqueChars* error)
|
|
{
|
|
MOZ_RELEASE_ASSERT(wasm::HaveSignalHandlers());
|
|
|
|
Decoder d(bytecode.begin(), bytecode.end(), error);
|
|
|
|
auto init = js::MakeUnique<ModuleGeneratorData>();
|
|
if (!init)
|
|
return nullptr;
|
|
|
|
if (!DecodePreamble(d))
|
|
return nullptr;
|
|
|
|
if (!DecodeTypeSection(d, &init->sigs))
|
|
return nullptr;
|
|
|
|
ImportVector imports;
|
|
if (!::DecodeImportSection(d, init.get(), &imports))
|
|
return nullptr;
|
|
|
|
if (!::DecodeFunctionSection(d, init.get()))
|
|
return nullptr;
|
|
|
|
if (!DecodeTableSection(d, init.get()))
|
|
return nullptr;
|
|
|
|
if (!::DecodeMemorySection(d, init.get()))
|
|
return nullptr;
|
|
|
|
if (!DecodeGlobalSection(d, init.get()))
|
|
return nullptr;
|
|
|
|
ModuleGenerator mg(Move(imports));
|
|
if (!mg.init(Move(init), args))
|
|
return nullptr;
|
|
|
|
if (!DecodeExportSection(d, mg))
|
|
return nullptr;
|
|
|
|
if (!DecodeStartSection(d, mg))
|
|
return nullptr;
|
|
|
|
if (!DecodeElemSection(d, mg))
|
|
return nullptr;
|
|
|
|
if (!DecodeCodeSection(d, mg))
|
|
return nullptr;
|
|
|
|
if (!::DecodeDataSection(d, mg))
|
|
return nullptr;
|
|
|
|
if (!DecodeNameSection(d, mg))
|
|
return nullptr;
|
|
|
|
if (!DecodeUnknownSections(d))
|
|
return nullptr;
|
|
|
|
MOZ_ASSERT(!*error, "unreported error in decoding");
|
|
|
|
return mg.finish(bytecode);
|
|
}
|