Bug 1284155 - Baldr: add support for same-instance Table imports (r=bbouvier)

MozReview-Commit-ID: DMMtDimNiAt

--HG--
extra : rebase_source : dba9107260d5f2f697f95e05258a8a5b76f88faa
This commit is contained in:
Luke Wagner 2016-07-19 15:49:31 -05:00
Родитель 27473ec09d
Коммит c7232efb25
19 изменённых файлов: 224 добавлений и 76 удалений

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

@ -1677,6 +1677,7 @@ class MOZ_STACK_CLASS ModuleValidator
importMap_(cx), importMap_(cx),
arrayViews_(cx), arrayViews_(cx),
atomicsPresent_(false), atomicsPresent_(false),
mg_(ImportVector()),
errorString_(nullptr), errorString_(nullptr),
errorOffset_(UINT32_MAX), errorOffset_(UINT32_MAX),
errorOverRecursed_(false) errorOverRecursed_(false)
@ -2303,17 +2304,13 @@ class MOZ_STACK_CLASS ModuleValidator
uint32_t endAfterCurly = pos.end; uint32_t endAfterCurly = pos.end;
asmJSMetadata_->srcLengthWithRightBrace = endAfterCurly - asmJSMetadata_->srcStart; asmJSMetadata_->srcLengthWithRightBrace = endAfterCurly - asmJSMetadata_->srcStart;
// asm.js has its own, different, version of imports through
// AsmJSGlobal.
ImportVector imports;
// asm.js does not have any wasm bytecode to save; view-source is // asm.js does not have any wasm bytecode to save; view-source is
// provided through the ScriptSource. // provided through the ScriptSource.
SharedBytes bytes = js_new<ShareableBytes>(); SharedBytes bytes = js_new<ShareableBytes>();
if (!bytes) if (!bytes)
return nullptr; return nullptr;
return mg_.finish(Move(imports), *bytes); return mg_.finish(*bytes);
} }
}; };

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

@ -530,15 +530,15 @@ class AstImport : public AstNode
AstImport(AstName name, AstName module, AstName field, AstRef funcSig) AstImport(AstName name, AstName module, AstName field, AstRef funcSig)
: name_(name), module_(module), field_(field), kind_(DefinitionKind::Function), funcSig_(funcSig) : name_(name), module_(module), field_(field), kind_(DefinitionKind::Function), funcSig_(funcSig)
{} {}
AstImport(AstName name, AstName module, AstName field, AstResizable resizable) AstImport(AstName name, AstName module, AstName field, DefinitionKind kind, AstResizable resizable)
: name_(name), module_(module), field_(field), kind_(DefinitionKind::Memory), resizable_(resizable) : name_(name), module_(module), field_(field), kind_(kind), resizable_(resizable)
{} {}
AstName name() const { return name_; } AstName name() const { return name_; }
AstName module() const { return module_; } AstName module() const { return module_; }
AstName field() const { return field_; } AstName field() const { return field_; }
DefinitionKind kind() const { return kind_; } DefinitionKind kind() const { return kind_; }
AstRef& funcSig() { MOZ_ASSERT(kind_ == DefinitionKind::Function); return funcSig_; } AstRef& funcSig() { MOZ_ASSERT(kind_ == DefinitionKind::Function); return funcSig_; }
AstResizable memory() const { MOZ_ASSERT(kind_ == DefinitionKind::Memory); return resizable_; } AstResizable resizable() const { MOZ_ASSERT(kind_ != DefinitionKind::Function); return resizable_; }
}; };
class AstExport : public AstNode class AstExport : public AstNode

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

@ -5200,7 +5200,7 @@ BaseCompiler::emitCallIndirect(uint32_t callOffset)
? mg_.tables[mg_.asmJSSigToTableIndex[sigIndex]] ? mg_.tables[mg_.asmJSSigToTableIndex[sigIndex]]
: mg_.tables[0]; : mg_.tables[0];
funcPtrCall(sig, sigIndex, table.length, table.globalDataOffset, callee, baselineCall); funcPtrCall(sig, sigIndex, table.initial, table.globalDataOffset, callee, baselineCall);
endCall(baselineCall); endCall(baselineCall);

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

@ -232,10 +232,11 @@ struct TableDesc
{ {
TableKind kind; TableKind kind;
uint32_t globalDataOffset; uint32_t globalDataOffset;
uint32_t length; uint32_t initial;
uint32_t maximum;
TableDesc() : kind(TableKind::AnyFunction), globalDataOffset(0), length(0) {} TableDesc() { PodZero(this); }
explicit TableDesc(TableKind kind) : kind(kind) {} explicit TableDesc(TableKind kind) : kind(kind), globalDataOffset(0), initial(0), maximum(0) {}
}; };
WASM_DECLARE_POD_VECTOR(TableDesc, TableDescVector) WASM_DECLARE_POD_VECTOR(TableDesc, TableDescVector)

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

@ -714,7 +714,8 @@ DecodeResizableTable(Decoder& d, ModuleGeneratorData* init)
return Fail(d, "already have default table"); return Fail(d, "already have default table");
TableDesc table(TableKind::AnyFunction); TableDesc table(TableKind::AnyFunction);
table.length = resizable.initial; table.initial = resizable.initial;
table.maximum = resizable.maximum ? *resizable.maximum : UINT32_MAX;
return init->tables.append(table); return init->tables.append(table);
} }
@ -772,6 +773,11 @@ DecodeImport(Decoder& d, bool newFormat, ModuleGeneratorData* init, ImportVector
return false; return false;
break; break;
} }
case DefinitionKind::Table: {
if (!DecodeResizableTable(d, init))
return false;
break;
}
case DefinitionKind::Memory: { case DefinitionKind::Memory: {
if (!DecodeResizableMemory(d, init)) if (!DecodeResizableMemory(d, init))
return false; return false;
@ -825,17 +831,18 @@ DecodeTableSection(Decoder& d, bool newFormat, ModuleGeneratorData* init, Uint32
return false; return false;
} else { } else {
TableDesc table(TableKind::AnyFunction); TableDesc table(TableKind::AnyFunction);
table.maximum = UINT32_MAX;
if (!d.readVarU32(&table.length)) if (!d.readVarU32(&table.initial))
return Fail(d, "expected number of table elems"); return Fail(d, "expected number of table elems");
if (table.length > MaxTableElems) if (table.initial > MaxTableElems)
return Fail(d, "too many table elements"); return Fail(d, "too many table elements");
if (!oldElems->resize(table.length)) if (!oldElems->resize(table.initial))
return false; return false;
for (uint32_t i = 0; i < table.length; i++) { for (uint32_t i = 0; i < table.initial; i++) {
uint32_t funcIndex; uint32_t funcIndex;
if (!d.readVarU32(&funcIndex)) if (!d.readVarU32(&funcIndex))
return Fail(d, "expected table element"); return Fail(d, "expected table element");
@ -1214,7 +1221,7 @@ DecodeElemSection(Decoder& d, bool newFormat, Uint32Vector&& oldElems, ModuleGen
if (!d.readVarU32(&numElems)) if (!d.readVarU32(&numElems))
return Fail(d, "expected segment size"); return Fail(d, "expected segment size");
uint32_t tableLength = mg.tables()[seg.tableIndex].length; uint32_t tableLength = mg.tables()[seg.tableIndex].initial;
if (seg.offset > tableLength || tableLength - seg.offset < numElems) if (seg.offset > tableLength || tableLength - seg.offset < numElems)
return Fail(d, "element segment does not fit"); return Fail(d, "element segment does not fit");
@ -1425,7 +1432,7 @@ wasm::Compile(const ShareableBytes& bytecode, CompileArgs&& args, UniqueChars* e
if (!DecodeMemorySection(d, newFormat, init.get(), &memoryExported)) if (!DecodeMemorySection(d, newFormat, init.get(), &memoryExported))
return nullptr; return nullptr;
ModuleGenerator mg; ModuleGenerator mg(Move(imports));
if (!mg.init(Move(init), Move(args))) if (!mg.init(Move(init), Move(args)))
return nullptr; return nullptr;
@ -1452,5 +1459,5 @@ wasm::Compile(const ShareableBytes& bytecode, CompileArgs&& args, UniqueChars* e
MOZ_ASSERT(!*error, "unreported error in decoding"); MOZ_ASSERT(!*error, "unreported error in decoding");
return mg.finish(Move(imports), bytecode); return mg.finish(bytecode);
} }

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

@ -42,8 +42,9 @@ using mozilla::MakeEnumeratedRange;
static const unsigned GENERATOR_LIFO_DEFAULT_CHUNK_SIZE = 4 * 1024; static const unsigned GENERATOR_LIFO_DEFAULT_CHUNK_SIZE = 4 * 1024;
static const unsigned COMPILATION_LIFO_DEFAULT_CHUNK_SIZE = 64 * 1024; static const unsigned COMPILATION_LIFO_DEFAULT_CHUNK_SIZE = 64 * 1024;
ModuleGenerator::ModuleGenerator() ModuleGenerator::ModuleGenerator(ImportVector&& imports)
: alwaysBaseline_(false), : alwaysBaseline_(false),
imports_(Move(imports)),
numSigs_(0), numSigs_(0),
numTables_(0), numTables_(0),
lifo_(GENERATOR_LIFO_DEFAULT_CHUNK_SIZE), lifo_(GENERATOR_LIFO_DEFAULT_CHUNK_SIZE),
@ -51,7 +52,7 @@ ModuleGenerator::ModuleGenerator()
masm_(MacroAssembler::AsmJSToken(), masmAlloc_), masm_(MacroAssembler::AsmJSToken(), masmAlloc_),
lastPatchedCallsite_(0), lastPatchedCallsite_(0),
startOfUnpatchedBranches_(0), startOfUnpatchedBranches_(0),
tableExported_(false), externalTable_(false),
parallel_(false), parallel_(false),
outstanding_(0), outstanding_(0),
activeFunc_(nullptr), activeFunc_(nullptr),
@ -59,6 +60,13 @@ ModuleGenerator::ModuleGenerator()
finishedFuncDefs_(false) finishedFuncDefs_(false)
{ {
MOZ_ASSERT(IsCompilingAsmJS()); MOZ_ASSERT(IsCompilingAsmJS());
for (const Import& import : imports_) {
if (import.kind == DefinitionKind::Table) {
externalTable_ = true;
break;
}
}
} }
ModuleGenerator::~ModuleGenerator() ModuleGenerator::~ModuleGenerator()
@ -695,7 +703,7 @@ bool
ModuleGenerator::addTableExport(UniqueChars fieldName) ModuleGenerator::addTableExport(UniqueChars fieldName)
{ {
MOZ_ASSERT(elemSegments_.empty()); MOZ_ASSERT(elemSegments_.empty());
tableExported_ = true; externalTable_ = true;
return exports_.emplaceBack(Move(fieldName), DefinitionKind::Table); return exports_.emplaceBack(Move(fieldName), DefinitionKind::Table);
} }
@ -843,9 +851,9 @@ ModuleGenerator::finishFuncDefs()
bool bool
ModuleGenerator::addElemSegment(ElemSegment&& seg) ModuleGenerator::addElemSegment(ElemSegment&& seg)
{ {
MOZ_ASSERT(seg.offset + seg.elems.length() <= shared_->tables[seg.tableIndex].length); MOZ_ASSERT(seg.offset + seg.elems.length() <= shared_->tables[seg.tableIndex].initial);
if (tableExported_) { if (externalTable_) {
for (uint32_t funcIndex : seg.elems) { for (uint32_t funcIndex : seg.elems) {
if (!exportedFuncs_.put(funcIndex)) if (!exportedFuncs_.put(funcIndex))
return false; return false;
@ -874,9 +882,10 @@ ModuleGenerator::initSigTableLength(uint32_t sigIndex, uint32_t length)
TableDesc& table = shared_->tables[numTables_++]; TableDesc& table = shared_->tables[numTables_++];
MOZ_ASSERT(table.globalDataOffset == 0); MOZ_ASSERT(table.globalDataOffset == 0);
MOZ_ASSERT(table.length == 0); MOZ_ASSERT(table.initial == 0);
table.kind = TableKind::TypedFunction; table.kind = TableKind::TypedFunction;
table.length = length; table.initial = length;
table.maximum = UINT32_MAX;
return allocateGlobalBytes(sizeof(void*), sizeof(void*), &table.globalDataOffset); return allocateGlobalBytes(sizeof(void*), sizeof(void*), &table.globalDataOffset);
} }
@ -886,13 +895,13 @@ ModuleGenerator::initSigTableElems(uint32_t sigIndex, Uint32Vector&& elemFuncInd
MOZ_ASSERT(isAsmJS()); MOZ_ASSERT(isAsmJS());
uint32_t tableIndex = shared_->asmJSSigToTableIndex[sigIndex]; uint32_t tableIndex = shared_->asmJSSigToTableIndex[sigIndex];
MOZ_ASSERT(shared_->tables[tableIndex].length == elemFuncIndices.length()); MOZ_ASSERT(shared_->tables[tableIndex].initial == elemFuncIndices.length());
return elemSegments_.emplaceBack(tableIndex, 0, Move(elemFuncIndices)); return elemSegments_.emplaceBack(tableIndex, 0, Move(elemFuncIndices));
} }
SharedModule SharedModule
ModuleGenerator::finish(ImportVector&& imports, const ShareableBytes& bytecode) ModuleGenerator::finish(const ShareableBytes& bytecode)
{ {
MOZ_ASSERT(!activeFunc_); MOZ_ASSERT(!activeFunc_);
MOZ_ASSERT(finishedFuncDefs_); MOZ_ASSERT(finishedFuncDefs_);
@ -976,7 +985,7 @@ ModuleGenerator::finish(ImportVector&& imports, const ShareableBytes& bytecode)
return SharedModule(js_new<Module>(Move(code), return SharedModule(js_new<Module>(Move(code),
Move(linkData_), Move(linkData_),
Move(imports), Move(imports_),
Move(exports_), Move(exports_),
Move(dataSegments_), Move(dataSegments_),
Move(elemSegments_), Move(elemSegments_),

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

@ -100,6 +100,7 @@ class MOZ_STACK_CLASS ModuleGenerator
LinkData linkData_; LinkData linkData_;
MutableMetadata metadata_; MutableMetadata metadata_;
ExportVector exports_; ExportVector exports_;
ImportVector imports_;
DataSegmentVector dataSegments_; DataSegmentVector dataSegments_;
ElemSegmentVector elemSegments_; ElemSegmentVector elemSegments_;
@ -116,7 +117,7 @@ class MOZ_STACK_CLASS ModuleGenerator
uint32_t lastPatchedCallsite_; uint32_t lastPatchedCallsite_;
uint32_t startOfUnpatchedBranches_; uint32_t startOfUnpatchedBranches_;
JumpSiteArray jumpThunks_; JumpSiteArray jumpThunks_;
bool tableExported_; bool externalTable_;
// Parallel compilation // Parallel compilation
bool parallel_; bool parallel_;
@ -141,7 +142,7 @@ class MOZ_STACK_CLASS ModuleGenerator
MOZ_MUST_USE bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOff); MOZ_MUST_USE bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOff);
public: public:
explicit ModuleGenerator(); explicit ModuleGenerator(ImportVector&& imports);
~ModuleGenerator(); ~ModuleGenerator();
MOZ_MUST_USE bool init(UniqueModuleGeneratorData shared, CompileArgs&& args, MOZ_MUST_USE bool init(UniqueModuleGeneratorData shared, CompileArgs&& args,
@ -207,7 +208,7 @@ class MOZ_STACK_CLASS ModuleGenerator
// Finish compilation, provided the list of imports and source bytecode. // Finish compilation, provided the list of imports and source bytecode.
// Both these Vectors may be empty (viz., b/c asm.js does different things // Both these Vectors may be empty (viz., b/c asm.js does different things
// for imports and source). // for imports and source).
SharedModule finish(ImportVector&& imports, const ShareableBytes& bytecode); SharedModule finish(const ShareableBytes& bytecode);
}; };
// A FunctionGenerator encapsulates the generation of a single function body. // A FunctionGenerator encapsulates the generation of a single function body.

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

@ -386,11 +386,6 @@ Instance::Instance(UniqueCodeSegment codeSegment,
for (size_t i = 0; i < tables_.length(); i++) for (size_t i = 0; i < tables_.length(); i++)
*addressOfTableBase(i) = tables_[i]->array(); *addressOfTableBase(i) = tables_[i]->array();
for (SharedTable& table : tables_) {
for (size_t i = 0; i < table->length(); i++)
table->array()[i] = codeSegment_->badIndirectCallCode();
}
} }
Instance::~Instance() Instance::~Instance()

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

@ -1737,7 +1737,7 @@ EmitCallIndirect(FunctionCompiler& f, uint32_t callOffset)
: f.mg().tables[0]; : f.mg().tables[0];
MDefinition* def; MDefinition* def;
if (!f.funcPtrCall(sigIndex, table.length, table.globalDataOffset, callee, args, &def)) if (!f.funcPtrCall(sigIndex, table.initial, table.globalDataOffset, callee, args, &def))
return false; return false;
if (IsVoid(sig.ret())) if (IsVoid(sig.ret()))

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

@ -69,6 +69,13 @@ Throw(JSContext* cx, const char* str)
return false; return false;
} }
static bool
Throw(JSContext* cx, unsigned errorNumber, const char* str)
{
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, errorNumber, str);
return false;
}
static bool static bool
GetProperty(JSContext* cx, HandleObject obj, const char* chars, MutableHandleValue v) GetProperty(JSContext* cx, HandleObject obj, const char* chars, MutableHandleValue v)
{ {
@ -98,7 +105,7 @@ GetImports(JSContext* cx,
if (strlen(import.func.get()) > 0) { if (strlen(import.func.get()) > 0) {
if (!v.isObject()) if (!v.isObject())
return Throw(cx, "import object field is not an Object"); return Throw(cx, JSMSG_WASM_BAD_IMPORT_FIELD, "an Object");
RootedObject obj(cx, &v.toObject()); RootedObject obj(cx, &v.toObject());
if (!GetProperty(cx, obj, import.func.get(), &v)) if (!GetProperty(cx, obj, import.func.get(), &v))
@ -108,17 +115,22 @@ GetImports(JSContext* cx,
switch (import.kind) { switch (import.kind) {
case DefinitionKind::Function: case DefinitionKind::Function:
if (!IsFunctionObject(v)) if (!IsFunctionObject(v))
return Throw(cx, "import object field is not a Function"); return Throw(cx, JSMSG_WASM_BAD_IMPORT_FIELD, "a Function");
if (!funcImports.append(&v.toObject().as<JSFunction>())) if (!funcImports.append(&v.toObject().as<JSFunction>()))
return false; return false;
break; break;
case DefinitionKind::Table: case DefinitionKind::Table:
MOZ_CRASH("NYI"); if (!v.isObject() || !v.toObject().is<WasmTableObject>())
return Throw(cx, JSMSG_WASM_BAD_IMPORT_FIELD, "a Table");
MOZ_ASSERT(!tableImport);
tableImport.set(&v.toObject().as<WasmTableObject>());
break;
case DefinitionKind::Memory: case DefinitionKind::Memory:
if (!v.isObject() || !v.toObject().is<WasmMemoryObject>()) if (!v.isObject() || !v.toObject().is<WasmMemoryObject>())
return Throw(cx, "import object field is not a Memory"); return Throw(cx, JSMSG_WASM_BAD_IMPORT_FIELD, "a Memory");
MOZ_ASSERT(!memoryImport); MOZ_ASSERT(!memoryImport);
memoryImport.set(&v.toObject().as<WasmMemoryObject>()); memoryImport.set(&v.toObject().as<WasmMemoryObject>());
@ -787,6 +799,11 @@ bool
WasmTableObject::init(JSContext* cx, HandleWasmInstanceObject instanceObj) WasmTableObject::init(JSContext* cx, HandleWasmInstanceObject instanceObj)
{ {
MOZ_ASSERT(!initialized()); MOZ_ASSERT(!initialized());
MOZ_ASSERT(!table().initialized());
// Ensure initialization is atomic so that the table is never left in an
// inconsistent state (where the Table is initialized but the
// WasmTableObject is not).
auto instanceVector = MakeUnique<InstanceVector>(); auto instanceVector = MakeUnique<InstanceVector>();
if (!instanceVector || !instanceVector->appendN(instanceObj.get(), table().length())) { if (!instanceVector || !instanceVector->appendN(instanceObj.get(), table().length())) {
@ -795,8 +812,10 @@ WasmTableObject::init(JSContext* cx, HandleWasmInstanceObject instanceObj)
} }
initReservedSlot(INSTANCE_VECTOR_SLOT, PrivateValue(instanceVector.release())); initReservedSlot(INSTANCE_VECTOR_SLOT, PrivateValue(instanceVector.release()));
table().init(instanceObj->instance().codeSegment());
MOZ_ASSERT(initialized()); MOZ_ASSERT(initialized());
MOZ_ASSERT(table().initialized());
return true; return true;
} }
@ -977,10 +996,8 @@ WasmTableObject::setImpl(JSContext* cx, const CallArgs& args)
MOZ_ASSERT(value == f); MOZ_ASSERT(value == f);
#endif #endif
if (instanceVector[index] != instanceObj) { if (!tableObj->setInstance(cx, index, instanceObj))
JS_ReportError(cx, "cross-module Table.prototype.set NYI");
return false; return false;
}
Instance& instance = instanceObj->instance(); Instance& instance = instanceObj->instance();
const FuncExport& funcExport = instance.metadata().lookupFuncExport(funcIndex); const FuncExport& funcExport = instance.metadata().lookupFuncExport(funcIndex);
@ -1020,12 +1037,19 @@ WasmTableObject::instanceVector() const
return *(InstanceVector*)getReservedSlot(INSTANCE_VECTOR_SLOT).toPrivate(); return *(InstanceVector*)getReservedSlot(INSTANCE_VECTOR_SLOT).toPrivate();
} }
void bool
WasmTableObject::setInstance(uint32_t index, HandleWasmInstanceObject instanceObj) WasmTableObject::setInstance(JSContext* cx, uint32_t index, HandleWasmInstanceObject instanceObj)
{ {
MOZ_ASSERT(initialized()); MOZ_ASSERT(initialized());
MOZ_ASSERT(instanceObj->instance().codeSegment().containsCodePC(table().array()[index])); MOZ_ASSERT(instanceObj->instance().codeSegment().containsCodePC(table().array()[index]));
if (instanceVector()[index] != instanceObj) {
JS_ReportError(cx, "cross-module Table import NYI");
return false;
}
instanceVector()[index] = instanceObj; instanceVector()[index] = instanceObj;
return true;
} }
// ============================================================================ // ============================================================================

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

@ -227,7 +227,7 @@ class WasmTableObject : public NativeObject
// vector. // vector.
wasm::Table& table() const; wasm::Table& table() const;
void setInstance(uint32_t index, HandleWasmInstanceObject instanceObj); bool setInstance(JSContext* cx, uint32_t index, HandleWasmInstanceObject instanceObj);
}; };
typedef Rooted<WasmTableObject*> RootedWasmTableObject; typedef Rooted<WasmTableObject*> RootedWasmTableObject;

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

@ -349,26 +349,43 @@ Module::addSizeOfMisc(MallocSizeOf mallocSizeOf,
bytecode_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenBytes); bytecode_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenBytes);
} }
void bool
Module::initElems(HandleWasmInstanceObject instanceObj, HandleWasmTableObject tableObj) const Module::initElems(JSContext* cx, HandleWasmInstanceObject instanceObj,
HandleWasmTableObject tableObj) const
{ {
Instance& instance = instanceObj->instance(); Instance& instance = instanceObj->instance();
const CodeSegment& codeSegment = instance.codeSegment(); const CodeSegment& codeSegment = instance.codeSegment();
const SharedTableVector& tables = instance.tables(); const SharedTableVector& tables = instance.tables();
// Initialize tables that have a WasmTableObject first, so that this
// initialization can be done atomically.
if (tableObj && !tableObj->initialized() && !tableObj->init(cx, instanceObj))
return false;
// Initialize all remaining Tables that do not have objects.
for (const SharedTable& table : tables) {
if (!table->initialized())
table->init(codeSegment);
}
// Now that all tables have been initialized, write elements.
for (const ElemSegment& seg : elemSegments_) { for (const ElemSegment& seg : elemSegments_) {
Table& table = *tables[seg.tableIndex]; Table& table = *tables[seg.tableIndex];
MOZ_ASSERT(seg.offset + seg.elems.length() <= table.length()); MOZ_ASSERT(seg.offset + seg.elems.length() <= table.length());
for (uint32_t i = 0; i < seg.elems.length(); i++)
table.array()[seg.offset + i] = codeSegment.code() + seg.elems[i];
if (tableObj) { if (tableObj) {
MOZ_ASSERT(seg.tableIndex == 0); MOZ_ASSERT(seg.tableIndex == 0);
for (uint32_t i = 0; i < seg.elems.length(); i++) for (uint32_t i = 0; i < seg.elems.length(); i++) {
tableObj->setInstance(seg.offset + i, instanceObj); if (!tableObj->setInstance(cx, seg.offset + i, instanceObj))
return false;
}
} }
for (uint32_t i = 0; i < seg.elems.length(); i++)
table.array()[seg.offset + i] = codeSegment.code() + seg.elems[i];
} }
return true;
} }
// asm.js module instantiation supplies its own buffer, but for wasm, create and // asm.js module instantiation supplies its own buffer, but for wasm, create and
@ -388,7 +405,7 @@ Module::instantiateMemory(JSContext* cx, MutableHandleWasmMemoryObject memory) c
buffer = &memory->buffer(); buffer = &memory->buffer();
uint32_t length = buffer->byteLength(); uint32_t length = buffer->byteLength();
if (length < metadata_->minMemoryLength || length > metadata_->maxMemoryLength) { if (length < metadata_->minMemoryLength || length > metadata_->maxMemoryLength) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_MEM_IMP_SIZE); JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_IMP_SIZE, "Memory");
return false; return false;
} }
@ -431,12 +448,17 @@ Module::instantiateTable(JSContext* cx, const CodeSegment& codeSegment,
for (const TableDesc& tableDesc : metadata_->tables) { for (const TableDesc& tableDesc : metadata_->tables) {
SharedTable table; SharedTable table;
if (tableImport) { if (tableImport) {
MOZ_CRASH("NYI: table imports"); table = &tableImport->table();
if (table->length() < tableDesc.initial || table->length() > tableDesc.maximum) {
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_IMP_SIZE, "Table");
return false;
}
} else { } else {
table = Table::create(cx, tableDesc.kind, tableDesc.length); table = Table::create(cx, tableDesc.kind, tableDesc.initial);
if (!table) if (!table)
return false; return false;
} }
if (!tables->emplaceBack(table)) { if (!tables->emplaceBack(table)) {
ReportOutOfMemory(cx); ReportOutOfMemory(cx);
return false; return false;
@ -488,7 +510,7 @@ CreateExportObject(JSContext* cx,
if (!tableObj) { if (!tableObj) {
MOZ_ASSERT(instance.tables().length() == 1); MOZ_ASSERT(instance.tables().length() == 1);
tableObj.set(WasmTableObject::create(cx, *instance.tables()[0])); tableObj.set(WasmTableObject::create(cx, *instance.tables()[0]));
if (!tableObj || !tableObj->init(cx, instanceObj)) if (!tableObj)
return false; return false;
} }
val = ObjectValue(*tableObj); val = ObjectValue(*tableObj);
@ -581,9 +603,12 @@ Module::instantiate(JSContext* cx,
return false; return false;
// Initialize table elements only after the instance is fully initialized // Initialize table elements only after the instance is fully initialized
// since the Table object needs to point to a valid instance object. // 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).
initElems(instanceObj, table); if (!initElems(cx, instanceObj, table))
return false;
// Done! Notify the Debugger of the new Instance. // Done! Notify the Debugger of the new Instance.

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

@ -188,7 +188,8 @@ class Module : public RefCounted<Module>
bool instantiateMemory(JSContext* cx, MutableHandleWasmMemoryObject memory) const; bool instantiateMemory(JSContext* cx, MutableHandleWasmMemoryObject memory) const;
bool instantiateTable(JSContext* cx, const CodeSegment& codeSegment, bool instantiateTable(JSContext* cx, const CodeSegment& codeSegment,
HandleWasmTableObject tableImport, SharedTableVector* tables) const; HandleWasmTableObject tableImport, SharedTableVector* tables) const;
void initElems(HandleWasmInstanceObject instanceObj, HandleWasmTableObject tableObj) const; bool initElems(JSContext* cx, HandleWasmInstanceObject instanceObj,
HandleWasmTableObject tableObj) const;
public: public:
Module(Bytes&& code, Module(Bytes&& code,

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

@ -24,19 +24,33 @@ using namespace js::wasm;
/* static */ SharedTable /* static */ SharedTable
Table::create(JSContext* cx, TableKind kind, uint32_t length) Table::create(JSContext* cx, TableKind kind, uint32_t length)
{ {
SharedTable table = js_new<Table>(); SharedTable table = cx->new_<Table>();
if (!table) if (!table)
return nullptr; return nullptr;
table->array_.reset(js_pod_calloc<void*>(length)); table->array_.reset(cx->pod_calloc<void*>(length));
if (!table->array_) if (!table->array_)
return nullptr; return nullptr;
table->kind_ = kind; table->kind_ = kind;
table->length_ = length; table->length_ = length;
table->initialized_ = false;
return table; return table;
} }
void
Table::init(const CodeSegment& codeSegment)
{
MOZ_ASSERT(!initialized());
for (uint32_t i = 0; i < length_; i++) {
MOZ_ASSERT(!array_.get()[i]);
array_.get()[i] = codeSegment.badIndirectCallCode();
}
initialized_ = true;
}
size_t size_t
Table::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const Table::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
{ {

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

@ -33,14 +33,22 @@ class Table : public ShareableBase<Table>
TableKind kind_; TableKind kind_;
UniquePtr<void*> array_; UniquePtr<void*> array_;
uint32_t length_; uint32_t length_;
bool initialized_;
public: public:
static RefPtr<Table> create(JSContext* cx, TableKind kind, uint32_t length); static RefPtr<Table> create(JSContext* cx, TableKind kind, uint32_t length);
// These accessors may be used before initialization.
bool isTypedFunction() const { return kind_ == TableKind::TypedFunction; } bool isTypedFunction() const { return kind_ == TableKind::TypedFunction; }
void** array() const { return array_.get(); } void** array() const { return array_.get(); }
uint32_t length() const { return length_; } uint32_t length() const { return length_; }
// A Table must be initialized before any dependent instance can execute.
bool initialized() const { return initialized_; }
void init(const CodeSegment& codeSegment);
// about:memory reporting: // about:memory reporting:
size_t sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const; size_t sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const;

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

@ -2439,13 +2439,25 @@ ParseImport(WasmParseContext& c, bool newFormat, AstModule* module)
AstRef sigRef; AstRef sigRef;
WasmToken openParen; WasmToken openParen;
if (c.ts.getIf(WasmToken::OpenParen, &openParen)) { if (c.ts.getIf(WasmToken::OpenParen, &openParen)) {
if (newFormat && c.ts.getIf(WasmToken::Memory)) { if (newFormat) {
AstResizable memory; if (c.ts.getIf(WasmToken::Memory)) {
if (!ParseResizable(c, &memory)) AstResizable memory;
return nullptr; if (!ParseResizable(c, &memory))
if (!c.ts.match(WasmToken::CloseParen, c.error)) return nullptr;
return nullptr; if (!c.ts.match(WasmToken::CloseParen, c.error))
return new(c.lifo) AstImport(name, moduleName.text(), fieldName.text(), memory); return nullptr;
return new(c.lifo) AstImport(name, moduleName.text(), fieldName.text(),
DefinitionKind::Memory, memory);
}
if (c.ts.getIf(WasmToken::Table)) {
AstResizable table;
if (!ParseResizable(c, &table))
return nullptr;
if (!c.ts.match(WasmToken::CloseParen, c.error))
return nullptr;
return new(c.lifo) AstImport(name, moduleName.text(), fieldName.text(),
DefinitionKind::Table, table);
}
} }
if (c.ts.getIf(WasmToken::Type)) { if (c.ts.getIf(WasmToken::Type)) {
@ -3558,9 +3570,8 @@ EncodeImport(Encoder& e, bool newFormat, AstImport& imp)
return false; return false;
break; break;
case DefinitionKind::Table: case DefinitionKind::Table:
MOZ_CRASH("NYI");
case DefinitionKind::Memory: case DefinitionKind::Memory:
if (!EncodeResizable(e, imp.memory())) if (!EncodeResizable(e, imp.resizable()))
return false; return false;
break; break;
} }

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

@ -10,6 +10,10 @@ const mem1Page = new Memory({initial:1});
const mem2Page = new Memory({initial:2}); const mem2Page = new Memory({initial:2});
const mem3Page = new Memory({initial:3}); const mem3Page = new Memory({initial:3});
const mem4Page = new Memory({initial:4}); const mem4Page = new Memory({initial:4});
const tab1Elem = new Table({initial:1});
const tab2Elem = new Table({initial:2});
const tab3Elem = new Table({initial:3});
const tab4Elem = new Table({initial:4});
// Explicitly opt into the new binary format for imports and exports until it // Explicitly opt into the new binary format for imports and exports until it
// is used by default everywhere. // is used by default everywhere.
@ -54,9 +58,22 @@ assertEq(new Instance(m5, {a:{b:mem2Page}}) instanceof Instance, true);
assertEq(new Instance(m5, {a:{b:mem3Page}}) instanceof Instance, true); assertEq(new Instance(m5, {a:{b:mem3Page}}) instanceof Instance, true);
assertEq(new Instance(m5, {a:{b:mem4Page}}) instanceof Instance, true); assertEq(new Instance(m5, {a:{b:mem4Page}}) instanceof Instance, true);
const m6 = new Module(textToBinary('(module (import "a" "b" (table 2)))'));
assertErrorMessage(() => new Instance(m6, {a:{b:tab1Elem}}), TypeError, /imported Table with incompatible size/);
assertEq(new Instance(m6, {a:{b:tab2Elem}}) instanceof Instance, true);
assertEq(new Instance(m6, {a:{b:tab3Elem}}) instanceof Instance, true);
assertEq(new Instance(m6, {a:{b:tab4Elem}}) instanceof Instance, true);
const m7 = new Module(textToBinary('(module (import "a" "b" (table 2 3)))'));
assertErrorMessage(() => new Instance(m7, {a:{b:tab1Elem}}), TypeError, /imported Table with incompatible size/);
assertEq(new Instance(m7, {a:{b:tab2Elem}}) instanceof Instance, true);
assertEq(new Instance(m7, {a:{b:tab3Elem}}) instanceof Instance, true);
assertErrorMessage(() => new Instance(m7, {a:{b:tab4Elem}}), TypeError, /imported Table with incompatible size/);
assertErrorMessage(() => new Module(textToBinary('(module (memory 2 1))')), TypeError, /maximum length less than initial length/); assertErrorMessage(() => new Module(textToBinary('(module (memory 2 1))')), TypeError, /maximum length less than initial length/);
assertErrorMessage(() => new Module(textToBinary('(module (import "a" "b" (memory 2 1)))')), TypeError, /maximum length less than initial length/); assertErrorMessage(() => new Module(textToBinary('(module (import "a" "b" (memory 2 1)))')), TypeError, /maximum length less than initial length/);
assertErrorMessage(() => new Module(textToBinary('(module (table (resizable 2 1)))')), TypeError, /maximum length less than initial length/); assertErrorMessage(() => new Module(textToBinary('(module (table (resizable 2 1)))')), TypeError, /maximum length less than initial length/);
assertErrorMessage(() => new Module(textToBinary('(module (import "a" "b" (table 2 1)))')), TypeError, /maximum length less than initial length/);
// Import order: // Import order:
@ -241,16 +258,24 @@ var e = new Instance(new Module(code), {a:{b:mem}}).exports;
assertEq(mem, e.foo); assertEq(mem, e.foo);
assertEq(mem, e.bar); assertEq(mem, e.bar);
var code = textToBinary('(module (import "a" "b" (table 1 1)) (export "foo" table) (export "bar" table))');
var tbl = new Table({initial:1});
var e = new Instance(new Module(code), {a:{b:tbl}}).exports;
assertEq(tbl, e.foo);
assertEq(tbl, e.bar);
// Non-existent export errors // Non-existent export errors
assertErrorMessage(() => new Module(textToBinary('(module (export "a" 0))')), TypeError, /exported function index out of bounds/); assertErrorMessage(() => new Module(textToBinary('(module (export "a" 0))')), TypeError, /exported function index out of bounds/);
assertErrorMessage(() => new Module(textToBinary('(module (export "a" memory))')), TypeError, /exported memory index out of bounds/); assertErrorMessage(() => new Module(textToBinary('(module (export "a" memory))')), TypeError, /exported memory index out of bounds/);
assertErrorMessage(() => new Module(textToBinary('(module (export "a" table))')), TypeError, /exported table index out of bounds/); assertErrorMessage(() => new Module(textToBinary('(module (export "a" table))')), TypeError, /exported table index out of bounds/);
// Default memory rules // Default memory/table rules
assertErrorMessage(() => new Module(textToBinary('(module (import "a" "b" (memory 1 1)) (memory 1 1))')), TypeError, /already have default memory/); assertErrorMessage(() => new Module(textToBinary('(module (import "a" "b" (memory 1 1)) (memory 1 1))')), TypeError, /already have default memory/);
assertErrorMessage(() => new Module(textToBinary('(module (import "a" "b" (memory 1 1)) (import "x" "y" (memory 2 2)))')), TypeError, /already have default memory/); assertErrorMessage(() => new Module(textToBinary('(module (import "a" "b" (memory 1 1)) (import "x" "y" (memory 2 2)))')), TypeError, /already have default memory/);
assertErrorMessage(() => new Module(textToBinary('(module (import "a" "b" (table 1 1)) (table 1 1))')), TypeError, /already have default table/);
assertErrorMessage(() => new Module(textToBinary('(module (import "a" "b" (table 1 1)) (import "x" "y" (table 2 2)))')), TypeError, /already have default table/);
// Data segments on imports // Data segments on imports
@ -278,3 +303,25 @@ assertEq(i8[2], 0x0);
assertEq(i8[100], 0xc); assertEq(i8[100], 0xc);
assertEq(i8[101], 0xd); assertEq(i8[101], 0xd);
assertEq(i8[102], 0x0); assertEq(i8[102], 0x0);
// Elem segments on imports
var m = new Module(textToBinary(`
(module
(import "a" "b" (table 10))
(elem 0 $one $two)
(elem 3 $three $four)
(func $one (result i32) (i32.const 1))
(func $two (result i32) (i32.const 2))
(func $three (result i32) (i32.const 3))
(func $four (result i32) (i32.const 4)))
`));
var tbl = new Table({initial:10});
new Instance(m, {a:{b:tbl}});
assertEq(tbl.get(0)(), 1);
assertEq(tbl.get(1)(), 2);
assertEq(tbl.get(2), null);
assertEq(tbl.get(3)(), 3);
assertEq(tbl.get(4)(), 4);
for (var i = 5; i < 10; i++)
assertEq(tbl.get(i), null);

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

@ -53,6 +53,13 @@ assertEq(call(5), 2);
assertErrorMessage(() => call(6), Error, /bad wasm indirect call/); assertErrorMessage(() => call(6), Error, /bad wasm indirect call/);
assertErrorMessage(() => call(10), Error, /out-of-range/); assertErrorMessage(() => call(10), Error, /out-of-range/);
var tbl = new Table({initial:3});
var call = evalText(`(module (import "a" "b" (table 2)) (export "tbl" table) (elem 0 $f0 $f1) ${callee(0)} ${callee(1)} ${caller})`, {a:{b:tbl}}).exports.call;
assertEq(call(0), 0);
assertEq(call(1), 1);
assertEq(tbl.get(0)(), 0);
assertEq(tbl.get(1)(), 1);
// A table should not hold exported functions alive and exported functions // A table should not hold exported functions alive and exported functions
// should not hold their originating table alive. Live exported functions should // should not hold their originating table alive. Live exported functions should
// hold instances alive. Nothing should hold the export object alive. // hold instances alive. Nothing should hold the export object alive.

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

@ -348,9 +348,10 @@ MSG_DEF(JSMSG_WASM_BAD_IND_CALL, 0, JSEXN_ERR, "bad wasm indirect
MSG_DEF(JSMSG_WASM_BAD_BUF_ARG, 0, JSEXN_TYPEERR, "first argument must be an ArrayBuffer or typed array object") MSG_DEF(JSMSG_WASM_BAD_BUF_ARG, 0, JSEXN_TYPEERR, "first argument must be an ArrayBuffer or typed array object")
MSG_DEF(JSMSG_WASM_BAD_MOD_ARG, 0, JSEXN_TYPEERR, "first argument must be a WebAssembly.Module") MSG_DEF(JSMSG_WASM_BAD_MOD_ARG, 0, JSEXN_TYPEERR, "first argument must be a WebAssembly.Module")
MSG_DEF(JSMSG_WASM_BAD_DESC_ARG, 1, JSEXN_TYPEERR, "first argument must be a {0} descriptor") MSG_DEF(JSMSG_WASM_BAD_DESC_ARG, 1, JSEXN_TYPEERR, "first argument must be a {0} descriptor")
MSG_DEF(JSMSG_WASM_BAD_MEM_IMP_SIZE, 0, JSEXN_TYPEERR, "imported Memory with incompatible size") MSG_DEF(JSMSG_WASM_BAD_IMP_SIZE, 1, JSEXN_TYPEERR, "imported {0} with incompatible size")
MSG_DEF(JSMSG_WASM_BAD_SIZE, 2, JSEXN_TYPEERR, "bad {0} {1} size") MSG_DEF(JSMSG_WASM_BAD_SIZE, 2, JSEXN_TYPEERR, "bad {0} {1} size")
MSG_DEF(JSMSG_WASM_BAD_IMPORT_ARG, 0, JSEXN_TYPEERR, "second argument, if present, must be an object") MSG_DEF(JSMSG_WASM_BAD_IMPORT_ARG, 0, JSEXN_TYPEERR, "second argument, if present, must be an object")
MSG_DEF(JSMSG_WASM_BAD_IMPORT_FIELD, 1, JSEXN_TYPEERR, "import object field is not {0}")
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_SET_VALUE, 0, JSEXN_TYPEERR, "second argument must be null or an exported WebAssembly Function object")
MSG_DEF(JSMSG_WASM_UNREACHABLE, 0, JSEXN_ERR, "unreachable executed") 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_INTEGER_OVERFLOW, 0, JSEXN_ERR, "integer overflow")