зеркало из https://github.com/mozilla/gecko-dev.git
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:
Родитель
27473ec09d
Коммит
c7232efb25
|
@ -1677,6 +1677,7 @@ class MOZ_STACK_CLASS ModuleValidator
|
|||
importMap_(cx),
|
||||
arrayViews_(cx),
|
||||
atomicsPresent_(false),
|
||||
mg_(ImportVector()),
|
||||
errorString_(nullptr),
|
||||
errorOffset_(UINT32_MAX),
|
||||
errorOverRecursed_(false)
|
||||
|
@ -2303,17 +2304,13 @@ class MOZ_STACK_CLASS ModuleValidator
|
|||
uint32_t endAfterCurly = pos.end;
|
||||
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
|
||||
// provided through the ScriptSource.
|
||||
SharedBytes bytes = js_new<ShareableBytes>();
|
||||
if (!bytes)
|
||||
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)
|
||||
: name_(name), module_(module), field_(field), kind_(DefinitionKind::Function), funcSig_(funcSig)
|
||||
{}
|
||||
AstImport(AstName name, AstName module, AstName field, AstResizable resizable)
|
||||
: name_(name), module_(module), field_(field), kind_(DefinitionKind::Memory), resizable_(resizable)
|
||||
AstImport(AstName name, AstName module, AstName field, DefinitionKind kind, AstResizable resizable)
|
||||
: name_(name), module_(module), field_(field), kind_(kind), resizable_(resizable)
|
||||
{}
|
||||
AstName name() const { return name_; }
|
||||
AstName module() const { return module_; }
|
||||
AstName field() const { return field_; }
|
||||
DefinitionKind kind() const { return kind_; }
|
||||
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
|
||||
|
|
|
@ -5200,7 +5200,7 @@ BaseCompiler::emitCallIndirect(uint32_t callOffset)
|
|||
? mg_.tables[mg_.asmJSSigToTableIndex[sigIndex]]
|
||||
: mg_.tables[0];
|
||||
|
||||
funcPtrCall(sig, sigIndex, table.length, table.globalDataOffset, callee, baselineCall);
|
||||
funcPtrCall(sig, sigIndex, table.initial, table.globalDataOffset, callee, baselineCall);
|
||||
|
||||
endCall(baselineCall);
|
||||
|
||||
|
|
|
@ -232,10 +232,11 @@ struct TableDesc
|
|||
{
|
||||
TableKind kind;
|
||||
uint32_t globalDataOffset;
|
||||
uint32_t length;
|
||||
uint32_t initial;
|
||||
uint32_t maximum;
|
||||
|
||||
TableDesc() : kind(TableKind::AnyFunction), globalDataOffset(0), length(0) {}
|
||||
explicit TableDesc(TableKind kind) : kind(kind) {}
|
||||
TableDesc() { PodZero(this); }
|
||||
explicit TableDesc(TableKind kind) : kind(kind), globalDataOffset(0), initial(0), maximum(0) {}
|
||||
};
|
||||
|
||||
WASM_DECLARE_POD_VECTOR(TableDesc, TableDescVector)
|
||||
|
|
|
@ -714,7 +714,8 @@ DecodeResizableTable(Decoder& d, ModuleGeneratorData* init)
|
|||
return Fail(d, "already have default table");
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -772,6 +773,11 @@ DecodeImport(Decoder& d, bool newFormat, ModuleGeneratorData* init, ImportVector
|
|||
return false;
|
||||
break;
|
||||
}
|
||||
case DefinitionKind::Table: {
|
||||
if (!DecodeResizableTable(d, init))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
case DefinitionKind::Memory: {
|
||||
if (!DecodeResizableMemory(d, init))
|
||||
return false;
|
||||
|
@ -825,17 +831,18 @@ DecodeTableSection(Decoder& d, bool newFormat, ModuleGeneratorData* init, Uint32
|
|||
return false;
|
||||
} else {
|
||||
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");
|
||||
|
||||
if (table.length > MaxTableElems)
|
||||
if (table.initial > MaxTableElems)
|
||||
return Fail(d, "too many table elements");
|
||||
|
||||
if (!oldElems->resize(table.length))
|
||||
if (!oldElems->resize(table.initial))
|
||||
return false;
|
||||
|
||||
for (uint32_t i = 0; i < table.length; i++) {
|
||||
for (uint32_t i = 0; i < table.initial; i++) {
|
||||
uint32_t funcIndex;
|
||||
if (!d.readVarU32(&funcIndex))
|
||||
return Fail(d, "expected table element");
|
||||
|
@ -1214,7 +1221,7 @@ DecodeElemSection(Decoder& d, bool newFormat, Uint32Vector&& oldElems, ModuleGen
|
|||
if (!d.readVarU32(&numElems))
|
||||
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)
|
||||
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))
|
||||
return nullptr;
|
||||
|
||||
ModuleGenerator mg;
|
||||
ModuleGenerator mg(Move(imports));
|
||||
if (!mg.init(Move(init), Move(args)))
|
||||
return nullptr;
|
||||
|
||||
|
@ -1452,5 +1459,5 @@ wasm::Compile(const ShareableBytes& bytecode, CompileArgs&& args, UniqueChars* e
|
|||
|
||||
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 COMPILATION_LIFO_DEFAULT_CHUNK_SIZE = 64 * 1024;
|
||||
|
||||
ModuleGenerator::ModuleGenerator()
|
||||
ModuleGenerator::ModuleGenerator(ImportVector&& imports)
|
||||
: alwaysBaseline_(false),
|
||||
imports_(Move(imports)),
|
||||
numSigs_(0),
|
||||
numTables_(0),
|
||||
lifo_(GENERATOR_LIFO_DEFAULT_CHUNK_SIZE),
|
||||
|
@ -51,7 +52,7 @@ ModuleGenerator::ModuleGenerator()
|
|||
masm_(MacroAssembler::AsmJSToken(), masmAlloc_),
|
||||
lastPatchedCallsite_(0),
|
||||
startOfUnpatchedBranches_(0),
|
||||
tableExported_(false),
|
||||
externalTable_(false),
|
||||
parallel_(false),
|
||||
outstanding_(0),
|
||||
activeFunc_(nullptr),
|
||||
|
@ -59,6 +60,13 @@ ModuleGenerator::ModuleGenerator()
|
|||
finishedFuncDefs_(false)
|
||||
{
|
||||
MOZ_ASSERT(IsCompilingAsmJS());
|
||||
|
||||
for (const Import& import : imports_) {
|
||||
if (import.kind == DefinitionKind::Table) {
|
||||
externalTable_ = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ModuleGenerator::~ModuleGenerator()
|
||||
|
@ -695,7 +703,7 @@ bool
|
|||
ModuleGenerator::addTableExport(UniqueChars fieldName)
|
||||
{
|
||||
MOZ_ASSERT(elemSegments_.empty());
|
||||
tableExported_ = true;
|
||||
externalTable_ = true;
|
||||
return exports_.emplaceBack(Move(fieldName), DefinitionKind::Table);
|
||||
}
|
||||
|
||||
|
@ -843,9 +851,9 @@ ModuleGenerator::finishFuncDefs()
|
|||
bool
|
||||
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) {
|
||||
if (!exportedFuncs_.put(funcIndex))
|
||||
return false;
|
||||
|
@ -874,9 +882,10 @@ ModuleGenerator::initSigTableLength(uint32_t sigIndex, uint32_t length)
|
|||
|
||||
TableDesc& table = shared_->tables[numTables_++];
|
||||
MOZ_ASSERT(table.globalDataOffset == 0);
|
||||
MOZ_ASSERT(table.length == 0);
|
||||
MOZ_ASSERT(table.initial == 0);
|
||||
table.kind = TableKind::TypedFunction;
|
||||
table.length = length;
|
||||
table.initial = length;
|
||||
table.maximum = UINT32_MAX;
|
||||
return allocateGlobalBytes(sizeof(void*), sizeof(void*), &table.globalDataOffset);
|
||||
}
|
||||
|
||||
|
@ -886,13 +895,13 @@ ModuleGenerator::initSigTableElems(uint32_t sigIndex, Uint32Vector&& elemFuncInd
|
|||
MOZ_ASSERT(isAsmJS());
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
SharedModule
|
||||
ModuleGenerator::finish(ImportVector&& imports, const ShareableBytes& bytecode)
|
||||
ModuleGenerator::finish(const ShareableBytes& bytecode)
|
||||
{
|
||||
MOZ_ASSERT(!activeFunc_);
|
||||
MOZ_ASSERT(finishedFuncDefs_);
|
||||
|
@ -976,7 +985,7 @@ ModuleGenerator::finish(ImportVector&& imports, const ShareableBytes& bytecode)
|
|||
|
||||
return SharedModule(js_new<Module>(Move(code),
|
||||
Move(linkData_),
|
||||
Move(imports),
|
||||
Move(imports_),
|
||||
Move(exports_),
|
||||
Move(dataSegments_),
|
||||
Move(elemSegments_),
|
||||
|
|
|
@ -100,6 +100,7 @@ class MOZ_STACK_CLASS ModuleGenerator
|
|||
LinkData linkData_;
|
||||
MutableMetadata metadata_;
|
||||
ExportVector exports_;
|
||||
ImportVector imports_;
|
||||
DataSegmentVector dataSegments_;
|
||||
ElemSegmentVector elemSegments_;
|
||||
|
||||
|
@ -116,7 +117,7 @@ class MOZ_STACK_CLASS ModuleGenerator
|
|||
uint32_t lastPatchedCallsite_;
|
||||
uint32_t startOfUnpatchedBranches_;
|
||||
JumpSiteArray jumpThunks_;
|
||||
bool tableExported_;
|
||||
bool externalTable_;
|
||||
|
||||
// Parallel compilation
|
||||
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);
|
||||
|
||||
public:
|
||||
explicit ModuleGenerator();
|
||||
explicit ModuleGenerator(ImportVector&& imports);
|
||||
~ModuleGenerator();
|
||||
|
||||
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.
|
||||
// Both these Vectors may be empty (viz., b/c asm.js does different things
|
||||
// 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.
|
||||
|
|
|
@ -386,11 +386,6 @@ Instance::Instance(UniqueCodeSegment codeSegment,
|
|||
|
||||
for (size_t i = 0; i < tables_.length(); i++)
|
||||
*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()
|
||||
|
|
|
@ -1737,7 +1737,7 @@ EmitCallIndirect(FunctionCompiler& f, uint32_t callOffset)
|
|||
: f.mg().tables[0];
|
||||
|
||||
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;
|
||||
|
||||
if (IsVoid(sig.ret()))
|
||||
|
|
|
@ -69,6 +69,13 @@ Throw(JSContext* cx, const char* str)
|
|||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
Throw(JSContext* cx, unsigned errorNumber, const char* str)
|
||||
{
|
||||
JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, errorNumber, str);
|
||||
return false;
|
||||
}
|
||||
|
||||
static bool
|
||||
GetProperty(JSContext* cx, HandleObject obj, const char* chars, MutableHandleValue v)
|
||||
{
|
||||
|
@ -98,7 +105,7 @@ GetImports(JSContext* cx,
|
|||
|
||||
if (strlen(import.func.get()) > 0) {
|
||||
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());
|
||||
if (!GetProperty(cx, obj, import.func.get(), &v))
|
||||
|
@ -108,17 +115,22 @@ GetImports(JSContext* cx,
|
|||
switch (import.kind) {
|
||||
case DefinitionKind::Function:
|
||||
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>()))
|
||||
return false;
|
||||
|
||||
break;
|
||||
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:
|
||||
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);
|
||||
memoryImport.set(&v.toObject().as<WasmMemoryObject>());
|
||||
|
@ -787,6 +799,11 @@ bool
|
|||
WasmTableObject::init(JSContext* cx, HandleWasmInstanceObject instanceObj)
|
||||
{
|
||||
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>();
|
||||
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()));
|
||||
table().init(instanceObj->instance().codeSegment());
|
||||
|
||||
MOZ_ASSERT(initialized());
|
||||
MOZ_ASSERT(table().initialized());
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -977,10 +996,8 @@ WasmTableObject::setImpl(JSContext* cx, const CallArgs& args)
|
|||
MOZ_ASSERT(value == f);
|
||||
#endif
|
||||
|
||||
if (instanceVector[index] != instanceObj) {
|
||||
JS_ReportError(cx, "cross-module Table.prototype.set NYI");
|
||||
if (!tableObj->setInstance(cx, index, instanceObj))
|
||||
return false;
|
||||
}
|
||||
|
||||
Instance& instance = instanceObj->instance();
|
||||
const FuncExport& funcExport = instance.metadata().lookupFuncExport(funcIndex);
|
||||
|
@ -1020,12 +1037,19 @@ WasmTableObject::instanceVector() const
|
|||
return *(InstanceVector*)getReservedSlot(INSTANCE_VECTOR_SLOT).toPrivate();
|
||||
}
|
||||
|
||||
void
|
||||
WasmTableObject::setInstance(uint32_t index, HandleWasmInstanceObject instanceObj)
|
||||
bool
|
||||
WasmTableObject::setInstance(JSContext* cx, uint32_t index, HandleWasmInstanceObject instanceObj)
|
||||
{
|
||||
MOZ_ASSERT(initialized());
|
||||
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;
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
|
|
|
@ -227,7 +227,7 @@ class WasmTableObject : public NativeObject
|
|||
// vector.
|
||||
|
||||
wasm::Table& table() const;
|
||||
void setInstance(uint32_t index, HandleWasmInstanceObject instanceObj);
|
||||
bool setInstance(JSContext* cx, uint32_t index, HandleWasmInstanceObject instanceObj);
|
||||
};
|
||||
|
||||
typedef Rooted<WasmTableObject*> RootedWasmTableObject;
|
||||
|
|
|
@ -349,26 +349,43 @@ Module::addSizeOfMisc(MallocSizeOf mallocSizeOf,
|
|||
bytecode_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenBytes);
|
||||
}
|
||||
|
||||
void
|
||||
Module::initElems(HandleWasmInstanceObject instanceObj, HandleWasmTableObject tableObj) const
|
||||
bool
|
||||
Module::initElems(JSContext* cx, HandleWasmInstanceObject instanceObj,
|
||||
HandleWasmTableObject tableObj) const
|
||||
{
|
||||
Instance& instance = instanceObj->instance();
|
||||
const CodeSegment& codeSegment = instance.codeSegment();
|
||||
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_) {
|
||||
Table& table = *tables[seg.tableIndex];
|
||||
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) {
|
||||
MOZ_ASSERT(seg.tableIndex == 0);
|
||||
for (uint32_t i = 0; i < seg.elems.length(); i++)
|
||||
tableObj->setInstance(seg.offset + i, instanceObj);
|
||||
for (uint32_t i = 0; i < seg.elems.length(); i++) {
|
||||
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
|
||||
|
@ -388,7 +405,7 @@ Module::instantiateMemory(JSContext* cx, MutableHandleWasmMemoryObject memory) c
|
|||
buffer = &memory->buffer();
|
||||
uint32_t length = buffer->byteLength();
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -431,12 +448,17 @@ Module::instantiateTable(JSContext* cx, const CodeSegment& codeSegment,
|
|||
for (const TableDesc& tableDesc : metadata_->tables) {
|
||||
SharedTable table;
|
||||
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 {
|
||||
table = Table::create(cx, tableDesc.kind, tableDesc.length);
|
||||
table = Table::create(cx, tableDesc.kind, tableDesc.initial);
|
||||
if (!table)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!tables->emplaceBack(table)) {
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
|
@ -488,7 +510,7 @@ CreateExportObject(JSContext* cx,
|
|||
if (!tableObj) {
|
||||
MOZ_ASSERT(instance.tables().length() == 1);
|
||||
tableObj.set(WasmTableObject::create(cx, *instance.tables()[0]));
|
||||
if (!tableObj || !tableObj->init(cx, instanceObj))
|
||||
if (!tableObj)
|
||||
return false;
|
||||
}
|
||||
val = ObjectValue(*tableObj);
|
||||
|
@ -581,9 +603,12 @@ Module::instantiate(JSContext* cx,
|
|||
return false;
|
||||
|
||||
// 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.
|
||||
|
||||
|
|
|
@ -188,7 +188,8 @@ class Module : public RefCounted<Module>
|
|||
bool instantiateMemory(JSContext* cx, MutableHandleWasmMemoryObject memory) const;
|
||||
bool instantiateTable(JSContext* cx, const CodeSegment& codeSegment,
|
||||
HandleWasmTableObject tableImport, SharedTableVector* tables) const;
|
||||
void initElems(HandleWasmInstanceObject instanceObj, HandleWasmTableObject tableObj) const;
|
||||
bool initElems(JSContext* cx, HandleWasmInstanceObject instanceObj,
|
||||
HandleWasmTableObject tableObj) const;
|
||||
|
||||
public:
|
||||
Module(Bytes&& code,
|
||||
|
|
|
@ -24,19 +24,33 @@ using namespace js::wasm;
|
|||
/* static */ SharedTable
|
||||
Table::create(JSContext* cx, TableKind kind, uint32_t length)
|
||||
{
|
||||
SharedTable table = js_new<Table>();
|
||||
SharedTable table = cx->new_<Table>();
|
||||
if (!table)
|
||||
return nullptr;
|
||||
|
||||
table->array_.reset(js_pod_calloc<void*>(length));
|
||||
table->array_.reset(cx->pod_calloc<void*>(length));
|
||||
if (!table->array_)
|
||||
return nullptr;
|
||||
|
||||
table->kind_ = kind;
|
||||
table->length_ = length;
|
||||
table->initialized_ = false;
|
||||
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
|
||||
Table::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
|
||||
{
|
||||
|
|
|
@ -33,14 +33,22 @@ class Table : public ShareableBase<Table>
|
|||
TableKind kind_;
|
||||
UniquePtr<void*> array_;
|
||||
uint32_t length_;
|
||||
bool initialized_;
|
||||
|
||||
public:
|
||||
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; }
|
||||
void** array() const { return array_.get(); }
|
||||
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:
|
||||
|
||||
size_t sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const;
|
||||
|
|
|
@ -2439,13 +2439,25 @@ ParseImport(WasmParseContext& c, bool newFormat, AstModule* module)
|
|||
AstRef sigRef;
|
||||
WasmToken openParen;
|
||||
if (c.ts.getIf(WasmToken::OpenParen, &openParen)) {
|
||||
if (newFormat && c.ts.getIf(WasmToken::Memory)) {
|
||||
AstResizable memory;
|
||||
if (!ParseResizable(c, &memory))
|
||||
return nullptr;
|
||||
if (!c.ts.match(WasmToken::CloseParen, c.error))
|
||||
return nullptr;
|
||||
return new(c.lifo) AstImport(name, moduleName.text(), fieldName.text(), memory);
|
||||
if (newFormat) {
|
||||
if (c.ts.getIf(WasmToken::Memory)) {
|
||||
AstResizable memory;
|
||||
if (!ParseResizable(c, &memory))
|
||||
return nullptr;
|
||||
if (!c.ts.match(WasmToken::CloseParen, c.error))
|
||||
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)) {
|
||||
|
@ -3558,9 +3570,8 @@ EncodeImport(Encoder& e, bool newFormat, AstImport& imp)
|
|||
return false;
|
||||
break;
|
||||
case DefinitionKind::Table:
|
||||
MOZ_CRASH("NYI");
|
||||
case DefinitionKind::Memory:
|
||||
if (!EncodeResizable(e, imp.memory()))
|
||||
if (!EncodeResizable(e, imp.resizable()))
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -10,6 +10,10 @@ const mem1Page = new Memory({initial:1});
|
|||
const mem2Page = new Memory({initial:2});
|
||||
const mem3Page = new Memory({initial:3});
|
||||
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
|
||||
// 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: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 (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 (import "a" "b" (table 2 1)))')), TypeError, /maximum length less than initial length/);
|
||||
|
||||
// Import order:
|
||||
|
||||
|
@ -241,16 +258,24 @@ var e = new Instance(new Module(code), {a:{b:mem}}).exports;
|
|||
assertEq(mem, e.foo);
|
||||
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
|
||||
|
||||
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" 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)) (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
|
||||
|
||||
|
@ -278,3 +303,25 @@ assertEq(i8[2], 0x0);
|
|||
assertEq(i8[100], 0xc);
|
||||
assertEq(i8[101], 0xd);
|
||||
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(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
|
||||
// should not hold their originating table alive. Live exported functions should
|
||||
// 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_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_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_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_UNREACHABLE, 0, JSEXN_ERR, "unreachable executed")
|
||||
MSG_DEF(JSMSG_WASM_INTEGER_OVERFLOW, 0, JSEXN_ERR, "integer overflow")
|
||||
|
|
Загрузка…
Ссылка в новой задаче