mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1244405 - Baldr: add memory segments (r=sunfish)
This commit is contained in:
parent
ec2e633035
commit
9f62e6aede
@ -30,8 +30,6 @@
|
||||
using namespace js;
|
||||
using namespace js::wasm;
|
||||
|
||||
using mozilla::PodCopy;
|
||||
|
||||
typedef Handle<WasmModuleObject*> HandleWasmModule;
|
||||
typedef MutableHandle<WasmModuleObject*> MutableHandleWasmModule;
|
||||
|
||||
@ -515,7 +513,7 @@ DecodeFuncBody(JSContext* cx, Decoder& d, ModuleGenerator& mg, FunctionGenerator
|
||||
if (!fg.bytecode().resize(bodyLength))
|
||||
return false;
|
||||
|
||||
PodCopy(fg.bytecode().begin(), bodyBegin, bodyLength);
|
||||
memcpy(fg.bytecode().begin(), bodyBegin, bodyLength);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -902,6 +900,56 @@ DecodeCodeSection(JSContext* cx, Decoder& d, ModuleGenerator& mg)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
DecodeDataSection(JSContext* cx, Decoder& d, Handle<ArrayBufferObject*> heap)
|
||||
{
|
||||
if (!d.readCStringIf(DataSection))
|
||||
return true;
|
||||
|
||||
uint32_t sectionStart;
|
||||
if (!d.startSection(§ionStart))
|
||||
return Fail(cx, d, "expected data section byte size");
|
||||
|
||||
uint32_t numSegments;
|
||||
if (!d.readVarU32(&numSegments))
|
||||
return Fail(cx, d, "expected number of data segments");
|
||||
|
||||
uint8_t* const heapBase = heap->dataPointer();
|
||||
uint32_t const heapLength = heap->byteLength();
|
||||
uint32_t prevEnd = 0;
|
||||
|
||||
for (uint32_t i = 0; i < numSegments; i++) {
|
||||
if (!d.readCStringIf(SegmentSubsection))
|
||||
return Fail(cx, d, "expected segment tag");
|
||||
|
||||
uint32_t dstOffset;
|
||||
if (!d.readVarU32(&dstOffset))
|
||||
return Fail(cx, d, "expected segment destination offset");
|
||||
|
||||
if (dstOffset < prevEnd)
|
||||
return Fail(cx, d, "data segments must be disjoint and ordered");
|
||||
|
||||
uint32_t numBytes;
|
||||
if (!d.readVarU32(&numBytes))
|
||||
return Fail(cx, d, "expected segment size");
|
||||
|
||||
if (dstOffset > heapLength || heapLength - dstOffset < numBytes)
|
||||
return Fail(cx, d, "data segment does not fit in memory");
|
||||
|
||||
const uint8_t* src;
|
||||
if (!d.readData(numBytes, &src))
|
||||
return Fail(cx, d, "data segment shorter than declared");
|
||||
|
||||
memcpy(heapBase + dstOffset, src, numBytes);
|
||||
prevEnd = dstOffset + numBytes;
|
||||
}
|
||||
|
||||
if (!d.finishSection(sectionStart))
|
||||
return Fail(cx, d, "data section byte size mismatch");
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
DecodeUnknownSection(JSContext* cx, Decoder& d)
|
||||
{
|
||||
@ -913,7 +961,8 @@ DecodeUnknownSection(JSContext* cx, Decoder& d)
|
||||
!strcmp(sectionName.get(), ImportSection) ||
|
||||
!strcmp(sectionName.get(), DeclSection) ||
|
||||
!strcmp(sectionName.get(), ExportSection) ||
|
||||
!strcmp(sectionName.get(), CodeSection))
|
||||
!strcmp(sectionName.get(), CodeSection) ||
|
||||
!strcmp(sectionName.get(), DataSection))
|
||||
{
|
||||
return Fail(cx, d, "known section out of order");
|
||||
}
|
||||
@ -964,6 +1013,9 @@ DecodeModule(JSContext* cx, UniqueChars file, const uint8_t* bytes, uint32_t len
|
||||
if (!DecodeCodeSection(cx, d, mg))
|
||||
return false;
|
||||
|
||||
if (!DecodeDataSection(cx, d, heap))
|
||||
return false;
|
||||
|
||||
CacheableCharsVector funcNames;
|
||||
|
||||
while (!d.readCStringIf(EndSection)) {
|
||||
|
@ -48,11 +48,13 @@ static const char DeclSection[] = "decl";
|
||||
static const char MemorySection[] = "memory";
|
||||
static const char ExportSection[] = "export";
|
||||
static const char CodeSection[] = "code";
|
||||
static const char DataSection[] = "data";
|
||||
static const char EndSection[] = "";
|
||||
|
||||
// Subsection names:
|
||||
static const char FuncSubsection[] = "func";
|
||||
static const char MemorySubsection[] = "memory";
|
||||
static const char SegmentSubsection[] = "segment";
|
||||
|
||||
// Field names:
|
||||
static const char FieldInitial[] = "initial";
|
||||
@ -439,6 +441,11 @@ class Encoder
|
||||
return bytecode_.append(reinterpret_cast<const uint8_t*>(cstr), strlen(cstr) + 1);
|
||||
}
|
||||
|
||||
MOZ_WARN_UNUSED_RESULT bool writeData(const uint8_t* bytes, uint32_t numBytes) {
|
||||
MOZ_ASSERT(bytes);
|
||||
return bytecode_.append(bytes, numBytes);
|
||||
}
|
||||
|
||||
MOZ_WARN_UNUSED_RESULT bool startSection(size_t* offset) {
|
||||
if (!writeU32(BadSectionLength))
|
||||
return false;
|
||||
@ -485,10 +492,14 @@ class Decoder
|
||||
const uint8_t* const end_;
|
||||
const uint8_t* cur_;
|
||||
|
||||
uintptr_t bytesRemain() const {
|
||||
MOZ_ASSERT(end_ >= cur_);
|
||||
return uintptr_t(end_ - cur_);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
MOZ_WARN_UNUSED_RESULT bool
|
||||
read(T* out) {
|
||||
if (uintptr_t(end_ - cur_) < sizeof(T))
|
||||
MOZ_WARN_UNUSED_RESULT bool read(T* out) {
|
||||
if (bytesRemain() < sizeof(T))
|
||||
return false;
|
||||
if (out)
|
||||
memcpy((void*)out, cur_, sizeof(T));
|
||||
@ -497,8 +508,7 @@ class Decoder
|
||||
}
|
||||
|
||||
template <class IntT, class T>
|
||||
MOZ_WARN_UNUSED_RESULT bool
|
||||
readEnum(T* out) {
|
||||
MOZ_WARN_UNUSED_RESULT bool readEnum(T* out) {
|
||||
static_assert(mozilla::IsEnum<T>::value, "is an enum");
|
||||
// See Encoder::writeEnum.
|
||||
IntT i;
|
||||
@ -511,7 +521,7 @@ class Decoder
|
||||
|
||||
template <class T>
|
||||
T uncheckedPeek() const {
|
||||
MOZ_ASSERT(uintptr_t(end_ - cur_) >= sizeof(T));
|
||||
MOZ_ASSERT(bytesRemain() >= sizeof(T));
|
||||
T ret;
|
||||
memcpy(&ret, cur_, sizeof(T));
|
||||
return ret;
|
||||
@ -652,6 +662,15 @@ class Decoder
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_WARN_UNUSED_RESULT bool readData(uint32_t numBytes, const uint8_t** bytes = nullptr) {
|
||||
if (bytes)
|
||||
*bytes = cur_;
|
||||
if (bytesRemain() < numBytes)
|
||||
return false;
|
||||
cur_ += numBytes;
|
||||
return true;
|
||||
}
|
||||
|
||||
MOZ_WARN_UNUSED_RESULT bool startSection(uint32_t* offset) {
|
||||
uint32_t unused;
|
||||
if (!readU32(&unused))
|
||||
@ -669,7 +688,7 @@ class Decoder
|
||||
uint32_t numBytes;
|
||||
if (!readU32(&numBytes))
|
||||
return false;
|
||||
if (uintptr_t(end_ - cur_) < numBytes)
|
||||
if (bytesRemain() < numBytes)
|
||||
return false;
|
||||
cur_ += numBytes;
|
||||
return true;
|
||||
|
@ -292,15 +292,33 @@ class WasmAstExport : public WasmAstNode
|
||||
size_t funcIndex() const { MOZ_ASSERT(kind_ == WasmAstExportKind::Func); return u.funcIndex_; }
|
||||
};
|
||||
|
||||
class WasmAstSegment : public WasmAstNode
|
||||
{
|
||||
uint32_t offset_;
|
||||
TwoByteChars text_;
|
||||
|
||||
public:
|
||||
WasmAstSegment(uint32_t offset, TwoByteChars text)
|
||||
: offset_(offset), text_(text)
|
||||
{}
|
||||
uint32_t offset() const { return offset_; }
|
||||
TwoByteChars text() const { return text_; }
|
||||
};
|
||||
|
||||
typedef WasmAstVector<WasmAstSegment*> WasmAstSegmentVector;
|
||||
|
||||
class WasmAstMemory : public WasmAstNode
|
||||
{
|
||||
uint32_t initialSize_;
|
||||
WasmAstSegmentVector segments_;
|
||||
|
||||
public:
|
||||
explicit WasmAstMemory(uint32_t initialSize)
|
||||
: initialSize_(initialSize)
|
||||
explicit WasmAstMemory(uint32_t initialSize, WasmAstSegmentVector&& segments)
|
||||
: initialSize_(initialSize),
|
||||
segments_(Move(segments))
|
||||
{}
|
||||
uint32_t initialSize() const { return initialSize_; }
|
||||
const WasmAstSegmentVector& segments() const { return segments_; }
|
||||
};
|
||||
|
||||
class WasmAstModule : public WasmAstNode
|
||||
@ -475,6 +493,7 @@ class WasmToken
|
||||
OpenParen,
|
||||
Param,
|
||||
Result,
|
||||
Segment,
|
||||
SetLocal,
|
||||
Text,
|
||||
UnaryOpcode,
|
||||
@ -602,6 +621,73 @@ IsNameAfterDollar(char16_t c)
|
||||
return c == '_' || IsWasmDigit(c) || IsWasmLetter(c);
|
||||
}
|
||||
|
||||
static bool
|
||||
IsHexDigit(char c, uint8_t* value)
|
||||
{
|
||||
if (c >= '0' && c <= '9') {
|
||||
*value = c - '0';
|
||||
return true;
|
||||
}
|
||||
|
||||
if (c >= 'a' && c <= 'f') {
|
||||
*value = 10 + (c - 'a');
|
||||
return true;
|
||||
}
|
||||
|
||||
if (c >= 'A' && c <= 'F') {
|
||||
*value = 10 + (c - 'A');
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
ConsumeTextByte(const char16_t** curp, const char16_t* end, uint8_t *byte = nullptr)
|
||||
{
|
||||
const char16_t*& cur = *curp;
|
||||
MOZ_ASSERT(cur != end);
|
||||
|
||||
if (*cur != '\\') {
|
||||
if (byte)
|
||||
*byte = *cur;
|
||||
cur++;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (++cur == end)
|
||||
return false;
|
||||
|
||||
uint8_t u8;
|
||||
switch (*cur) {
|
||||
case 'n': u8 = '\n'; break;
|
||||
case 't': u8 = '\t'; break;
|
||||
case '\\': u8 = '\\'; break;
|
||||
case '\"': u8 = '\"'; break;
|
||||
case '\'': u8 = '\''; break;
|
||||
default: {
|
||||
uint8_t lowNibble;
|
||||
if (!IsHexDigit(*cur, &lowNibble))
|
||||
return false;
|
||||
|
||||
if (++cur == end)
|
||||
return false;
|
||||
|
||||
uint8_t highNibble;
|
||||
if (!IsHexDigit(*cur, &highNibble))
|
||||
return false;
|
||||
|
||||
u8 = lowNibble | (highNibble << 4);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (byte)
|
||||
*byte = u8;
|
||||
cur++;
|
||||
return true;
|
||||
}
|
||||
|
||||
class WasmTokenStream
|
||||
{
|
||||
static const uint32_t LookaheadSize = 2;
|
||||
@ -641,10 +727,15 @@ class WasmTokenStream
|
||||
switch (*begin) {
|
||||
case '"':
|
||||
cur_++;
|
||||
do {
|
||||
while (true) {
|
||||
if (cur_ == end_)
|
||||
return fail(begin);
|
||||
} while (*cur_++ != '"');
|
||||
if (*cur_ == '"')
|
||||
break;
|
||||
if (!ConsumeTextByte(&cur_, end_))
|
||||
return fail(begin);
|
||||
}
|
||||
cur_++;
|
||||
return WasmToken(WasmToken::Text, begin, cur_);
|
||||
|
||||
case '$':
|
||||
@ -1133,6 +1224,8 @@ class WasmTokenStream
|
||||
case 's':
|
||||
if (consume(MOZ_UTF16("set_local")))
|
||||
return WasmToken(WasmToken::SetLocal, begin, cur_);
|
||||
if (consume(MOZ_UTF16("segment")))
|
||||
return WasmToken(WasmToken::Segment, begin, cur_);
|
||||
break;
|
||||
|
||||
default:
|
||||
@ -1484,6 +1577,23 @@ ParseFunc(WasmParseContext& c, WasmAstModule* module)
|
||||
return new(c.lifo) WasmAstFunc(sigIndex, Move(vars), maybeBody);
|
||||
}
|
||||
|
||||
static WasmAstSegment*
|
||||
ParseSegment(WasmParseContext& c)
|
||||
{
|
||||
if (!c.ts.match(WasmToken::Segment, c.error))
|
||||
return nullptr;
|
||||
|
||||
WasmToken dstOffset;
|
||||
if (!c.ts.match(WasmToken::Integer, &dstOffset, c.error))
|
||||
return nullptr;
|
||||
|
||||
WasmToken text;
|
||||
if (!c.ts.match(WasmToken::Text, &text, c.error))
|
||||
return nullptr;
|
||||
|
||||
return new(c.lifo) WasmAstSegment(dstOffset.integer(), text.text());
|
||||
}
|
||||
|
||||
static WasmAstMemory*
|
||||
ParseMemory(WasmParseContext& c)
|
||||
{
|
||||
@ -1491,7 +1601,16 @@ ParseMemory(WasmParseContext& c)
|
||||
if (!c.ts.match(WasmToken::Integer, &initialSize, c.error))
|
||||
return nullptr;
|
||||
|
||||
return new(c.lifo) WasmAstMemory(initialSize.integer());
|
||||
WasmAstSegmentVector segments(c.lifo);
|
||||
while (c.ts.getIf(WasmToken::OpenParen)) {
|
||||
WasmAstSegment* segment = ParseSegment(c);
|
||||
if (!segment || !segments.append(segment))
|
||||
return nullptr;
|
||||
if (!c.ts.match(WasmToken::CloseParen, c.error))
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return new(c.lifo) WasmAstMemory(initialSize.integer(), Move(segments));
|
||||
}
|
||||
|
||||
static WasmAstImport*
|
||||
@ -1557,7 +1676,7 @@ ParseExport(WasmParseContext& c)
|
||||
}
|
||||
|
||||
static WasmAstModule*
|
||||
TextToAst(const char16_t* text, LifoAlloc& lifo, UniqueChars* error)
|
||||
ParseModule(const char16_t* text, LifoAlloc& lifo, UniqueChars* error)
|
||||
{
|
||||
WasmParseContext c(text, lifo, error);
|
||||
|
||||
@ -2010,8 +2129,67 @@ EncodeCodeSection(Encoder& e, WasmAstModule& module)
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
EncodeDataSegment(Encoder& e, WasmAstSegment& segment)
|
||||
{
|
||||
if (!e.writeCString(SegmentSubsection))
|
||||
return false;
|
||||
|
||||
if (!e.writeVarU32(segment.offset()))
|
||||
return false;
|
||||
|
||||
TwoByteChars text = segment.text();
|
||||
|
||||
Vector<uint8_t, 0, SystemAllocPolicy> bytes;
|
||||
if (!bytes.reserve(text.length()))
|
||||
return false;
|
||||
|
||||
const char16_t* cur = text.start().get();
|
||||
const char16_t* end = text.end().get();
|
||||
while (cur != end) {
|
||||
uint8_t byte;
|
||||
MOZ_ALWAYS_TRUE(ConsumeTextByte(&cur, end, &byte));
|
||||
bytes.infallibleAppend(byte);
|
||||
}
|
||||
|
||||
if (!e.writeVarU32(bytes.length()))
|
||||
return false;
|
||||
|
||||
if (!e.writeData(bytes.begin(), bytes.length()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
EncodeDataSection(Encoder& e, WasmAstModule& module)
|
||||
{
|
||||
if (!module.maybeMemory() || module.maybeMemory()->segments().empty())
|
||||
return true;
|
||||
|
||||
const WasmAstSegmentVector& segments = module.maybeMemory()->segments();
|
||||
|
||||
if (!e.writeCString(DataSection))
|
||||
return false;
|
||||
|
||||
size_t offset;
|
||||
if (!e.startSection(&offset))
|
||||
return false;
|
||||
|
||||
if (!e.writeVarU32(segments.length()))
|
||||
return false;
|
||||
|
||||
for (WasmAstSegment* segment : segments) {
|
||||
if (!EncodeDataSegment(e, *segment))
|
||||
return false;
|
||||
}
|
||||
|
||||
e.finishSection(offset);
|
||||
return true;
|
||||
}
|
||||
|
||||
static UniqueBytecode
|
||||
AstToBinary(WasmAstModule& module)
|
||||
EncodeModule(WasmAstModule& module)
|
||||
{
|
||||
UniqueBytecode bytecode = MakeUnique<Bytecode>();
|
||||
if (!bytecode)
|
||||
@ -2043,6 +2221,9 @@ AstToBinary(WasmAstModule& module)
|
||||
if (!EncodeCodeSection(e, module))
|
||||
return nullptr;
|
||||
|
||||
if (!EncodeDataSection(e, module))
|
||||
return nullptr;
|
||||
|
||||
if (!e.writeCString(EndSection))
|
||||
return nullptr;
|
||||
|
||||
@ -2055,9 +2236,9 @@ UniqueBytecode
|
||||
wasm::TextToBinary(const char16_t* text, UniqueChars* error)
|
||||
{
|
||||
LifoAlloc lifo(AST_LIFO_DEFAULT_CHUNK_SIZE);
|
||||
WasmAstModule* module = TextToAst(text, lifo, error);
|
||||
WasmAstModule* module = ParseModule(text, lifo, error);
|
||||
if (!module)
|
||||
return nullptr;
|
||||
|
||||
return AstToBinary(*module);
|
||||
return EncodeModule(*module);
|
||||
}
|
||||
|
@ -164,6 +164,27 @@ assertEq(obj.a, obj.b);
|
||||
assertEq(obj.byteLength, 65536);
|
||||
assertEq(obj.a(), 42);
|
||||
|
||||
var buf = wasmEvalText('(module (memory 65536 (segment 0 "")) (export "" memory))');
|
||||
assertEq(new Uint8Array(buf)[0], 0);
|
||||
|
||||
var buf = wasmEvalText('(module (memory 65536 (segment 65536 "")) (export "" memory))');
|
||||
assertEq(new Uint8Array(buf)[0], 0);
|
||||
|
||||
var buf = wasmEvalText('(module (memory 65536 (segment 0 "a")) (export "" memory))');
|
||||
assertEq(new Uint8Array(buf)[0], 'a'.charCodeAt(0));
|
||||
|
||||
var buf = wasmEvalText('(module (memory 65536 (segment 0 "a") (segment 2 "b")) (export "" memory))');
|
||||
assertEq(new Uint8Array(buf)[0], 'a'.charCodeAt(0));
|
||||
assertEq(new Uint8Array(buf)[1], 0);
|
||||
assertEq(new Uint8Array(buf)[2], 'b'.charCodeAt(0));
|
||||
|
||||
var buf = wasmEvalText('(module (memory 65536 (segment 65535 "c")) (export "" memory))');
|
||||
assertEq(new Uint8Array(buf)[0], 0);
|
||||
assertEq(new Uint8Array(buf)[65535], 'c'.charCodeAt(0));
|
||||
|
||||
assertErrorMessage(() => wasmEvalText('(module (memory 65536 (segment 65536 "a")) (export "" memory))'), TypeError, /data segment does not fit/);
|
||||
assertErrorMessage(() => wasmEvalText('(module (memory 65536 (segment 65535 "ab")) (export "" memory))'), TypeError, /data segment does not fit/);
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// locals
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user