Bug 1292724 - Baldr: fix segment offsets (r=bbouvier)

MozReview-Commit-ID: L4sWbIYY0g9

--HG--
extra : rebase_source : fc31c5b1e139bb538ba7731fa56b04ed6d913f4a
This commit is contained in:
Luke Wagner 2016-09-06 09:42:13 -05:00
Родитель e12fcacd53
Коммит c9dc4a5e06
13 изменённых файлов: 249 добавлений и 181 удалений

Просмотреть файл

@ -646,14 +646,15 @@ class AstExport : public AstNode
class AstDataSegment : public AstNode
{
uint32_t offset_;
AstExpr* offset_;
AstName text_;
public:
AstDataSegment(uint32_t offset, AstName text)
AstDataSegment(AstExpr* offset, AstName text)
: offset_(offset), text_(text)
{}
uint32_t offset() const { return offset_; }
AstExpr* offset() const { return offset_; }
AstName text() const { return text_; }
};

Просмотреть файл

@ -1655,16 +1655,12 @@ AstDecodeDataSection(AstDecodeContext &c)
return AstDecodeFail(c, "failed to read number of data segments");
const uint32_t heapLength = c.module().hasMemory() ? c.module().memory().initial() : 0;
uint32_t prevEnd = 0;
for (uint32_t i = 0; i < numSegments; i++) {
uint32_t dstOffset;
if (!c.d.readVarU32(&dstOffset))
return AstDecodeFail(c, "expected segment destination offset");
if (dstOffset < prevEnd)
return AstDecodeFail(c, "data segments must be disjoint and ordered");
uint32_t numBytes;
if (!c.d.readVarU32(&numBytes))
return AstDecodeFail(c, "expected segment size");
@ -1680,12 +1676,14 @@ AstDecodeDataSection(AstDecodeContext &c)
for (size_t i = 0; i < numBytes; i++)
buffer[i] = src[i];
AstName name(buffer, numBytes);
AstDataSegment* segment = new(c.lifo) AstDataSegment(dstOffset, name);
if (!segment || !c.module().append(segment))
AstExpr* offset = new(c.lifo) AstConst(Val(dstOffset));
if (!offset)
return false;
prevEnd = dstOffset + numBytes;
AstName name(buffer, numBytes);
AstDataSegment* segment = new(c.lifo) AstDataSegment(offset, name);
if (!segment || !c.module().append(segment))
return false;
}
if (!c.d.finishSection(sectionStart, sectionSize))

Просмотреть файл

@ -1739,7 +1739,7 @@ PrintDataSection(WasmPrintContext& c, const AstModule& module)
return false;
if (!c.buffer.append("segment "))
return false;
if (!PrintInt32(c, segment->offset()))
if (!PrintInt32(c, segment->offset()->as<AstConst>().val().i32()))
return false;
if (!c.buffer.append(" \""))
return false;

Просмотреть файл

@ -1334,7 +1334,7 @@ RenderDataSection(WasmRenderContext& c, const AstModule& module)
return false;
if (!c.buffer.append("(segment "))
return false;
if (!RenderInt32(c, segment->offset()))
if (!RenderInt32(c, segment->offset()->as<AstConst>().val().i32()))
return false;
if (!c.buffer.append(" \""))
return false;

Просмотреть файл

@ -112,6 +112,7 @@ struct ShareableBytes : ShareableBase<ShareableBytes>
size_t sizeOfExcludingThis(MallocSizeOf m) const { return bytes.sizeOfExcludingThis(m); }
const uint8_t* begin() const { return bytes.begin(); }
const uint8_t* end() const { return bytes.end(); }
size_t length() const { return bytes.length(); }
bool append(const uint8_t *p, uint32_t ct) { return bytes.append(p, ct); }
};

Просмотреть файл

@ -1335,7 +1335,7 @@ DecodeElemSection(Decoder& d, bool newFormat, Uint32Vector&& oldElems, ModuleGen
if (numSegments > MaxElemSegments)
return Fail(d, "too many elem segments");
for (uint32_t i = 0, prevEnd = 0; i < numSegments; i++) {
for (uint32_t i = 0; i < numSegments; i++) {
uint32_t tableIndex;
if (!d.readVarU32(&tableIndex))
return Fail(d, "expected table index");
@ -1348,9 +1348,6 @@ DecodeElemSection(Decoder& d, bool newFormat, Uint32Vector&& oldElems, ModuleGen
if (!DecodeInitializerExpression(d, mg.globals(), ValType::I32, &offset))
return false;
if (offset.isVal() && offset.val().i32() < prevEnd)
return Fail(d, "elem segments must be disjoint and ordered");
uint32_t numElems;
if (!d.readVarU32(&numElems))
return Fail(d, "expected segment size");
@ -1373,9 +1370,6 @@ DecodeElemSection(Decoder& d, bool newFormat, Uint32Vector&& oldElems, ModuleGen
return Fail(d, "table element out of range");
}
if (offset.isVal())
prevEnd = offset.val().i32() + elemFuncIndices.length();
if (!mg.addElemSegment(offset, Move(elemFuncIndices)))
return false;
}
@ -1406,7 +1400,8 @@ DecodeDataSection(Decoder& d, bool newFormat, ModuleGenerator& mg)
return Fail(d, "too many data segments");
uint32_t max = mg.minMemoryLength();
for (uint32_t i = 0, prevEnd = 0; i < numSegments; i++) {
for (uint32_t i = 0; i < numSegments; i++) {
DataSegment seg;
if (newFormat) {
uint32_t linearMemoryIndex;
if (!d.readVarU32(&linearMemoryIndex))
@ -1415,26 +1410,24 @@ DecodeDataSection(Decoder& d, bool newFormat, ModuleGenerator& mg)
if (linearMemoryIndex != 0)
return Fail(d, "linear memory index must currently be 0");
Expr expr;
if (!d.readExpr(&expr))
return Fail(d, "failed to read initializer expression");
if (!DecodeInitializerExpression(d, mg.globals(), ValType::I32, &seg.offset))
return false;
} else {
uint32_t offset;
if (!d.readVarU32(&offset))
return Fail(d, "expected segment destination offset");
if (expr != Expr::I32Const)
return Fail(d, "expected i32.const initializer expression");
seg.offset = InitExpr(Val(offset));
}
DataSegment seg;
if (!d.readVarU32(&seg.memoryOffset))
return Fail(d, "expected segment destination offset");
if (seg.memoryOffset < prevEnd)
return Fail(d, "data segments must be disjoint and ordered");
if (!d.readVarU32(&seg.length))
return Fail(d, "expected segment size");
if (seg.memoryOffset > max || max - seg.memoryOffset < seg.length)
return Fail(d, "data segment data segment does not fit");
if (seg.offset.isVal()) {
uint32_t off = seg.offset.val().i32();
if (off > max || max - off < seg.length)
return Fail(d, "data segment does not fit");
}
seg.bytecodeOffset = d.currentOffset();
@ -1443,8 +1436,6 @@ DecodeDataSection(Decoder& d, bool newFormat, ModuleGenerator& mg)
if (!mg.addDataSegment(seg))
return false;
prevEnd = seg.memoryOffset + seg.length;
}
if (!d.finishSection(sectionStart, sectionSize))

Просмотреть файл

@ -412,69 +412,92 @@ Module::addSizeOfMisc(MallocSizeOf mallocSizeOf,
bytecode_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenBytes);
}
static uint32_t
EvaluateInitExpr(const ValVector& globalImports, InitExpr initExpr)
{
switch (initExpr.kind()) {
case InitExpr::Kind::Constant:
return initExpr.val().i32();
case InitExpr::Kind::GetGlobal:
return globalImports[initExpr.globalIndex()].i32();
}
MOZ_CRASH("bad initializer expression");
}
bool
Module::initElems(JSContext* cx, HandleWasmInstanceObject instanceObj,
const ValVector& globalImports, HandleWasmTableObject tableObj) const
Module::initSegments(JSContext* cx,
HandleWasmInstanceObject instanceObj,
HandleWasmMemoryObject memoryObj,
const ValVector& globalImports) const
{
Instance& instance = instanceObj->instance();
const SharedTableVector& tables = instance.tables();
// Perform all error checks up front so that this function does not perform
// partial initialization if an error is reported.
for (const ElemSegment& seg : elemSegments_) {
uint32_t tableLength = tables[seg.tableIndex]->length();
uint32_t offset = EvaluateInitExpr(globalImports, seg.offset);
if (offset > tableLength || tableLength - offset < seg.elemCodeRangeIndices.length()) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_FIT, "elem", "table");
return false;
}
}
if (memoryObj) {
for (const DataSegment& seg : dataSegments_) {
uint32_t memoryLength = memoryObj->buffer().byteLength();
uint32_t offset = EvaluateInitExpr(globalImports, seg.offset);
if (offset > memoryLength || memoryLength - offset < seg.length) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_FIT, "data", "memory");
return false;
}
}
} else {
MOZ_ASSERT(dataSegments_.empty());
}
// Ensure all tables are initialized before storing into them.
for (const SharedTable& table : tables) {
if (!table->initialized())
table->init(instance);
}
// Now that all tables have been initialized, write elements.
Vector<uint32_t> prevEnds(cx);
if (!prevEnds.appendN(0, tables.length()))
return false;
// Now that initialization can't fail partway through, write data/elem
// segments into memories/tables.
for (const ElemSegment& seg : elemSegments_) {
Table& table = *tables[seg.tableIndex];
uint32_t offset;
switch (seg.offset.kind()) {
case InitExpr::Kind::Constant: {
offset = seg.offset.val().i32();
break;
}
case InitExpr::Kind::GetGlobal: {
const GlobalDesc& global = metadata_->globals[seg.offset.globalIndex()];
offset = globalImports[global.importIndex()].i32();
break;
}
}
uint32_t& prevEnd = prevEnds[seg.tableIndex];
if (offset < prevEnd) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_FAIL,
"elem segments must be disjoint and ordered");
return false;
}
uint32_t tableLength = instance.metadata().tables[seg.tableIndex].initial;
if (offset > tableLength || tableLength - offset < seg.elemCodeRangeIndices.length()) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_FAIL,
"element segment does not fit");
return false;
}
uint32_t offset = EvaluateInitExpr(globalImports, seg.offset);
bool profilingEnabled = instance.code().profilingEnabled();
const CodeRangeVector& codeRanges = instance.code().metadata().codeRanges;
const CodeRangeVector& codeRanges = metadata().codeRanges;
uint8_t* codeBase = instance.codeBase();
for (uint32_t i = 0; i < seg.elemCodeRangeIndices.length(); i++) {
const CodeRange& cr = codeRanges[seg.elemCodeRangeIndices[i]];
uint32_t codeOffset = table.isTypedFunction()
? profilingEnabled
? cr.funcProfilingEntry()
: cr.funcNonProfilingEntry()
: cr.funcTableEntry();
table.set(offset + i, codeBase + codeOffset, instance);
uint32_t entryOffset = table.isTypedFunction()
? profilingEnabled
? cr.funcProfilingEntry()
: cr.funcNonProfilingEntry()
: cr.funcTableEntry();
table.set(offset + i, codeBase + entryOffset, instance);
}
}
prevEnd = offset + seg.elemFuncIndices.length();
if (memoryObj) {
uint8_t* memoryBase = memoryObj->buffer().dataPointerEither().unwrap(/* memcpy */);
for (const DataSegment& seg : dataSegments_) {
MOZ_ASSERT(seg.bytecodeOffset <= bytecode_->length());
MOZ_ASSERT(seg.length <= bytecode_->length() - seg.bytecodeOffset);
uint32_t offset = EvaluateInitExpr(globalImports, seg.offset);
memcpy(memoryBase + offset, bytecode_->begin() + seg.bytecodeOffset, seg.length);
}
}
return true;
@ -518,16 +541,16 @@ Module::instantiateMemory(JSContext* cx, MutableHandleWasmMemoryObject memory) c
return true;
}
RootedArrayBufferObjectMaybeShared buffer(cx);
if (memory) {
buffer = &memory->buffer();
uint32_t length = buffer->wasmActualByteLength();
uint32_t declaredMaxLength = metadata_->maxMemoryLength.valueOr(UINT32_MAX);
uint32_t declaredMin = metadata_->minMemoryLength;
Maybe<uint32_t> declaredMax = metadata_->maxMemoryLength;
// It's not an error to import a memory whose mapped size is less than
// the maxMemoryLength required for the module. This is the same as trying to
// map up to maxMemoryLength but actually getting less.
if (length < metadata_->minMemoryLength || length > declaredMaxLength) {
if (memory) {
RootedArrayBufferObjectMaybeShared buffer(cx, &memory->buffer());
MOZ_RELEASE_ASSERT(buffer->is<SharedArrayBufferObject>() ||
buffer->as<ArrayBufferObject>().isWasm());
uint32_t actualLength = buffer->wasmActualByteLength();
if (actualLength < declaredMin || actualLength > declaredMax.valueOr(UINT32_MAX)) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_IMP_SIZE, "Memory");
return false;
}
@ -536,27 +559,18 @@ Module::instantiateMemory(JSContext* cx, MutableHandleWasmMemoryObject memory) c
// For wasm we require that either both memory and module don't specify a max size
// OR that the memory's max size is less than the modules.
if (!metadata_->isAsmJS()) {
Maybe<uint32_t> memMaxSize =
buffer->as<ArrayBufferObject>().wasmMaxSize();
if (metadata_->maxMemoryLength.isSome() != memMaxSize.isSome() ||
metadata_->maxMemoryLength < memMaxSize) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_IMP_SIZE,
"Memory");
Maybe<uint32_t> actualMax = buffer->as<ArrayBufferObject>().wasmMaxSize();
if (declaredMax.isSome() != actualMax.isSome() || declaredMax < actualMax) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_IMP_SIZE, "Memory");
return false;
}
}
MOZ_RELEASE_ASSERT(buffer->is<SharedArrayBufferObject>() ||
buffer->as<ArrayBufferObject>().isWasm());
// We currently assume SharedArrayBuffer => asm.js. Can remove this
// once wasmMaxSize/mappedSize/growForWasm have been implemented in SAB
MOZ_ASSERT_IF(buffer->is<SharedArrayBufferObject>(), metadata_->isAsmJS());
} else {
buffer = ArrayBufferObject::createForWasm(cx, metadata_->minMemoryLength,
metadata_->maxMemoryLength);
MOZ_ASSERT(!metadata_->isAsmJS());
MOZ_ASSERT(metadata_->memoryUsage == MemoryUsage::Unshared);
RootedArrayBufferObjectMaybeShared buffer(cx,
ArrayBufferObject::createForWasm(cx, declaredMin, declaredMax));
if (!buffer)
return false;
@ -569,12 +583,6 @@ Module::instantiateMemory(JSContext* cx, MutableHandleWasmMemoryObject memory) c
return false;
}
MOZ_ASSERT(buffer->is<SharedArrayBufferObject>() || buffer->as<ArrayBufferObject>().isWasm());
uint8_t* memoryBase = memory->buffer().dataPointerEither().unwrap(/* memcpy */);
for (const DataSegment& seg : dataSegments_)
memcpy(memoryBase + seg.memoryOffset, bytecode_->begin() + seg.bytecodeOffset, seg.length);
return true;
}
@ -740,7 +748,7 @@ Module::instantiate(JSContext* cx,
HandleWasmMemoryObject memoryImport,
const ValVector& globalImports,
HandleObject instanceProto,
MutableHandleWasmInstanceObject instanceObj) const
MutableHandleWasmInstanceObject instance) const
{
if (!instantiateFunctions(cx, funcImports))
return false;
@ -773,18 +781,18 @@ Module::instantiate(JSContext* cx,
if (!code)
return false;
instanceObj.set(WasmInstanceObject::create(cx,
Move(code),
memory,
Move(tables),
funcImports,
globalImports,
instanceProto));
if (!instanceObj)
instance.set(WasmInstanceObject::create(cx,
Move(code),
memory,
Move(tables),
funcImports,
globalImports,
instanceProto));
if (!instance)
return false;
RootedObject exportObj(cx);
if (!CreateExportObject(cx, instanceObj, table, memory, globalImports, exports_, &exportObj))
if (!CreateExportObject(cx, instance, table, memory, globalImports, exports_, &exportObj))
return false;
JSAtom* atom = Atomize(cx, InstanceExportField, strlen(InstanceExportField));
@ -793,31 +801,31 @@ Module::instantiate(JSContext* cx,
RootedId id(cx, AtomToId(atom));
RootedValue val(cx, ObjectValue(*exportObj));
if (!JS_DefinePropertyById(cx, instanceObj, id, val, JSPROP_ENUMERATE))
if (!JS_DefinePropertyById(cx, instance, id, val, JSPROP_ENUMERATE))
return false;
// Register the instance with the JSCompartment so that it can find out
// about global events like profiling being enabled in the compartment.
// Registration does not require a fully-initialized instance and must
// precede initSegments as the final pre-requisite for a live instance.
if (!cx->compartment()->wasm.registerInstance(cx, instanceObj))
if (!cx->compartment()->wasm.registerInstance(cx, instance))
return false;
// Initialize table elements only after the instance is fully initialized
// since the Table object needs to point to a valid instance object. Perform
// initialization as the final step after the instance is fully live since
// it is observable (in the case of an imported Table object) and can't be
// easily rolled back in case of error.
// Perform initialization as the final step after the instance is fully
// constructed since this can make the instance live to content (even if the
// start function fails).
if (!initElems(cx, instanceObj, globalImports, table))
if (!initSegments(cx, instance, memory, globalImports))
return false;
// Call the start function, if there's one. This effectively makes the
// instance object live to content and thus must go after initialization is
// complete.
// Now that the instance is fully live and initialized, the start function.
// Note that failure may cause instantiation to throw, but the instance may
// still be live via edges created by initSegments or the start function.
if (metadata_->hasStartFunction()) {
FixedInvokeArgs<0> args(cx);
if (!instanceObj->instance().callExport(cx, metadata_->startFuncIndex(), args))
if (!instance->instance().callExport(cx, metadata_->startFuncIndex(), args))
return false;
}

Просмотреть файл

@ -149,7 +149,7 @@ typedef Vector<Export, 0, SystemAllocPolicy> ExportVector;
struct DataSegment
{
uint32_t memoryOffset;
InitExpr offset;
uint32_t bytecodeOffset;
uint32_t length;
};
@ -209,10 +209,13 @@ class Module : public RefCounted<Module>
bool instantiateFunctions(JSContext* cx, Handle<FunctionVector> funcImports) const;
bool instantiateMemory(JSContext* cx, MutableHandleWasmMemoryObject memory) const;
bool instantiateTable(JSContext* cx, MutableHandleWasmTableObject table,
bool instantiateTable(JSContext* cx,
MutableHandleWasmTableObject table,
SharedTableVector* tables) const;
bool initElems(JSContext* cx, HandleWasmInstanceObject instanceObj,
const ValVector& globalImports, HandleWasmTableObject tableObj) const;
bool initSegments(JSContext* cx,
HandleWasmInstanceObject instance,
HandleWasmMemoryObject memory,
const ValVector& globalImports) const;
public:
Module(Bytes&& code,

Просмотреть файл

@ -2403,17 +2403,32 @@ ParseTypeDef(WasmParseContext& c)
}
static AstDataSegment*
ParseDataSegment(WasmParseContext& c)
ParseDataSegment(WasmParseContext& c, bool newFormat)
{
WasmToken dstOffset;
if (!c.ts.match(WasmToken::Index, &dstOffset, c.error))
return nullptr;
AstExpr* offset;
if (newFormat) {
WasmToken dstOffset;
if (c.ts.getIf(WasmToken::Index, &dstOffset))
offset = new(c.lifo) AstConst(Val(dstOffset.index()));
else
offset = ParseExpr(c);
if (!offset)
return nullptr;
} else {
WasmToken dstOffset;
if (!c.ts.match(WasmToken::Index, &dstOffset, c.error))
return nullptr;
offset = new(c.lifo) AstConst(Val(dstOffset.index()));
if (!offset)
return nullptr;
}
WasmToken text;
if (!c.ts.match(WasmToken::Text, &text, c.error))
return nullptr;
return new(c.lifo) AstDataSegment(dstOffset.index(), text.text());
return new(c.lifo) AstDataSegment(offset, text.text());
}
static bool
@ -2442,7 +2457,7 @@ ParseMemory(WasmParseContext& c, WasmToken token, AstModule* module)
while (c.ts.getIf(WasmToken::OpenParen)) {
if (!c.ts.match(WasmToken::Segment, c.error))
return false;
AstDataSegment* segment = ParseDataSegment(c);
AstDataSegment* segment = ParseDataSegment(c, /* newFormat = */ false);
if (!segment || !module->append(segment))
return false;
if (!c.ts.match(WasmToken::CloseParen, c.error))
@ -2706,7 +2721,7 @@ ParseModule(const char16_t* text, bool newFormat, LifoAlloc& lifo, UniqueChars*
break;
}
case WasmToken::Data: {
AstDataSegment* segment = ParseDataSegment(c);
AstDataSegment* segment = ParseDataSegment(c, newFormat);
if (!segment || !module->append(segment))
return nullptr;
break;
@ -3213,13 +3228,6 @@ ResolveModule(LifoAlloc& lifo, AstModule* module, UniqueChars* error)
return r.fail("duplicate function");
}
for (AstElemSegment* seg : module->elemSegments()) {
for (AstRef& ref : seg->elems()) {
if (!r.resolveFunction(ref))
return false;
}
}
size_t numImports = module->imports().length();
size_t lastFuncImportIndex = 0;
size_t lastGlobalIndex = 0;
@ -3276,9 +3284,18 @@ ResolveModule(LifoAlloc& lifo, AstModule* module, UniqueChars* error)
return false;
}
for (AstDataSegment* segment : module->dataSegments()) {
if (!ResolveExpr(r, *segment->offset()))
return false;
}
for (AstElemSegment* segment : module->elemSegments()) {
if (!ResolveExpr(r, *segment->offset()))
return false;
for (AstRef& ref : segment->elems()) {
if (!r.resolveFunction(ref))
return false;
}
}
return true;
@ -4056,13 +4073,15 @@ EncodeDataSegment(Encoder& e, bool newFormat, AstDataSegment& segment)
if (!e.writeVarU32(0)) // linear memory index
return false;
if (!e.writeExpr(Expr::I32Const))
if (!EncodeExpr(e, *segment.offset()))
return false;
if (!e.writeExpr(Expr::End))
return false;
} else {
if (!e.writeVarU32(segment.offset()->as<AstConst>().val().i32()))
return false;
}
if (!e.writeVarU32(segment.offset()))
return false;
AstName text = segment.text();
Vector<uint8_t, 0, SystemAllocPolicy> bytes;
@ -4187,10 +4206,10 @@ EncodeModule(AstModule& module, bool newFormat, Bytes* bytes)
if (!EncodeCodeSection(e, module))
return false;
if (!EncodeDataSection(e, newFormat, module))
if (!EncodeElemSection(e, newFormat, module))
return false;
if (!EncodeElemSection(e, newFormat, module))
if (!EncodeDataSection(e, newFormat, module))
return false;
return true;

Просмотреть файл

@ -352,6 +352,60 @@ assertEq(i8[100], 0xc);
assertEq(i8[101], 0xd);
assertEq(i8[102], 0x0);
// Data segments with imported offsets
var m = new Module(textToBinary(`
(module
(import "glob" "a" (global i32 immutable))
(memory 1)
(data (get_global 0) "\\0a\\0b"))
`));
assertEq(new Instance(m, {glob:{a:0}}) instanceof Instance, true);
assertEq(new Instance(m, {glob:{a:(64*1024 - 2)}}) instanceof Instance, true);
assertErrorMessage(() => new Instance(m, {glob:{a:(64*1024 - 1)}}), RangeError, /data segment does not fit/);
assertErrorMessage(() => new Instance(m, {glob:{a:64*1024}}), RangeError, /data segment does not fit/);
// Errors during segment initialization do not have observable effects
// and are checked against the actual memory/table length, not the declared
// initial length.
var m = new Module(textToBinary(`
(module
(import "a" "mem" (memory 1))
(import "a" "tbl" (table 1))
(import $memOff "a" "memOff" (global i32 immutable))
(import $tblOff "a" "tblOff" (global i32 immutable))
(func $f)
(func $g)
(data (i32.const 0) "\\01")
(elem (i32.const 0) $f)
(data (get_global $memOff) "\\02")
(elem (get_global $tblOff) $g)
(export "f" $f)
(export "g" $g))
`));
var npages = 2;
var mem = new Memory({initial:npages});
var mem8 = new Uint8Array(mem.buffer);
var tbl = new Table({initial:2, element:"anyfunc"});
assertErrorMessage(() => new Instance(m, {a:{mem, tbl, memOff:1, tblOff:2}}), RangeError, /elem segment does not fit/);
assertEq(mem8[0], 0);
assertEq(mem8[1], 0);
assertEq(tbl.get(0), null);
assertErrorMessage(() => new Instance(m, {a:{mem, tbl, memOff:npages*64*1024, tblOff:1}}), RangeError, /data segment does not fit/);
assertEq(mem8[0], 0);
assertEq(tbl.get(0), null);
assertEq(tbl.get(1), null);
var i = new Instance(m, {a:{mem, tbl, memOff:npages*64*1024-1, tblOff:1}});
assertEq(mem8[0], 1);
assertEq(mem8[npages*64*1024-1], 2);
assertEq(tbl.get(0), i.exports.f);
assertEq(tbl.get(1), i.exports.g);
// Elem segments on imports
var m = new Module(textToBinary(`

Просмотреть файл

@ -19,18 +19,6 @@
(module (memory 1 2 (segment 0 "a") (segment 98304 "b")))
"data segment does not fit memory"
)
(assert_invalid
(module (memory 1 2 (segment 0 "abc") (segment 0 "def")))
"data segment not disjoint and ordered"
)
(assert_invalid
(module (memory 1 2 (segment 3 "ab") (segment 0 "de")))
"data segment not disjoint and ordered"
)
(assert_invalid
(module (memory 1 2 (segment 0 "a") (segment 2 "b") (segment 1 "c")))
"data segment not disjoint and ordered"
)
;; Test alignment annotation rules
;; TODO Tests being debated on the spec repo.

Просмотреть файл

@ -22,21 +22,25 @@ assertErrorMessage(() => new Module(textToBinary(`(module (table (resizable 10))
assertErrorMessage(() => new Module(textToBinary(`(module (table (resizable 10)) (elem (i32.const 10) $f0) ${callee(0)})`)), TypeError, /element segment does not fit/);
assertErrorMessage(() => new Module(textToBinary(`(module (table (resizable 10)) (elem (i32.const 8) $f0 $f0 $f0) ${callee(0)})`)), TypeError, /element segment does not fit/);
assertErrorMessage(() => new Module(textToBinary(`(module (table (resizable 10)) (elem (i32.const 1) $f0 $f0) (elem (i32.const 0) $f0) ${callee(0)})`)), TypeError, /must be.*ordered/);
assertErrorMessage(() => new Module(textToBinary(`(module (table (resizable 10)) (elem (i32.const 1) $f0 $f0) (elem (i32.const 2) $f0) ${callee(0)})`)), TypeError, /must be.*disjoint/);
assertErrorMessage(() => evalText(`(module (table (resizable 10)) (import "globals" "a" (global i32 immutable)) (elem (get_global 0) $f0) ${callee(0)})`, {globals:{a:10}}), TypeError, /element segment does not fit/);
assertErrorMessage(() => evalText(`(module (table (resizable 10)) (import "globals" "a" (global i32 immutable)) (elem (get_global 0) $f0 $f0 $f0) ${callee(0)})`, {globals:{a:8}}), TypeError, /element segment does not fit/);
assertErrorMessage(() => evalText(`(module (table (resizable 10)) (import "globals" "a" (global i32 immutable)) (elem (i32.const 1) $f0 $f0) (elem (get_global 0) $f0) ${callee(0)})`, {globals:{a:0}}), TypeError, /must be.*ordered/);
assertErrorMessage(() => evalText(`(module (table (resizable 10)) (import "globals" "a" (global i32 immutable)) (elem (get_global 0) $f0 $f0) (elem (i32.const 2) $f0) ${callee(0)})`, {globals:{a:1}}), TypeError, /must be.*disjoint/);
assertErrorMessage(() => evalText(`(module (table (resizable 10)) (import "globals" "a" (global i32 immutable)) (elem (get_global 0) $f0) ${callee(0)})`, {globals:{a:10}}), RangeError, /elem segment does not fit/);
assertErrorMessage(() => evalText(`(module (table (resizable 10)) (import "globals" "a" (global i32 immutable)) (elem (get_global 0) $f0 $f0 $f0) ${callee(0)})`, {globals:{a:8}}), RangeError, /elem segment does not fit/);
assertEq(new Module(textToBinary(`(module (table (resizable 10)) (elem (i32.const 1) $f0 $f0) (elem (i32.const 0) $f0) ${callee(0)})`)) instanceof Module, true);
assertEq(new Module(textToBinary(`(module (table (resizable 10)) (elem (i32.const 1) $f0 $f0) (elem (i32.const 2) $f0) ${callee(0)})`)) instanceof Module, true);
evalText(`(module (table (resizable 10)) (import "globals" "a" (global i32 immutable)) (elem (i32.const 1) $f0 $f0) (elem (get_global 0) $f0) ${callee(0)})`, {globals:{a:0}});
evalText(`(module (table (resizable 10)) (import "globals" "a" (global i32 immutable)) (elem (get_global 0) $f0 $f0) (elem (i32.const 2) $f0) ${callee(0)})`, {globals:{a:1}});
var m = new Module(textToBinary(`
(module
(import "globals" "table" (table 10))
(import "globals" "a" (global i32 immutable))
(elem (get_global 0) $f0 $f0)
${callee(0)})
`));
var tbl = new Table({initial:50, element:"anyfunc"});
assertErrorMessage(() => evalText(`(module
(import "globals" "table" (table 10 100))
(import "globals" "a" (global i32 immutable))
(elem (get_global 0) $f0 $f0)
${callee(0)})
`, {globals:{a:20, table:tbl}}), Error, /element segment does not fit/);
assertEq(new Instance(m, {globals:{a:20, table:tbl}}) instanceof Instance, true);
assertErrorMessage(() => new Instance(m, {globals:{a:50, table:tbl}}), RangeError, /elem segment does not fit/);
var caller = `(type $v2i (func (result i32))) (func $call (param $i i32) (result i32) (call_indirect $v2i (get_local $i))) (export "call" $call)`
var callee = i => `(func $f${i} (type $v2i) (result i32) (i32.const ${i}))`;

Просмотреть файл

@ -360,6 +360,7 @@ MSG_DEF(JSMSG_WASM_BAD_IMPORT_FIELD, 1, JSEXN_TYPEERR, "import object fiel
MSG_DEF(JSMSG_WASM_BAD_IMPORT_SIG, 0, JSEXN_TYPEERR, "imported function signature mismatch")
MSG_DEF(JSMSG_WASM_BAD_SET_VALUE, 0, JSEXN_TYPEERR, "second argument must be null or an exported WebAssembly Function object")
MSG_DEF(JSMSG_WASM_BAD_I64, 0, JSEXN_TYPEERR, "cannot pass i64 to or from JS")
MSG_DEF(JSMSG_WASM_BAD_FIT, 2, JSEXN_RANGEERR, "{0} segment does not fit in {1}")
MSG_DEF(JSMSG_WASM_UNREACHABLE, 0, JSEXN_ERR, "unreachable executed")
MSG_DEF(JSMSG_WASM_INTEGER_OVERFLOW, 0, JSEXN_ERR, "integer overflow")
MSG_DEF(JSMSG_WASM_INVALID_CONVERSION, 0, JSEXN_ERR, "invalid conversion to integer")