mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-08 00:38:39 +09:00
1321122 - Baldr: add Module.customSections.
1321122 - Baldr: add Module.customSections. As added here: https://github.com/WebAssembly/design/pull/877
This commit is contained in:
parent
d23ddf632e
commit
1f6c6d4469
13 changed files with 482 additions and 319 deletions
|
|
@ -2053,9 +2053,7 @@ class MOZ_STACK_CLASS ModuleValidator
|
|||
if (!bytes)
|
||||
return nullptr;
|
||||
|
||||
return mg_.finish(*bytes,
|
||||
DataSegmentVector(),
|
||||
NameInBytecodeVector());
|
||||
return mg_.finish(*bytes);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ static const uint32_t EncodingVersion = 0x01;
|
|||
static const uint32_t PrevEncodingVersion = 0x0d;
|
||||
|
||||
enum class SectionId {
|
||||
UserDefined = 0,
|
||||
Custom = 0,
|
||||
Type = 1,
|
||||
Import = 2,
|
||||
Function = 3,
|
||||
|
|
|
|||
|
|
@ -1825,10 +1825,10 @@ AstDecodeEnvironment(AstDecodeContext& c)
|
|||
}
|
||||
|
||||
static bool
|
||||
AstDecodeCodeSection(AstDecodeContext &c)
|
||||
AstDecodeCodeSection(AstDecodeContext& c)
|
||||
{
|
||||
uint32_t sectionStart, sectionSize;
|
||||
if (!c.d.startSection(SectionId::Code, §ionStart, §ionSize, "code"))
|
||||
if (!c.d.startSection(SectionId::Code, &c.env(), §ionStart, §ionSize, "code"))
|
||||
return false;
|
||||
|
||||
if (sectionStart == Decoder::NotStarted) {
|
||||
|
|
@ -1863,15 +1863,14 @@ AstDecodeCodeSection(AstDecodeContext &c)
|
|||
static const size_t WRAP_DATA_BYTES = 30;
|
||||
|
||||
static bool
|
||||
AstDecodeDataSection(AstDecodeContext &c)
|
||||
AstDecodeModuleTail(AstDecodeContext& c)
|
||||
{
|
||||
MOZ_ASSERT(c.module().memories().length() <= 1, "at most one memory in MVP");
|
||||
|
||||
DataSegmentVector segments;
|
||||
if (!DecodeDataSection(c.d, c.env(), &segments))
|
||||
if (!DecodeModuleTail(c.d, &c.env()))
|
||||
return false;
|
||||
|
||||
for (DataSegment& s : segments) {
|
||||
for (DataSegment& s : c.env().dataSegments) {
|
||||
char16_t* buffer = static_cast<char16_t*>(c.lifo.alloc(s.length * sizeof(char16_t)));
|
||||
if (!buffer)
|
||||
return false;
|
||||
|
|
@ -1913,8 +1912,7 @@ wasm::BinaryToAst(JSContext* cx, const uint8_t* bytes, uint32_t length,
|
|||
|
||||
if (!AstDecodeEnvironment(c) ||
|
||||
!AstDecodeCodeSection(c) ||
|
||||
!AstDecodeDataSection(c) ||
|
||||
!DecodeUnknownSections(c.d))
|
||||
!AstDecodeModuleTail(c))
|
||||
{
|
||||
if (error) {
|
||||
JS_ReportErrorNumberASCII(c.cx, GetErrorMessage, nullptr, JSMSG_WASM_COMPILE_ERROR,
|
||||
|
|
|
|||
|
|
@ -469,6 +469,7 @@ Metadata::serializedSize() const
|
|||
SerializedPodVectorSize(callSites) +
|
||||
SerializedPodVectorSize(callThunks) +
|
||||
SerializedPodVectorSize(funcNames) +
|
||||
SerializedPodVectorSize(customSections) +
|
||||
filename.serializedSize();
|
||||
}
|
||||
|
||||
|
|
@ -488,6 +489,7 @@ Metadata::serialize(uint8_t* cursor) const
|
|||
cursor = SerializePodVector(cursor, callSites);
|
||||
cursor = SerializePodVector(cursor, callThunks);
|
||||
cursor = SerializePodVector(cursor, funcNames);
|
||||
cursor = SerializePodVector(cursor, customSections);
|
||||
cursor = filename.serialize(cursor);
|
||||
return cursor;
|
||||
}
|
||||
|
|
@ -508,6 +510,7 @@ Metadata::deserialize(const uint8_t* cursor)
|
|||
(cursor = DeserializePodVector(cursor, &callSites)) &&
|
||||
(cursor = DeserializePodVector(cursor, &callThunks)) &&
|
||||
(cursor = DeserializePodVector(cursor, &funcNames)) &&
|
||||
(cursor = DeserializePodVector(cursor, &customSections)) &&
|
||||
(cursor = filename.deserialize(cursor));
|
||||
return cursor;
|
||||
}
|
||||
|
|
@ -527,6 +530,7 @@ Metadata::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
|
|||
callSites.sizeOfExcludingThis(mallocSizeOf) +
|
||||
callThunks.sizeOfExcludingThis(mallocSizeOf) +
|
||||
funcNames.sizeOfExcludingThis(mallocSizeOf) +
|
||||
customSections.sizeOfExcludingThis(mallocSizeOf) +
|
||||
filename.sizeOfExcludingThis(mallocSizeOf);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -397,12 +397,32 @@ struct NameInBytecode
|
|||
uint32_t length;
|
||||
|
||||
NameInBytecode() = default;
|
||||
NameInBytecode(uint32_t offset, uint32_t length) : offset(offset), length(length) {}
|
||||
NameInBytecode(uint32_t offset, uint32_t length)
|
||||
: offset(offset), length(length)
|
||||
{}
|
||||
};
|
||||
|
||||
typedef Vector<NameInBytecode, 0, SystemAllocPolicy> NameInBytecodeVector;
|
||||
typedef Vector<char16_t, 64> TwoByteName;
|
||||
|
||||
// CustomSection represents a custom section in the bytecode which can be
|
||||
// extracted via Module.customSections. The (offset, length) pair does not
|
||||
// include the custom section name.
|
||||
|
||||
struct CustomSection
|
||||
{
|
||||
NameInBytecode name;
|
||||
uint32_t offset;
|
||||
uint32_t length;
|
||||
|
||||
CustomSection() = default;
|
||||
CustomSection(NameInBytecode name, uint32_t offset, uint32_t length)
|
||||
: name(name), offset(offset), length(length)
|
||||
{}
|
||||
};
|
||||
|
||||
typedef Vector<CustomSection, 0, SystemAllocPolicy> CustomSectionVector;
|
||||
|
||||
// Metadata holds all the data that is needed to describe compiled wasm code
|
||||
// at runtime (as opposed to data that is only used to statically link or
|
||||
// instantiate a module).
|
||||
|
|
@ -445,6 +465,7 @@ struct Metadata : ShareableBase<Metadata>, MetadataCacheablePod
|
|||
CallSiteVector callSites;
|
||||
CallThunkVector callThunks;
|
||||
NameInBytecodeVector funcNames;
|
||||
CustomSectionVector customSections;
|
||||
CacheableChars filename;
|
||||
|
||||
bool usesMemory() const { return UsesMemory(memoryUsage); }
|
||||
|
|
|
|||
|
|
@ -64,11 +64,11 @@ DecodeFunctionBody(Decoder& d, ModuleGenerator& mg, uint32_t funcIndex)
|
|||
static bool
|
||||
DecodeCodeSection(Decoder& d, ModuleGenerator& mg)
|
||||
{
|
||||
if (!mg.startFuncDefs())
|
||||
uint32_t sectionStart, sectionSize;
|
||||
if (!d.startSection(SectionId::Code, &mg.mutableEnv(), §ionStart, §ionSize, "code"))
|
||||
return false;
|
||||
|
||||
uint32_t sectionStart, sectionSize;
|
||||
if (!d.startSection(SectionId::Code, §ionStart, §ionSize, "code"))
|
||||
if (!mg.startFuncDefs())
|
||||
return false;
|
||||
|
||||
if (sectionStart == Decoder::NotStarted) {
|
||||
|
|
@ -96,71 +96,6 @@ DecodeCodeSection(Decoder& d, ModuleGenerator& mg)
|
|||
return mg.finishFuncDefs();
|
||||
}
|
||||
|
||||
static void
|
||||
MaybeDecodeNameSectionBody(Decoder& d, NameInBytecodeVector* pfuncNames)
|
||||
{
|
||||
// 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;
|
||||
|
||||
// Use a local vector (and not pfuncNames) since it could result in a
|
||||
// partially initialized result in case of failure in the middle.
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
*pfuncNames = Move(funcNames);
|
||||
}
|
||||
|
||||
static bool
|
||||
DecodeNameSection(Decoder& d, NameInBytecodeVector* funcNames)
|
||||
{
|
||||
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, funcNames);
|
||||
|
||||
d.finishUserDefinedSection(sectionStart, sectionSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
CompileArgs::initFromContext(ExclusiveContext* cx, ScriptedCaller&& scriptedCaller)
|
||||
{
|
||||
|
|
@ -190,20 +125,10 @@ wasm::Compile(const ShareableBytes& bytecode, const CompileArgs& args, UniqueCha
|
|||
if (!DecodeCodeSection(d, mg))
|
||||
return nullptr;
|
||||
|
||||
DataSegmentVector dataSegments;
|
||||
if (!DecodeDataSection(d, mg.env(), &dataSegments))
|
||||
return nullptr;
|
||||
|
||||
NameInBytecodeVector funcNames;
|
||||
if (!DecodeNameSection(d, &funcNames))
|
||||
return nullptr;
|
||||
|
||||
if (!DecodeUnknownSections(d))
|
||||
if (!DecodeModuleTail(d, &mg.mutableEnv()))
|
||||
return nullptr;
|
||||
|
||||
MOZ_ASSERT(!*error, "unreported error in decoding");
|
||||
|
||||
return mg.finish(bytecode,
|
||||
Move(dataSegments),
|
||||
Move(funcNames));
|
||||
return mg.finish(bytecode);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -218,6 +218,15 @@ ModuleGenerator::init(UniqueModuleEnvironment env, const CompileArgs& args,
|
|||
return true;
|
||||
}
|
||||
|
||||
ModuleEnvironment&
|
||||
ModuleGenerator::mutableEnv()
|
||||
{
|
||||
// Mutation is not safe during parallel compilation.
|
||||
MOZ_ASSERT(!startedFuncDefs_ || finishedFuncDefs_);
|
||||
return *env_;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
ModuleGenerator::finishOutstandingTask()
|
||||
{
|
||||
|
|
@ -1020,8 +1029,7 @@ ModuleGenerator::initSigTableElems(uint32_t sigIndex, Uint32Vector&& elemFuncInd
|
|||
}
|
||||
|
||||
SharedModule
|
||||
ModuleGenerator::finish(const ShareableBytes& bytecode, DataSegmentVector&& dataSegments,
|
||||
NameInBytecodeVector&& funcNames)
|
||||
ModuleGenerator::finish(const ShareableBytes& bytecode)
|
||||
{
|
||||
MOZ_ASSERT(!activeFuncDef_);
|
||||
MOZ_ASSERT(finishedFuncDefs_);
|
||||
|
|
@ -1058,8 +1066,6 @@ ModuleGenerator::finish(const ShareableBytes& bytecode, DataSegmentVector&& data
|
|||
if (!metadata_->callSites.appendAll(masm_.callSites()))
|
||||
return nullptr;
|
||||
|
||||
metadata_->funcNames = Move(funcNames);
|
||||
|
||||
// The MacroAssembler has accumulated all the memory accesses during codegen.
|
||||
metadata_->memoryAccesses = masm_.extractMemoryAccesses();
|
||||
metadata_->memoryPatches = masm_.extractMemoryPatches();
|
||||
|
|
@ -1071,6 +1077,8 @@ ModuleGenerator::finish(const ShareableBytes& bytecode, DataSegmentVector&& data
|
|||
metadata_->maxMemoryLength = env_->maxMemoryLength;
|
||||
metadata_->tables = Move(env_->tables);
|
||||
metadata_->globals = Move(env_->globals);
|
||||
metadata_->funcNames = Move(env_->funcNames);
|
||||
metadata_->customSections = Move(env_->customSections);
|
||||
|
||||
// These Vectors can get large and the excess capacity can be significant,
|
||||
// so realloc them down to size.
|
||||
|
|
@ -1103,7 +1111,7 @@ ModuleGenerator::finish(const ShareableBytes& bytecode, DataSegmentVector&& data
|
|||
Move(linkData_),
|
||||
Move(env_->imports),
|
||||
Move(env_->exports),
|
||||
Move(dataSegments),
|
||||
Move(env_->dataSegments),
|
||||
Move(env_->elemSegments),
|
||||
*metadata_,
|
||||
bytecode));
|
||||
|
|
|
|||
|
|
@ -265,6 +265,7 @@ private:
|
|||
Metadata* maybeAsmJSMetadata = nullptr);
|
||||
|
||||
const ModuleEnvironment& env() const { return *env_; }
|
||||
ModuleEnvironment& mutableEnv();
|
||||
|
||||
bool isAsmJS() const { return metadata_->kind == ModuleKind::AsmJS; }
|
||||
jit::MacroAssembler& masm() { return masm_; }
|
||||
|
|
@ -303,11 +304,8 @@ private:
|
|||
[[nodiscard]] bool addGlobal(ValType type, bool isConst, uint32_t* index);
|
||||
[[nodiscard]] bool addExport(CacheableChars&& fieldChars, uint32_t funcIndex);
|
||||
|
||||
// Finish compilation, provided the list of imports and source bytecode.
|
||||
// Both these Vectors may be empty (viz., b/c asm.js does different things
|
||||
// for imports and source).
|
||||
SharedModule finish(const ShareableBytes& bytecode, DataSegmentVector&& dataSegments,
|
||||
NameInBytecodeVector&& funcNames);
|
||||
// Finish compilation of the given bytecode.
|
||||
SharedModule finish(const ShareableBytes& bytecode);
|
||||
};
|
||||
|
||||
// A FunctionGenerator encapsulates the generation of a single function body.
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@
|
|||
|
||||
#include "mozilla/CheckedInt.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
#include "mozilla/RangedPtr.h"
|
||||
|
||||
#include "jsprf.h"
|
||||
|
||||
|
|
@ -44,6 +45,7 @@ using mozilla::CheckedInt;
|
|||
using mozilla::IsNaN;
|
||||
using mozilla::IsSame;
|
||||
using mozilla::Nothing;
|
||||
using mozilla::RangedPtr;
|
||||
|
||||
bool
|
||||
wasm::HasCompilerSupport(ExclusiveContext* cx)
|
||||
|
|
@ -503,6 +505,7 @@ const JSFunctionSpec WasmModuleObject::static_methods[] =
|
|||
{
|
||||
JS_FN("imports", WasmModuleObject::imports, 1, 0),
|
||||
JS_FN("exports", WasmModuleObject::exports, 1, 0),
|
||||
JS_FN("customSections", WasmModuleObject::customSections, 2, 0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
|
|
@ -689,6 +692,58 @@ WasmModuleObject::exports(JSContext* cx, unsigned argc, Value* vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
WasmModuleObject::customSections(JSContext* cx, unsigned argc, Value* vp)
|
||||
{
|
||||
CallArgs args = CallArgsFromVp(argc, vp);
|
||||
|
||||
Module* module;
|
||||
if (!GetModuleArg(cx, args, "WebAssembly.Module.customSections", &module))
|
||||
return false;
|
||||
|
||||
Vector<char, 8> name(cx);
|
||||
{
|
||||
RootedString str(cx, ToString(cx, args.get(1)));
|
||||
if (!str)
|
||||
return false;
|
||||
|
||||
Rooted<JSFlatString*> flat(cx, str->ensureFlat(cx));
|
||||
if (!flat)
|
||||
return false;
|
||||
|
||||
if (!name.initLengthUninitialized(JS::GetDeflatedUTF8StringLength(flat)))
|
||||
return false;
|
||||
|
||||
JS::DeflateStringToUTF8Buffer(flat, RangedPtr<char>(name.begin(), name.length()));
|
||||
}
|
||||
|
||||
const uint8_t* bytecode = module->bytecode().begin();
|
||||
|
||||
AutoValueVector elems(cx);
|
||||
RootedArrayBufferObject buf(cx);
|
||||
for (const CustomSection& sec : module->metadata().customSections) {
|
||||
if (name.length() != sec.name.length)
|
||||
continue;
|
||||
if (memcmp(name.begin(), bytecode + sec.name.offset, name.length()))
|
||||
continue;
|
||||
|
||||
buf = ArrayBufferObject::create(cx, sec.length);
|
||||
if (!buf)
|
||||
return false;
|
||||
|
||||
memcpy(buf->dataPointer(), bytecode + sec.offset, sec.length);
|
||||
if (!elems.append(ObjectValue(*buf)))
|
||||
return false;
|
||||
}
|
||||
|
||||
JSObject* arr = NewDenseCopiedArray(cx, elems.length(), elems.begin());
|
||||
if (!arr)
|
||||
return false;
|
||||
|
||||
args.rval().setObject(*arr);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */ WasmModuleObject*
|
||||
WasmModuleObject::create(ExclusiveContext* cx, Module& module, HandleObject proto)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -123,6 +123,7 @@ class WasmModuleObject : public NativeObject
|
|||
static void finalize(FreeOp* fop, JSObject* obj);
|
||||
static bool imports(JSContext* cx, unsigned argc, Value* vp);
|
||||
static bool exports(JSContext* cx, unsigned argc, Value* vp);
|
||||
static bool customSections(JSContext* cx, unsigned argc, Value* vp);
|
||||
|
||||
public:
|
||||
static const unsigned RESERVED_SLOTS = 1;
|
||||
|
|
|
|||
|
|
@ -136,6 +136,7 @@ class Module : public JS::WasmModule
|
|||
const Metadata& metadata() const { return *metadata_; }
|
||||
const ImportVector& imports() const { return imports_; }
|
||||
const ExportVector& exports() const { return exports_; }
|
||||
const Bytes& bytecode() const { return bytecode_->bytes; }
|
||||
|
||||
// Instantiate this module with the given imports:
|
||||
|
||||
|
|
|
|||
|
|
@ -57,6 +57,149 @@ Decoder::fail(UniqueChars msg)
|
|||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
Decoder::startSection(SectionId id, ModuleEnvironment* env, uint32_t* sectionStart,
|
||||
uint32_t* sectionSize, const char* sectionName)
|
||||
{
|
||||
// Record state at beginning of section to allow rewinding to this point
|
||||
// if, after skipping through several custom sections, we don't find the
|
||||
// section 'id'.
|
||||
const uint8_t* const initialCur = cur_;
|
||||
const size_t initialCustomSectionsLength = env->customSections.length();
|
||||
|
||||
// Maintain a pointer to the current section that gets updated as custom
|
||||
// sections are skipped.
|
||||
const uint8_t* currentSectionStart = cur_;
|
||||
|
||||
// Only start a section with 'id', skipping any custom sections before it.
|
||||
|
||||
uint32_t idValue;
|
||||
if (!readVarU32(&idValue))
|
||||
goto rewind;
|
||||
|
||||
while (idValue != uint32_t(id)) {
|
||||
if (idValue != uint32_t(SectionId::Custom))
|
||||
goto rewind;
|
||||
|
||||
// Rewind to the beginning of the current section since this is what
|
||||
// skipCustomSection() assumes.
|
||||
cur_ = currentSectionStart;
|
||||
if (!skipCustomSection(env))
|
||||
return false;
|
||||
|
||||
// Having successfully skipped a custom section, consider the next
|
||||
// section.
|
||||
currentSectionStart = cur_;
|
||||
if (!readVarU32(&idValue))
|
||||
goto rewind;
|
||||
}
|
||||
|
||||
// Found it, now start the section.
|
||||
|
||||
if (!readVarU32(sectionSize) || bytesRemain() < *sectionSize)
|
||||
goto fail;
|
||||
|
||||
*sectionStart = cur_ - beg_;
|
||||
return true;
|
||||
|
||||
rewind:
|
||||
cur_ = initialCur;
|
||||
env->customSections.shrinkTo(initialCustomSectionsLength);
|
||||
*sectionStart = NotStarted;
|
||||
return true;
|
||||
|
||||
fail:
|
||||
return fail("failed to start %s section", sectionName);
|
||||
}
|
||||
|
||||
bool
|
||||
Decoder::finishSection(uint32_t sectionStart, uint32_t sectionSize, const char* sectionName)
|
||||
{
|
||||
if (sectionSize != (cur_ - beg_) - sectionStart)
|
||||
return fail("byte size mismatch in %s section", sectionName);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
Decoder::startCustomSection(const char* expected, size_t expectedLength, ModuleEnvironment* env,
|
||||
uint32_t* sectionStart, uint32_t* sectionSize)
|
||||
{
|
||||
// Record state at beginning of section to allow rewinding to this point
|
||||
// if, after skipping through several custom sections, we don't find the
|
||||
// section 'id'.
|
||||
const uint8_t* const initialCur = cur_;
|
||||
const size_t initialCustomSectionsLength = env->customSections.length();
|
||||
|
||||
while (true) {
|
||||
// Try to start a custom section. If we can't, rewind to the beginning
|
||||
// since we may have skipped several custom sections already looking for
|
||||
// 'expected'.
|
||||
if (!startSection(SectionId::Custom, env, sectionStart, sectionSize, "custom"))
|
||||
return false;
|
||||
if (*sectionStart == NotStarted)
|
||||
goto rewind;
|
||||
|
||||
NameInBytecode name;
|
||||
if (!readVarU32(&name.length) || name.length > bytesRemain())
|
||||
goto fail;
|
||||
|
||||
name.offset = currentOffset();
|
||||
uint32_t payloadOffset = name.offset + name.length;
|
||||
uint32_t payloadEnd = *sectionStart + *sectionSize;
|
||||
if (payloadOffset > payloadEnd)
|
||||
goto fail;
|
||||
|
||||
// Now that we have a valid custom section, record its offsets in the
|
||||
// metadata which can be queried by the user via Module.customSections.
|
||||
// Note: after an entry is appended, it may be popped if this loop or
|
||||
// the loop in startSection needs to rewind.
|
||||
if (!env->customSections.emplaceBack(name, payloadOffset, payloadEnd - payloadOffset))
|
||||
return false;
|
||||
|
||||
// If this is the expected custom section, we're done.
|
||||
if (!expected || (expectedLength == name.length && !memcmp(cur_, expected, name.length))) {
|
||||
cur_ += name.length;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, blindly skip the custom section and keep looking.
|
||||
finishCustomSection(*sectionStart, *sectionSize);
|
||||
}
|
||||
MOZ_CRASH("unreachable");
|
||||
|
||||
rewind:
|
||||
cur_ = initialCur;
|
||||
env->customSections.shrinkTo(initialCustomSectionsLength);
|
||||
return true;
|
||||
|
||||
fail:
|
||||
return fail("failed to start custom section");
|
||||
}
|
||||
|
||||
void
|
||||
Decoder::finishCustomSection(uint32_t sectionStart, uint32_t sectionSize)
|
||||
{
|
||||
MOZ_ASSERT(cur_ >= beg_);
|
||||
MOZ_ASSERT(cur_ <= end_);
|
||||
cur_ = (beg_ + sectionStart) + sectionSize;
|
||||
MOZ_ASSERT(cur_ <= end_);
|
||||
clearError();
|
||||
}
|
||||
|
||||
bool
|
||||
Decoder::skipCustomSection(ModuleEnvironment* env)
|
||||
{
|
||||
uint32_t sectionStart, sectionSize;
|
||||
if (!startCustomSection(nullptr, 0, env, §ionStart, §ionSize))
|
||||
return false;
|
||||
if (sectionStart == NotStarted)
|
||||
return fail("expected custom section");
|
||||
|
||||
finishCustomSection(sectionStart, sectionSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// Misc helpers.
|
||||
|
||||
bool
|
||||
|
|
@ -558,10 +701,10 @@ DecodePreamble(Decoder& d)
|
|||
}
|
||||
|
||||
static bool
|
||||
DecodeTypeSection(Decoder& d, SigWithIdVector* sigs)
|
||||
DecodeTypeSection(Decoder& d, ModuleEnvironment* env)
|
||||
{
|
||||
uint32_t sectionStart, sectionSize;
|
||||
if (!d.startSection(SectionId::Type, §ionStart, §ionSize, "type"))
|
||||
if (!d.startSection(SectionId::Type, env, §ionStart, §ionSize, "type"))
|
||||
return false;
|
||||
if (sectionStart == Decoder::NotStarted)
|
||||
return true;
|
||||
|
|
@ -573,7 +716,7 @@ DecodeTypeSection(Decoder& d, SigWithIdVector* sigs)
|
|||
if (numSigs > MaxSigs)
|
||||
return d.fail("too many signatures");
|
||||
|
||||
if (!sigs->resize(numSigs))
|
||||
if (!env->sigs.resize(numSigs))
|
||||
return false;
|
||||
|
||||
for (uint32_t sigIndex = 0; sigIndex < numSigs; sigIndex++) {
|
||||
|
|
@ -614,7 +757,7 @@ DecodeTypeSection(Decoder& d, SigWithIdVector* sigs)
|
|||
result = ToExprType(type);
|
||||
}
|
||||
|
||||
(*sigs)[sigIndex] = Sig(Move(args), result);
|
||||
env->sigs[sigIndex] = Sig(Move(args), result);
|
||||
}
|
||||
|
||||
if (!d.finishSection(sectionStart, sectionSize, "type"))
|
||||
|
|
@ -839,7 +982,7 @@ static bool
|
|||
DecodeImportSection(Decoder& d, ModuleEnvironment* env)
|
||||
{
|
||||
uint32_t sectionStart, sectionSize;
|
||||
if (!d.startSection(SectionId::Import, §ionStart, §ionSize, "import"))
|
||||
if (!d.startSection(SectionId::Import, env, §ionStart, §ionSize, "import"))
|
||||
return false;
|
||||
if (sectionStart == Decoder::NotStarted)
|
||||
return true;
|
||||
|
|
@ -870,7 +1013,7 @@ static bool
|
|||
DecodeFunctionSection(Decoder& d, ModuleEnvironment* env)
|
||||
{
|
||||
uint32_t sectionStart, sectionSize;
|
||||
if (!d.startSection(SectionId::Function, §ionStart, §ionSize, "function"))
|
||||
if (!d.startSection(SectionId::Function, env, §ionStart, §ionSize, "function"))
|
||||
return false;
|
||||
if (sectionStart == Decoder::NotStarted)
|
||||
return true;
|
||||
|
|
@ -901,10 +1044,10 @@ DecodeFunctionSection(Decoder& d, ModuleEnvironment* env)
|
|||
}
|
||||
|
||||
static bool
|
||||
DecodeTableSection(Decoder& d, TableDescVector* tables)
|
||||
DecodeTableSection(Decoder& d, ModuleEnvironment* env)
|
||||
{
|
||||
uint32_t sectionStart, sectionSize;
|
||||
if (!d.startSection(SectionId::Table, §ionStart, §ionSize, "table"))
|
||||
if (!d.startSection(SectionId::Table, env, §ionStart, §ionSize, "table"))
|
||||
return false;
|
||||
if (sectionStart == Decoder::NotStarted)
|
||||
return true;
|
||||
|
|
@ -916,7 +1059,7 @@ DecodeTableSection(Decoder& d, TableDescVector* tables)
|
|||
if (numTables != 1)
|
||||
return d.fail("the number of tables must be exactly one");
|
||||
|
||||
if (!DecodeTableLimits(d, tables))
|
||||
if (!DecodeTableLimits(d, &env->tables))
|
||||
return false;
|
||||
|
||||
if (!d.finishSection(sectionStart, sectionSize, "table"))
|
||||
|
|
@ -929,7 +1072,7 @@ static bool
|
|||
DecodeMemorySection(Decoder& d, ModuleEnvironment* env)
|
||||
{
|
||||
uint32_t sectionStart, sectionSize;
|
||||
if (!d.startSection(SectionId::Memory, §ionStart, §ionSize, "memory"))
|
||||
if (!d.startSection(SectionId::Memory, env, §ionStart, §ionSize, "memory"))
|
||||
return false;
|
||||
if (sectionStart == Decoder::NotStarted)
|
||||
return true;
|
||||
|
|
@ -1014,10 +1157,10 @@ DecodeInitializerExpression(Decoder& d, const GlobalDescVector& globals, ValType
|
|||
}
|
||||
|
||||
static bool
|
||||
DecodeGlobalSection(Decoder& d, GlobalDescVector* globals)
|
||||
DecodeGlobalSection(Decoder& d, ModuleEnvironment* env)
|
||||
{
|
||||
uint32_t sectionStart, sectionSize;
|
||||
if (!d.startSection(SectionId::Global, §ionStart, §ionSize, "global"))
|
||||
if (!d.startSection(SectionId::Global, env, §ionStart, §ionSize, "global"))
|
||||
return false;
|
||||
if (sectionStart == Decoder::NotStarted)
|
||||
return true;
|
||||
|
|
@ -1026,12 +1169,12 @@ DecodeGlobalSection(Decoder& d, GlobalDescVector* globals)
|
|||
if (!d.readVarU32(&numDefs))
|
||||
return d.fail("expected number of globals");
|
||||
|
||||
CheckedInt<uint32_t> numGlobals = globals->length();
|
||||
CheckedInt<uint32_t> numGlobals = env->globals.length();
|
||||
numGlobals += numDefs;
|
||||
if (!numGlobals.isValid() || numGlobals.value() > MaxGlobals)
|
||||
return d.fail("too many globals");
|
||||
|
||||
if (!globals->reserve(numGlobals.value()))
|
||||
if (!env->globals.reserve(numGlobals.value()))
|
||||
return false;
|
||||
|
||||
for (uint32_t i = 0; i < numDefs; i++) {
|
||||
|
|
@ -1041,10 +1184,10 @@ DecodeGlobalSection(Decoder& d, GlobalDescVector* globals)
|
|||
return false;
|
||||
|
||||
InitExpr initializer;
|
||||
if (!DecodeInitializerExpression(d, *globals, type, &initializer))
|
||||
if (!DecodeInitializerExpression(d, env->globals, type, &initializer))
|
||||
return false;
|
||||
|
||||
globals->infallibleAppend(GlobalDesc(initializer, isMutable));
|
||||
env->globals.infallibleAppend(GlobalDesc(initializer, isMutable));
|
||||
}
|
||||
|
||||
if (!d.finishSection(sectionStart, sectionSize, "global"))
|
||||
|
|
@ -1146,7 +1289,7 @@ static bool
|
|||
DecodeExportSection(Decoder& d, ModuleEnvironment* env)
|
||||
{
|
||||
uint32_t sectionStart, sectionSize;
|
||||
if (!d.startSection(SectionId::Export, §ionStart, §ionSize, "export"))
|
||||
if (!d.startSection(SectionId::Export, env, §ionStart, §ionSize, "export"))
|
||||
return false;
|
||||
if (sectionStart == Decoder::NotStarted)
|
||||
return true;
|
||||
|
|
@ -1177,7 +1320,7 @@ static bool
|
|||
DecodeStartSection(Decoder& d, ModuleEnvironment* env)
|
||||
{
|
||||
uint32_t sectionStart, sectionSize;
|
||||
if (!d.startSection(SectionId::Start, §ionStart, §ionSize, "start"))
|
||||
if (!d.startSection(SectionId::Start, env, §ionStart, §ionSize, "start"))
|
||||
return false;
|
||||
if (sectionStart == Decoder::NotStarted)
|
||||
return true;
|
||||
|
|
@ -1208,7 +1351,7 @@ static bool
|
|||
DecodeElemSection(Decoder& d, ModuleEnvironment* env)
|
||||
{
|
||||
uint32_t sectionStart, sectionSize;
|
||||
if (!d.startSection(SectionId::Elem, §ionStart, §ionSize, "elem"))
|
||||
if (!d.startSection(SectionId::Elem, env, §ionStart, §ionSize, "elem"))
|
||||
return false;
|
||||
if (sectionStart == Decoder::NotStarted)
|
||||
return true;
|
||||
|
|
@ -1266,7 +1409,7 @@ wasm::DecodeModuleEnvironment(Decoder& d, ModuleEnvironment* env)
|
|||
if (!DecodePreamble(d))
|
||||
return false;
|
||||
|
||||
if (!DecodeTypeSection(d, &env->sigs))
|
||||
if (!DecodeTypeSection(d, env))
|
||||
return false;
|
||||
|
||||
if (!DecodeImportSection(d, env))
|
||||
|
|
@ -1275,13 +1418,13 @@ wasm::DecodeModuleEnvironment(Decoder& d, ModuleEnvironment* env)
|
|||
if (!DecodeFunctionSection(d, env))
|
||||
return false;
|
||||
|
||||
if (!DecodeTableSection(d, &env->tables))
|
||||
if (!DecodeTableSection(d, env))
|
||||
return false;
|
||||
|
||||
if (!DecodeMemorySection(d, env))
|
||||
return false;
|
||||
|
||||
if (!DecodeGlobalSection(d, &env->globals))
|
||||
if (!DecodeGlobalSection(d, env))
|
||||
return false;
|
||||
|
||||
if (!DecodeExportSection(d, env))
|
||||
|
|
@ -1318,14 +1461,14 @@ DecodeFunctionBody(Decoder& d, const ModuleEnvironment& env, uint32_t funcIndex)
|
|||
}
|
||||
|
||||
static bool
|
||||
DecodeCodeSection(Decoder& d, const ModuleEnvironment& env)
|
||||
DecodeCodeSection(Decoder& d, ModuleEnvironment* env)
|
||||
{
|
||||
uint32_t sectionStart, sectionSize;
|
||||
if (!d.startSection(SectionId::Code, §ionStart, §ionSize, "code"))
|
||||
if (!d.startSection(SectionId::Code, env, §ionStart, §ionSize, "code"))
|
||||
return false;
|
||||
|
||||
if (sectionStart == Decoder::NotStarted) {
|
||||
if (env.numFuncDefs() != 0)
|
||||
if (env->numFuncDefs() != 0)
|
||||
return d.fail("expected function bodies");
|
||||
return true;
|
||||
}
|
||||
|
|
@ -1334,11 +1477,11 @@ DecodeCodeSection(Decoder& d, const ModuleEnvironment& env)
|
|||
if (!d.readVarU32(&numFuncDefs))
|
||||
return d.fail("expected function body count");
|
||||
|
||||
if (numFuncDefs != env.numFuncDefs())
|
||||
if (numFuncDefs != env->numFuncDefs())
|
||||
return d.fail("function body count does not match function signature count");
|
||||
|
||||
for (uint32_t funcDefIndex = 0; funcDefIndex < numFuncDefs; funcDefIndex++) {
|
||||
if (!DecodeFunctionBody(d, env, env.numFuncImports() + funcDefIndex))
|
||||
if (!DecodeFunctionBody(d, *env, env->numFuncImports() + funcDefIndex))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1349,16 +1492,16 @@ DecodeCodeSection(Decoder& d, const ModuleEnvironment& env)
|
|||
}
|
||||
|
||||
|
||||
bool
|
||||
wasm::DecodeDataSection(Decoder& d, const ModuleEnvironment& env, DataSegmentVector* segments)
|
||||
static bool
|
||||
DecodeDataSection(Decoder& d, ModuleEnvironment* env)
|
||||
{
|
||||
uint32_t sectionStart, sectionSize;
|
||||
if (!d.startSection(SectionId::Data, §ionStart, §ionSize, "data"))
|
||||
if (!d.startSection(SectionId::Data, env, §ionStart, §ionSize, "data"))
|
||||
return false;
|
||||
if (sectionStart == Decoder::NotStarted)
|
||||
return true;
|
||||
|
||||
if (!env.usesMemory())
|
||||
if (!env->usesMemory())
|
||||
return d.fail("data section requires a memory section");
|
||||
|
||||
uint32_t numSegments;
|
||||
|
|
@ -1377,7 +1520,7 @@ wasm::DecodeDataSection(Decoder& d, const ModuleEnvironment& env, DataSegmentVec
|
|||
return d.fail("linear memory index must currently be 0");
|
||||
|
||||
DataSegment seg;
|
||||
if (!DecodeInitializerExpression(d, env.globals, ValType::I32, &seg.offset))
|
||||
if (!DecodeInitializerExpression(d, env->globals, ValType::I32, &seg.offset))
|
||||
return false;
|
||||
|
||||
if (!d.readVarU32(&seg.length))
|
||||
|
|
@ -1388,7 +1531,7 @@ wasm::DecodeDataSection(Decoder& d, const ModuleEnvironment& env, DataSegmentVec
|
|||
if (!d.readBytes(seg.length))
|
||||
return d.fail("data segment shorter than declared");
|
||||
|
||||
if (!segments->append(seg))
|
||||
if (!env->dataSegments.append(seg))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1398,11 +1541,82 @@ wasm::DecodeDataSection(Decoder& d, const ModuleEnvironment& env, DataSegmentVec
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
wasm::DecodeUnknownSections(Decoder& d)
|
||||
static void
|
||||
MaybeDecodeNameSectionBody(Decoder& d, ModuleEnvironment* env)
|
||||
{
|
||||
// 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;
|
||||
|
||||
// Use a local vector (and not env->funcNames) since it could result in a
|
||||
// partially initialized result in case of failure in the middle.
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
env->funcNames = Move(funcNames);
|
||||
}
|
||||
|
||||
static bool
|
||||
DecodeNameSection(Decoder& d, ModuleEnvironment* env)
|
||||
{
|
||||
uint32_t sectionStart, sectionSize;
|
||||
if (!d.startCustomSection(NameSectionName, env, §ionStart, §ionSize))
|
||||
return false;
|
||||
if (sectionStart == Decoder::NotStarted)
|
||||
return true;
|
||||
|
||||
// Once started, custom sections do not report validation errors.
|
||||
|
||||
MaybeDecodeNameSectionBody(d, env);
|
||||
|
||||
d.finishCustomSection(sectionStart, sectionSize);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
wasm::DecodeModuleTail(Decoder& d, ModuleEnvironment* env)
|
||||
{
|
||||
if (!DecodeDataSection(d, env))
|
||||
return false;
|
||||
|
||||
if (!DecodeNameSection(d, env))
|
||||
return false;
|
||||
|
||||
while (!d.done()) {
|
||||
if (!d.skipUserDefinedSection())
|
||||
if (!d.skipCustomSection(env))
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -1421,15 +1635,13 @@ wasm::Validate(const ShareableBytes& bytecode, UniqueChars* error)
|
|||
if (!DecodeModuleEnvironment(d, &env))
|
||||
return false;
|
||||
|
||||
if (!DecodeCodeSection(d, env))
|
||||
if (!DecodeCodeSection(d, &env))
|
||||
return false;
|
||||
|
||||
DataSegmentVector dataSegments;
|
||||
if (!DecodeDataSection(d, env, &dataSegments))
|
||||
if (!DecodeModuleTail(d, &env))
|
||||
return false;
|
||||
|
||||
if (!DecodeUnknownSections(d))
|
||||
return false;
|
||||
MOZ_ASSERT(!*error, "unreported error in decoding");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
|
@ -25,6 +25,83 @@
|
|||
namespace js {
|
||||
namespace wasm {
|
||||
|
||||
// ModuleEnvironment contains all the state necessary to validate, process or
|
||||
// render functions. It is created by decoding all the sections before the wasm
|
||||
// code section and then used immutably during. When compiling a module using a
|
||||
// ModuleGenerator, the ModuleEnvironment holds state shared between the
|
||||
// ModuleGenerator thread and background compile threads. All the threads
|
||||
// are given a read-only view of the ModuleEnvironment, thus preventing race
|
||||
// conditions.
|
||||
struct ModuleEnvironment
|
||||
{
|
||||
ModuleKind kind;
|
||||
MemoryUsage memoryUsage;
|
||||
mozilla::Atomic<uint32_t> minMemoryLength;
|
||||
Maybe<uint32_t> maxMemoryLength;
|
||||
|
||||
SigWithIdVector sigs;
|
||||
SigWithIdPtrVector funcSigs;
|
||||
Uint32Vector funcImportGlobalDataOffsets;
|
||||
GlobalDescVector globals;
|
||||
TableDescVector tables;
|
||||
Uint32Vector asmJSSigToTableIndex;
|
||||
ImportVector imports;
|
||||
ExportVector exports;
|
||||
Maybe<uint32_t> startFuncIndex;
|
||||
ElemSegmentVector elemSegments;
|
||||
DataSegmentVector dataSegments;
|
||||
NameInBytecodeVector funcNames;
|
||||
CustomSectionVector customSections;
|
||||
|
||||
explicit ModuleEnvironment(ModuleKind kind = ModuleKind::Wasm)
|
||||
: kind(kind),
|
||||
memoryUsage(MemoryUsage::None),
|
||||
minMemoryLength(0)
|
||||
{}
|
||||
|
||||
size_t numTables() const {
|
||||
return tables.length();
|
||||
}
|
||||
size_t numSigs() const {
|
||||
return sigs.length();
|
||||
}
|
||||
|
||||
size_t numFuncs() const {
|
||||
// asm.js pre-reserves a bunch of function index space which is
|
||||
// incrementally filled in during function-body validation. Thus, there
|
||||
// are a few possible interpretations of numFuncs() (total index space
|
||||
// size vs. exact number of imports/definitions encountered so far) and
|
||||
// to simplify things we simply only define this quantity for wasm.
|
||||
MOZ_ASSERT(!isAsmJS());
|
||||
return funcSigs.length();
|
||||
}
|
||||
size_t numFuncDefs() const {
|
||||
// asm.js overallocates the length of funcSigs and in general does not
|
||||
// know the number of function definitions until it's done compiling.
|
||||
MOZ_ASSERT(!isAsmJS());
|
||||
return funcSigs.length() - funcImportGlobalDataOffsets.length();
|
||||
}
|
||||
size_t numFuncImports() const {
|
||||
MOZ_ASSERT(!isAsmJS());
|
||||
return funcImportGlobalDataOffsets.length();
|
||||
}
|
||||
bool usesMemory() const {
|
||||
return UsesMemory(memoryUsage);
|
||||
}
|
||||
|
||||
bool isAsmJS() const {
|
||||
return kind == ModuleKind::AsmJS;
|
||||
}
|
||||
bool funcIsImport(uint32_t funcIndex) const {
|
||||
return funcIndex < funcImportGlobalDataOffsets.length();
|
||||
}
|
||||
uint32_t funcIndexToSigIndex(uint32_t funcIndex) const {
|
||||
return funcSigs[funcIndex] - sigs.begin();
|
||||
}
|
||||
};
|
||||
|
||||
typedef UniquePtr<ModuleEnvironment> UniqueModuleEnvironment;
|
||||
|
||||
// The Encoder class appends bytes to the Bytes object it is given during
|
||||
// construction. The client is responsible for the Bytes's lifetime and must
|
||||
// keep the Bytes alive as long as the Encoder is used.
|
||||
|
|
@ -196,8 +273,7 @@ class Encoder
|
|||
// end while the size's varU32 must be stored at the beginning. Immediately
|
||||
// after the section length is the string id of the section.
|
||||
|
||||
MOZ_MUST_USE bool startSection(SectionId id, size_t* offset) {
|
||||
MOZ_ASSERT(id != SectionId::UserDefined); // not supported yet
|
||||
[[nodiscard]] bool startSection(SectionId id, size_t* offset) {
|
||||
|
||||
return writeVarU32(uint32_t(id)) &&
|
||||
writePatchableVarU32(offset);
|
||||
|
|
@ -418,104 +494,34 @@ class Decoder
|
|||
|
||||
static const uint32_t NotStarted = UINT32_MAX;
|
||||
|
||||
MOZ_MUST_USE bool startSection(SectionId id,
|
||||
uint32_t* startOffset,
|
||||
uint32_t* size,
|
||||
const char* sectionName)
|
||||
{
|
||||
const uint8_t* const before = cur_;
|
||||
const uint8_t* beforeId = before;
|
||||
uint32_t idValue;
|
||||
if (!readVarU32(&idValue))
|
||||
goto backup;
|
||||
while (idValue != uint32_t(id)) {
|
||||
if (idValue != uint32_t(SectionId::UserDefined))
|
||||
goto backup;
|
||||
// Rewind to the section id since skipUserDefinedSection expects it.
|
||||
cur_ = beforeId;
|
||||
if (!skipUserDefinedSection())
|
||||
return false;
|
||||
beforeId = cur_;
|
||||
if (!readVarU32(&idValue))
|
||||
goto backup;
|
||||
}
|
||||
if (!readVarU32(size))
|
||||
goto fail;
|
||||
if (bytesRemain() < *size)
|
||||
goto fail;
|
||||
*startOffset = cur_ - beg_;
|
||||
return true;
|
||||
backup:
|
||||
cur_ = before;
|
||||
*startOffset = NotStarted;
|
||||
return true;
|
||||
fail:
|
||||
return fail("failed to start %s section", sectionName);
|
||||
}
|
||||
MOZ_MUST_USE bool finishSection(uint32_t startOffset, uint32_t size,
|
||||
const char* sectionName)
|
||||
{
|
||||
if (size != (cur_ - beg_) - startOffset)
|
||||
return fail("byte size mismatch in %s section", sectionName);
|
||||
return true;
|
||||
}
|
||||
[[nodiscard]] bool startSection(SectionId id,
|
||||
ModuleEnvironment* env,
|
||||
uint32_t* sectionStart,
|
||||
uint32_t* sectionSize,
|
||||
const char* sectionName);
|
||||
[[nodiscard]] bool finishSection(uint32_t sectionStart,
|
||||
uint32_t sectionSize,
|
||||
const char* sectionName);
|
||||
|
||||
// "User sections" do not cause validation errors unless the error is in
|
||||
// the user-defined section header itself.
|
||||
// Custom sections do not cause validation errors unless the error is in
|
||||
// the section header itself.
|
||||
|
||||
MOZ_MUST_USE bool startUserDefinedSection(const char* expectedId,
|
||||
size_t expectedIdSize,
|
||||
uint32_t* sectionStart,
|
||||
uint32_t* sectionSize)
|
||||
[[nodiscard]] bool startCustomSection(const char* expected,
|
||||
size_t expectedLength,
|
||||
ModuleEnvironment* env,
|
||||
uint32_t* sectionStart,
|
||||
uint32_t* sectionSize);
|
||||
template <size_t NameSizeWith0>
|
||||
[[nodiscard]] bool startCustomSection(const char (&name)[NameSizeWith0],
|
||||
ModuleEnvironment* env,
|
||||
uint32_t* sectionStart,
|
||||
uint32_t* sectionSize)
|
||||
{
|
||||
const uint8_t* const before = cur_;
|
||||
while (true) {
|
||||
if (!startSection(SectionId::UserDefined, sectionStart, sectionSize, "user-defined"))
|
||||
return false;
|
||||
if (*sectionStart == NotStarted) {
|
||||
cur_ = before;
|
||||
return true;
|
||||
}
|
||||
uint32_t idSize;
|
||||
if (!readVarU32(&idSize))
|
||||
goto fail;
|
||||
if (idSize > bytesRemain() || currentOffset() + idSize > *sectionStart + *sectionSize)
|
||||
goto fail;
|
||||
if (expectedId && (expectedIdSize != idSize || !!memcmp(cur_, expectedId, idSize))) {
|
||||
finishUserDefinedSection(*sectionStart, *sectionSize);
|
||||
continue;
|
||||
}
|
||||
cur_ += idSize;
|
||||
return true;
|
||||
}
|
||||
MOZ_CRASH("unreachable");
|
||||
fail:
|
||||
return fail("failed to start user-defined section");
|
||||
}
|
||||
template <size_t IdSizeWith0>
|
||||
MOZ_MUST_USE bool startUserDefinedSection(const char (&id)[IdSizeWith0],
|
||||
uint32_t* sectionStart,
|
||||
uint32_t* sectionSize)
|
||||
{
|
||||
MOZ_ASSERT(id[IdSizeWith0 - 1] == '\0');
|
||||
return startUserDefinedSection(id, IdSizeWith0 - 1, sectionStart, sectionSize);
|
||||
}
|
||||
void finishUserDefinedSection(uint32_t sectionStart, uint32_t sectionSize) {
|
||||
MOZ_ASSERT(cur_ >= beg_);
|
||||
MOZ_ASSERT(cur_ <= end_);
|
||||
cur_ = (beg_ + sectionStart) + sectionSize;
|
||||
MOZ_ASSERT(cur_ <= end_);
|
||||
clearError();
|
||||
}
|
||||
MOZ_MUST_USE bool skipUserDefinedSection() {
|
||||
uint32_t sectionStart, sectionSize;
|
||||
if (!startUserDefinedSection(nullptr, 0, §ionStart, §ionSize))
|
||||
return false;
|
||||
if (sectionStart == NotStarted)
|
||||
return fail("expected user-defined section");
|
||||
finishUserDefinedSection(sectionStart, sectionSize);
|
||||
return true;
|
||||
MOZ_ASSERT(name[NameSizeWith0 - 1] == '\0');
|
||||
return startCustomSection(name, NameSizeWith0 - 1, env, sectionStart, sectionSize);
|
||||
}
|
||||
void finishCustomSection(uint32_t sectionStart, uint32_t sectionSize);
|
||||
[[nodiscard]] bool skipCustomSection(ModuleEnvironment* env);
|
||||
|
||||
// The infallible "unchecked" decoding functions can be used when we are
|
||||
// sure that the bytes are well-formed (by construction or due to previous
|
||||
|
|
@ -579,11 +585,8 @@ class Decoder
|
|||
}
|
||||
};
|
||||
|
||||
// Reusable macro encoding/decoding functions reused by both the two
|
||||
// encoders (AsmJS/WasmTextToBinary) and all the decoders
|
||||
// (WasmCompile/WasmIonCompile/WasmBaselineCompile/WasmBinaryToText).
|
||||
|
||||
// Misc helpers.
|
||||
// The local entries are part of function bodies and thus serialized by both
|
||||
// wasm and asm.js and decoded as part of both validation and compilation.
|
||||
|
||||
[[nodiscard]] bool
|
||||
EncodeLocalEntries(Encoder& d, const ValTypeVector& locals);
|
||||
|
|
@ -599,86 +602,25 @@ DecodeLocalEntries(Decoder& d, ModuleKind kind, ValTypeVector* locals);
|
|||
// are given a read-only view of the ModuleEnvironment, thus preventing race
|
||||
// conditions.
|
||||
|
||||
struct ModuleEnvironment
|
||||
{
|
||||
ModuleKind kind;
|
||||
MemoryUsage memoryUsage;
|
||||
mozilla::Atomic<uint32_t> minMemoryLength;
|
||||
Maybe<uint32_t> maxMemoryLength;
|
||||
|
||||
SigWithIdVector sigs;
|
||||
SigWithIdPtrVector funcSigs;
|
||||
Uint32Vector funcImportGlobalDataOffsets;
|
||||
GlobalDescVector globals;
|
||||
TableDescVector tables;
|
||||
Uint32Vector asmJSSigToTableIndex;
|
||||
ImportVector imports;
|
||||
ExportVector exports;
|
||||
Maybe<uint32_t> startFuncIndex;
|
||||
ElemSegmentVector elemSegments;
|
||||
|
||||
explicit ModuleEnvironment(ModuleKind kind = ModuleKind::Wasm)
|
||||
: kind(kind),
|
||||
memoryUsage(MemoryUsage::None),
|
||||
minMemoryLength(0)
|
||||
{}
|
||||
|
||||
size_t numTables() const {
|
||||
return tables.length();
|
||||
}
|
||||
size_t numSigs() const {
|
||||
return sigs.length();
|
||||
}
|
||||
|
||||
size_t numFuncs() const {
|
||||
// asm.js pre-reserves a bunch of function index space which is
|
||||
// incrementally filled in during function-body validation. Thus, there
|
||||
// are a few possible interpretations of numFuncs() (total index space
|
||||
// size vs. exact number of imports/definitions encountered so far) and
|
||||
// to simplify things we simply only define this quantity for wasm.
|
||||
MOZ_ASSERT(!isAsmJS());
|
||||
return funcSigs.length();
|
||||
}
|
||||
size_t numFuncDefs() const {
|
||||
// asm.js overallocates the length of funcSigs and in general does not
|
||||
// know the number of function definitions until it's done compiling.
|
||||
MOZ_ASSERT(!isAsmJS());
|
||||
return funcSigs.length() - funcImportGlobalDataOffsets.length();
|
||||
}
|
||||
size_t numFuncImports() const {
|
||||
MOZ_ASSERT(!isAsmJS());
|
||||
return funcImportGlobalDataOffsets.length();
|
||||
}
|
||||
bool usesMemory() const {
|
||||
return UsesMemory(memoryUsage);
|
||||
}
|
||||
|
||||
bool isAsmJS() const {
|
||||
return kind == ModuleKind::AsmJS;
|
||||
}
|
||||
bool funcIsImport(uint32_t funcIndex) const {
|
||||
return funcIndex < funcImportGlobalDataOffsets.length();
|
||||
}
|
||||
uint32_t funcIndexToSigIndex(uint32_t funcIndex) const {
|
||||
return funcSigs[funcIndex] - sigs.begin();
|
||||
}
|
||||
};
|
||||
|
||||
typedef UniquePtr<ModuleEnvironment> UniqueModuleEnvironment;
|
||||
|
||||
// Section macros.
|
||||
// Calling DecodeModuleEnvironment decodes all sections up to the code section
|
||||
// and performs full validation of all those sections. The client must then
|
||||
// decode the code section itself, reusing ValidateFunctionBody if necessary,
|
||||
// and finally call DecodeModuleTail to decode all remaining sections after the
|
||||
// code section (again, performing full validation).
|
||||
|
||||
[[nodiscard]] bool
|
||||
DecodeModuleEnvironment(Decoder& d, ModuleEnvironment* env);
|
||||
|
||||
[[nodiscard]] bool
|
||||
DecodeDataSection(Decoder& d, const ModuleEnvironment& env, DataSegmentVector* segments);
|
||||
|
||||
MOZ_MUST_USE bool
|
||||
DecodeUnknownSections(Decoder& d);
|
||||
ValidateFunctionBody(const ModuleEnvironment& env, uint32_t funcIndex, Decoder& d);
|
||||
|
||||
[[nodiscard]] bool
|
||||
ValidateFunctionBody(const ModuleEnvironment& env, uint32_t funcIndex, Decoder& d);
|
||||
DecodeModuleTail(Decoder& d, ModuleEnvironment* env);
|
||||
|
||||
// Validate an entire module, returning true if the module was validated
|
||||
// successfully. If Validate returns false:
|
||||
// - if *error is null, the caller should report out-of-memory
|
||||
// - otherwise, there was a legitimate error described by *error
|
||||
|
||||
[[nodiscard]] bool
|
||||
Validate(const ShareableBytes& bytecode, UniqueChars* error);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue