Bug 1284155 - Baldr: add Table exports (r=bbouvier)

MozReview-Commit-ID: 4RVN5vi2ZQC

--HG--
extra : rebase_source : 39118e95455995521b1902013937b76d79dab5f1
This commit is contained in:
Luke Wagner 2016-07-13 12:34:44 -05:00
Родитель 3c2b9e1f36
Коммит 3450e6c004
13 изменённых файлов: 100 добавлений и 20 удалений

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

@ -551,12 +551,12 @@ class AstExport : public AstNode
AstExport(AstName name, AstRef func) AstExport(AstName name, AstRef func)
: name_(name), kind_(DefinitionKind::Function), func_(func) : name_(name), kind_(DefinitionKind::Function), func_(func)
{} {}
explicit AstExport(AstName name) explicit AstExport(AstName name, DefinitionKind kind)
: name_(name), kind_(DefinitionKind::Memory) : name_(name), kind_(kind)
{} {}
AstName name() const { return name_; } AstName name() const { return name_; }
DefinitionKind kind() const { return kind_; } DefinitionKind kind() const { return kind_; }
AstRef& func() { return func_; } AstRef& func() { MOZ_ASSERT(kind_ == DefinitionKind::Function); return func_; }
}; };
class AstDataSegment : public AstNode class AstDataSegment : public AstNode

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

@ -69,7 +69,8 @@ enum class TypeConstructor
enum class DefinitionKind enum class DefinitionKind
{ {
Function = 0x00, Function = 0x00,
Memory = 0x01 Table = 0x01,
Memory = 0x02
}; };
enum class ResizableFlags enum class ResizableFlags

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

@ -1262,7 +1262,8 @@ AstDecodeMemorySection(AstDecodeContext& c)
return AstDecodeFail(c, "expected exported byte"); return AstDecodeFail(c, "expected exported byte");
if (exported) { if (exported) {
AstExport* export_ = new(c.lifo) AstExport(AstName(MOZ_UTF16("memory"))); AstName fieldName(MOZ_UTF16("memory"));
AstExport* export_ = new(c.lifo) AstExport(fieldName, DefinitionKind::Memory);
if (!export_ || !c.module().append(export_)) if (!export_ || !c.module().append(export_))
return false; return false;
} }

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

@ -944,7 +944,7 @@ DecodeExport(Decoder& d, bool newFormat, ModuleGenerator& mg, CStringSet* dupSet
return Fail(d, "expected export internal index"); return Fail(d, "expected export internal index");
if (funcIndex >= mg.numFuncSigs()) if (funcIndex >= mg.numFuncSigs())
return Fail(d, "export function index out of range"); return Fail(d, "exported function index out of bounds");
if (!CheckTypeForJS(d, mg.funcSig(funcIndex))) if (!CheckTypeForJS(d, mg.funcSig(funcIndex)))
return false; return false;
@ -971,20 +971,30 @@ DecodeExport(Decoder& d, bool newFormat, ModuleGenerator& mg, CStringSet* dupSet
return Fail(d, "expected export internal index"); return Fail(d, "expected export internal index");
if (funcIndex >= mg.numFuncSigs()) if (funcIndex >= mg.numFuncSigs())
return Fail(d, "export function index out of range"); return Fail(d, "exported function index out of bounds");
if (!CheckTypeForJS(d, mg.funcSig(funcIndex))) if (!CheckTypeForJS(d, mg.funcSig(funcIndex)))
return false; return false;
return mg.declareFuncExport(Move(fieldName), funcIndex); return mg.declareFuncExport(Move(fieldName), funcIndex);
} }
case DefinitionKind::Table: {
uint32_t tableIndex;
if (!d.readVarU32(&tableIndex))
return Fail(d, "expected table index");
if (tableIndex >= mg.tables().length())
return Fail(d, "exported table index out of bounds");
return mg.addTableExport(Move(fieldName));
}
case DefinitionKind::Memory: { case DefinitionKind::Memory: {
uint32_t memoryIndex; uint32_t memoryIndex;
if (!d.readVarU32(&memoryIndex)) if (!d.readVarU32(&memoryIndex))
return Fail(d, "expected memory index"); return Fail(d, "expected memory index");
if (memoryIndex > 0 || !mg.usesMemory()) if (memoryIndex > 0 || !mg.usesMemory())
return Fail(d, "memory index out of bounds"); return Fail(d, "exported memory index out of bounds");
return mg.addMemoryExport(Move(fieldName)); return mg.addMemoryExport(Move(fieldName));
} }

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

@ -680,6 +680,12 @@ ModuleGenerator::numExports() const
return metadata_->funcExports.length(); return metadata_->funcExports.length();
} }
bool
ModuleGenerator::addTableExport(UniqueChars fieldName)
{
return exports_.emplaceBack(Move(fieldName), DefinitionKind::Table);
}
bool bool
ModuleGenerator::addMemoryExport(UniqueChars fieldName) ModuleGenerator::addMemoryExport(UniqueChars fieldName)
{ {

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

@ -175,8 +175,9 @@ class MOZ_STACK_CLASS ModuleGenerator
// Exports: // Exports:
MOZ_MUST_USE bool declareFuncExport(UniqueChars fieldName, uint32_t funcIndex, MOZ_MUST_USE bool declareFuncExport(UniqueChars fieldName, uint32_t funcIndex,
uint32_t* funcExportIndex = nullptr); uint32_t* funcExportIndex = nullptr);
uint32_t numExports() const; MOZ_MUST_USE bool addTableExport(UniqueChars fieldName);
MOZ_MUST_USE bool addMemoryExport(UniqueChars fieldName); MOZ_MUST_USE bool addMemoryExport(UniqueChars fieldName);
uint32_t numExports() const;
// Function definitions: // Function definitions:
MOZ_MUST_USE bool startFuncDefs(); MOZ_MUST_USE bool startFuncDefs();

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

@ -80,6 +80,7 @@ class Instance
const CodeSegment& codeSegment() const { return *codeSegment_; } const CodeSegment& codeSegment() const { return *codeSegment_; }
const Metadata& metadata() const { return *metadata_; } const Metadata& metadata() const { return *metadata_; }
const SharedTableVector& tables() const { return tables_; }
SharedMem<uint8_t*> memoryBase() const; SharedMem<uint8_t*> memoryBase() const;
size_t memoryLength() const; size_t memoryLength() const;

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

@ -110,6 +110,8 @@ GetImports(JSContext* cx, HandleObject importObj, const ImportVector& imports,
return false; return false;
break; break;
case DefinitionKind::Table:
MOZ_CRASH("NYI");
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, "import object field is not a Memory");
@ -637,8 +639,10 @@ const JSPropertySpec WasmTableObject::properties[] =
{ JS_PS_END }; { JS_PS_END };
/* static */ WasmTableObject* /* static */ WasmTableObject*
WasmTableObject::create(ExclusiveContext* cx, Table& table, HandleObject proto) WasmTableObject::create(JSContext* cx, Table& table)
{ {
RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmTable).toObject());
AutoSetNewObjectMetadata metadata(cx); AutoSetNewObjectMetadata metadata(cx);
auto* obj = NewObjectWithGivenProto<WasmTableObject>(cx, proto); auto* obj = NewObjectWithGivenProto<WasmTableObject>(cx, proto);
if (!obj) if (!obj)
@ -688,8 +692,7 @@ WasmTableObject::construct(JSContext* cx, unsigned argc, Value* vp)
if (!table) if (!table)
return false; return false;
RootedObject proto(cx, &cx->global()->getPrototype(JSProto_WasmTable).toObject()); RootedWasmTableObject tableObj(cx, WasmTableObject::create(cx, *table));
RootedWasmTableObject tableObj(cx, WasmTableObject::create(cx, *table, proto));
if (!tableObj) if (!tableObj)
return false; return false;

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

@ -166,14 +166,13 @@ class WasmTableObject : public NativeObject
static const JSPropertySpec properties[]; static const JSPropertySpec properties[];
static bool construct(JSContext*, unsigned, Value*); static bool construct(JSContext*, unsigned, Value*);
static WasmTableObject* create(ExclusiveContext* cx, static WasmTableObject* create(JSContext* cx, wasm::Table& table);
wasm::Table& table,
HandleObject proto);
wasm::Table& table() const; wasm::Table& table() const;
}; };
typedef Rooted<WasmTableObject*> RootedWasmTableObject; typedef Rooted<WasmTableObject*> RootedWasmTableObject;
typedef Handle<WasmTableObject*> HandleWasmTableObject; typedef Handle<WasmTableObject*> HandleWasmTableObject;
typedef MutableHandle<WasmTableObject*> MutableHandleWasmTableObject;
} // namespace js } // namespace js

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

@ -464,9 +464,12 @@ CreateExportObject(JSContext* cx,
HandleWasmInstanceObject instanceObj, HandleWasmInstanceObject instanceObj,
HandleWasmMemoryObject memoryObj, HandleWasmMemoryObject memoryObj,
const ExportVector& exports, const ExportVector& exports,
const Metadata& metadata,
MutableHandleObject exportObj) MutableHandleObject exportObj)
{ {
const Instance& instance = instanceObj->instance();
const Metadata& metadata = instance.metadata();
const SharedTableVector& tables = instance.tables();
if (metadata.isAsmJS() && exports.length() == 1 && strlen(exports[0].fieldName()) == 0) { if (metadata.isAsmJS() && exports.length() == 1 && strlen(exports[0].fieldName()) == 0) {
exportObj.set(NewExportedFunction(cx, instanceObj, 0)); exportObj.set(NewExportedFunction(cx, instanceObj, 0));
return !!exportObj; return !!exportObj;
@ -483,6 +486,7 @@ CreateExportObject(JSContext* cx,
return false; return false;
} }
RootedWasmTableObject tableObj(cx);
for (const Export& exp : exports) { for (const Export& exp : exports) {
JSAtom* atom = AtomizeUTF8Chars(cx, exp.fieldName(), strlen(exp.fieldName())); JSAtom* atom = AtomizeUTF8Chars(cx, exp.fieldName(), strlen(exp.fieldName()));
if (!atom) if (!atom)
@ -494,6 +498,15 @@ CreateExportObject(JSContext* cx,
case DefinitionKind::Function: case DefinitionKind::Function:
val = vals[exp.funcExportIndex()]; val = vals[exp.funcExportIndex()];
break; break;
case DefinitionKind::Table:
MOZ_ASSERT(tables.length() == 1);
if (!tableObj) {
tableObj = WasmTableObject::create(cx, *tables[0]);
if (!tableObj)
return false;
}
val = ObjectValue(*tableObj);
break;
case DefinitionKind::Memory: case DefinitionKind::Memory:
if (metadata.assumptions.newFormat) if (metadata.assumptions.newFormat)
val = ObjectValue(*memoryObj); val = ObjectValue(*memoryObj);
@ -562,7 +575,7 @@ Module::instantiate(JSContext* cx,
// Create the export object. // Create the export object.
RootedObject exportObj(cx); RootedObject exportObj(cx);
if (!CreateExportObject(cx, instanceObj, memory, exports_, *metadata_, &exportObj)) if (!CreateExportObject(cx, instanceObj, memory, exports_, &exportObj))
return false; return false;
instanceObj->initExportsObject(exportObj); instanceObj->initExportsObject(exportObj);

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

@ -2485,8 +2485,10 @@ ParseExport(WasmParseContext& c)
return new(c.lifo) AstExport(name.text(), AstRef(AstName(), exportee.index())); return new(c.lifo) AstExport(name.text(), AstRef(AstName(), exportee.index()));
case WasmToken::Name: case WasmToken::Name:
return new(c.lifo) AstExport(name.text(), AstRef(exportee.name(), AstNoIndex)); return new(c.lifo) AstExport(name.text(), AstRef(exportee.name(), AstNoIndex));
case WasmToken::Table:
return new(c.lifo) AstExport(name.text(), DefinitionKind::Table);
case WasmToken::Memory: case WasmToken::Memory:
return new(c.lifo) AstExport(name.text()); return new(c.lifo) AstExport(name.text(), DefinitionKind::Memory);
default: default:
break; break;
} }
@ -3075,6 +3077,7 @@ ResolveModule(LifoAlloc& lifo, AstModule* module, UniqueChars* error)
return false; return false;
break; break;
case DefinitionKind::Memory: case DefinitionKind::Memory:
case DefinitionKind::Table:
break; break;
} }
} }
@ -3554,6 +3557,8 @@ EncodeImport(Encoder& e, bool newFormat, AstImport& imp)
if (!e.writeVarU32(imp.funcSig().index())) if (!e.writeVarU32(imp.funcSig().index()))
return false; return false;
break; break;
case DefinitionKind::Table:
MOZ_CRASH("NYI");
case DefinitionKind::Memory: case DefinitionKind::Memory:
if (!EncodeResizable(e, imp.memory())) if (!EncodeResizable(e, imp.memory()))
return false; return false;
@ -3651,6 +3656,7 @@ EncodeExport(Encoder& e, bool newFormat, AstExport& exp)
if (!e.writeVarU32(exp.func().index())) if (!e.writeVarU32(exp.func().index()))
return false; return false;
break; break;
case DefinitionKind::Table:
case DefinitionKind::Memory: case DefinitionKind::Memory:
if (!e.writeVarU32(0)) if (!e.writeVarU32(0))
return false; return false;

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

@ -29,8 +29,8 @@ wasmEvalText('(module (func) (func) (export "a" 1))');
wasmEvalText('(module (func $a) (func $b) (export "a" $a) (export "b" $b))'); wasmEvalText('(module (func $a) (func $b) (export "a" $a) (export "b" $b))');
wasmEvalText('(module (func $a) (func $b) (export "a" $a) (export "b" $b))'); wasmEvalText('(module (func $a) (func $b) (export "a" $a) (export "b" $b))');
assertErrorMessage(() => wasmEvalText('(module (func) (export "a" 1))'), TypeError, /export function index out of range/); assertErrorMessage(() => wasmEvalText('(module (func) (export "a" 1))'), TypeError, /exported function index out of bounds/);
assertErrorMessage(() => wasmEvalText('(module (func) (func) (export "a" 2))'), TypeError, /export function index out of range/); assertErrorMessage(() => wasmEvalText('(module (func) (func) (export "a" 2))'), TypeError, /exported function index out of bounds/);
var o = wasmEvalText('(module (func) (export "a" 0) (export "b" 0))'); var o = wasmEvalText('(module (func) (export "a" 0) (export "b" 0))');
assertEq(Object.getOwnPropertyNames(o).sort().toString(), "a,b"); assertEq(Object.getOwnPropertyNames(o).sort().toString(), "a,b");

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

@ -56,6 +56,7 @@ assertEq(new Instance(m5, {a:{b:mem4Page}}) instanceof Instance, true);
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/);
// Import order: // Import order:
@ -158,6 +159,38 @@ assertEq(Object.keys(e).length, 1);
assertEq(String(Object.keys(e)), ""); assertEq(String(Object.keys(e)), "");
assertEq(e[""] instanceof Memory, true); assertEq(e[""] instanceof Memory, true);
var code = textToBinary('(module (table) (export "tbl" table))');
var e = new Instance(new Module(code)).exports;
assertEq(Object.keys(e).join(), "tbl");
assertEq(e.tbl instanceof Table, true);
var code = textToBinary('(module (table (resizable 1)) (export "t1" table) (export "t2" table))');
var e = new Instance(new Module(code)).exports;
assertEq(Object.keys(e).join(), "t1,t2");
assertEq(e.t1 instanceof Table, true);
assertEq(e.t2 instanceof Table, true);
assertEq(e.t1, e.t2);
var code = textToBinary('(module (table) (memory 1 1) (func) (export "t" table) (export "m" memory) (export "f" 0))');
var e = new Instance(new Module(code)).exports;
assertEq(Object.keys(e).join(), "t,m,f");
assertEq(e.f(), undefined);
assertEq(e.t instanceof Table, true);
assertEq(e.m instanceof Memory, true);
var code = textToBinary('(module (table) (memory 1 1) (func) (export "m" memory) (export "f" 0) (export "t" table))');
var e = new Instance(new Module(code)).exports;
assertEq(Object.keys(e).join(), "m,f,t");
assertEq(e.f(), undefined);
assertEq(e.t instanceof Table, true);
assertEq(e.m instanceof Memory, true);
var code = textToBinary('(module (table) (export "" table))');
var e = new Instance(new Module(code)).exports;
assertEq(Object.keys(e).length, 1);
assertEq(String(Object.keys(e)), "");
assertEq(e[""] instanceof Table, true);
// Re-exports: // Re-exports:
var code = textToBinary('(module (import "a" "b" (memory 1 1)) (export "foo" memory) (export "bar" memory))'); var code = textToBinary('(module (import "a" "b" (memory 1 1)) (export "foo" memory) (export "bar" memory))');
@ -166,6 +199,12 @@ 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);
// 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 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/);