Bug 1459900 - Struct types: read, write, validate. r=luke

We introduce a simple "struct" type definition for Wasm.  A struct has
fields of primitive types, including anyref, but no other information,
notably no information about subtype relationships.

The syntax is:

  (type $tname (struct (field $fname i32) ...))

where the $fnames are currently ignored.

(In the future, the $fnames will denote the field numbers of their
fields within the structure and will be used by the struct.get and
struct.set instructions in the text format.  If any $fname is bound in
multiple structures the bindings must resolve to the same field number
and field type.)

To record the type information there is a new StructType type in
WasmTypes.h.

We generalize the SigWithId table in ModuleEnvironment to instead be a
TypeDef table, where a TypeDef is a tagged union of SigWithId and
StructType.

Similarly, there is a new AstTypeDef base class for AstSig and
AstStruct, and the sigs_ table in AstModule becomes a types_ table.

When the ModuleEnvironment is about to be destroyed we move the
StructType types into a dense structTypes_ table in the Module; a
later patch will make use of these types.  The structTypes_ get
serialized and deserialized with the module.

--HG--
extra : rebase_source : 62458ece9793c58fc1b813262e605916477c9954
This commit is contained in:
Lars T Hansen 2018-05-08 14:18:30 +02:00
Родитель 3f9f7d8191
Коммит 50a17eeb52
18 изменённых файлов: 823 добавлений и 172 удалений

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

@ -281,8 +281,8 @@ const v2vSigSection = sigSection([v2vSig]);
const i2vSig = {args:[I32Code], ret:VoidCode}; const i2vSig = {args:[I32Code], ret:VoidCode};
const v2vBody = funcBody({locals:[], body:[]}); const v2vBody = funcBody({locals:[], body:[]});
assertErrorMessage(() => wasmEval(moduleWithSections([ {name: typeId, body: U32MAX_LEB } ])), CompileError, /too many signatures/); assertErrorMessage(() => wasmEval(moduleWithSections([ {name: typeId, body: U32MAX_LEB } ])), CompileError, /too many types/);
assertErrorMessage(() => wasmEval(moduleWithSections([ {name: typeId, body: [1, 0], } ])), CompileError, /expected function form/); assertErrorMessage(() => wasmEval(moduleWithSections([ {name: typeId, body: [1, 0], } ])), CompileError, /expected type form/);
assertErrorMessage(() => wasmEval(moduleWithSections([ {name: typeId, body: [1, FuncCode, ...U32MAX_LEB], } ])), CompileError, /too many arguments in signature/); assertErrorMessage(() => wasmEval(moduleWithSections([ {name: typeId, body: [1, FuncCode, ...U32MAX_LEB], } ])), CompileError, /too many arguments in signature/);
assertThrowsInstanceOf(() => wasmEval(moduleWithSections([{name: typeId, body: [1]}])), CompileError); assertThrowsInstanceOf(() => wasmEval(moduleWithSections([{name: typeId, body: [1]}])), CompileError);

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

@ -0,0 +1,162 @@
if (!wasmGcEnabled()) {
assertErrorMessage(() => wasmEvalText(`(module (type $s (struct)))`),
WebAssembly.CompileError, /Structure types not enabled/);
quit();
}
var bin = wasmTextToBinary(
`(module
(table 2 anyfunc)
(elem (i32.const 0) $doit $doitagain)
;; Type array has a mix of types
(type $f1 (func (param i32) (result i32)))
(type $point (struct
(field $point_x i32)
(field $point_y i32)))
(type $f2 (func (param f64) (result f64)))
(type $int_node (struct
(field $intbox_val (mut i32))
(field $intbox_next (mut anyref))))
;; Test all the types.
(type $omni (struct
(field $omni_i32 i32)
(field $omni_i32m (mut i32))
(field $omni_i64 i64)
(field $omni_i64m (mut i64))
(field $omni_f32 f32)
(field $omni_f32m (mut f32))
(field $omni_f64 f64)
(field $omni_f64m (mut f64))
(field $omni_anyref anyref)
(field $omni_anyrefm (mut anyref))))
;; Various ways to reference a type in the middle of the
;; type array, make sure we get the right one
(func $x1 (import "m" "x1") (type $f1))
(func $x2 (import "m" "x2") (type $f2))
(func (export "hello") (param f64) (param i32) (result f64)
(call_indirect $f2 (get_local 0) (get_local 1)))
(func $doit (param f64) (result f64)
(f64.sqrt (get_local 0)))
(func $doitagain (param f64) (result f64)
(f64.mul (get_local 0) (get_local 0)))
(func (export "x1") (param i32) (result i32)
(call $x1 (get_local 0)))
(func (export "x2") (param f64) (result f64)
(call $x2 (get_local 0)))
)`)
var mod = new WebAssembly.Module(bin);
var ins = new WebAssembly.Instance(mod, {m:{x1(x){ return x*3 }, x2(x){ return Math.PI }}}).exports;
assertEq(ins.hello(4.0, 0), 2.0)
assertEq(ins.hello(4.0, 1), 16.0)
assertEq(ins.x1(12), 36)
assertEq(ins.x2(8), Math.PI)
// Crude but at least checks that we have *something*.
var txt = wasmBinaryToText(bin);
var re = /\(type\s+\$[a-z0-9]+\s+\(struct/gm;
assertEq(Array.isArray(re.exec(txt)), true);
assertEq(Array.isArray(re.exec(txt)), true);
assertEq(Array.isArray(re.exec(txt)), true);
assertEq(Array.isArray(re.exec(txt)), false);
// The field name is optional, so this should work.
wasmEvalText(`
(module
(type $s (struct (field i32))))
`)
// Empty structs are OK.
wasmEvalText(`
(module
(type $s (struct)))
`)
// Bogus type definition syntax.
assertErrorMessage(() => wasmEvalText(`
(module
(type $s))
`),
SyntaxError, /parsing wasm text/);
assertErrorMessage(() => wasmEvalText(`
(module
(type $s (field $x i32)))
`),
SyntaxError, /bad type definition/);
assertErrorMessage(() => wasmEvalText(`
(module
(type $s (struct (field $x i31))))
`),
SyntaxError, /parsing wasm text/);
assertErrorMessage(() => wasmEvalText(`
(module
(type $s (struct (fjeld $x i32))))
`),
SyntaxError, /parsing wasm text/);
assertErrorMessage(() => wasmEvalText(`
(module
(type $s (struct abracadabra)))
`),
SyntaxError, /parsing wasm text/);
// Function should not reference struct type: syntactic test
assertErrorMessage(() => wasmEvalText(`
(module
(type $s (struct))
(type $f (func (param i32) (result i32)))
(func (type 0) (param i32) (result i32) (unreachable)))
`),
WebAssembly.CompileError, /signature index references non-signature/);
// Function should not reference struct type: binary test
var bad = new Uint8Array([0x00, 0x61, 0x73, 0x6d,
0x01, 0x00, 0x00, 0x00,
0x01, // Type section
0x03, // Section size
0x01, // One type
0x50, // Struct
0x00, // Zero fields
0x03, // Function section
0x02, // Section size
0x01, // One function
0x00, // Type of function
0x0a, // Code section
0x05, // Section size
0x01, // One body
0x03, // Body size
0x00, // Zero locals
0x00, // UNREACHABLE
0x0b]); // END
assertErrorMessage(() => new WebAssembly.Module(bad),
WebAssembly.CompileError, /signature index references non-signature/);

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

@ -1638,17 +1638,17 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED ModuleValidator
class HashableSig class HashableSig
{ {
uint32_t sigIndex_; uint32_t sigIndex_;
const SigWithIdVector& sigs_; const TypeDefVector& types_;
public: public:
HashableSig(uint32_t sigIndex, const SigWithIdVector& sigs) HashableSig(uint32_t sigIndex, const TypeDefVector& types)
: sigIndex_(sigIndex), sigs_(sigs) : sigIndex_(sigIndex), types_(types)
{} {}
uint32_t sigIndex() const { uint32_t sigIndex() const {
return sigIndex_; return sigIndex_;
} }
const Sig& sig() const { const Sig& sig() const {
return sigs_[sigIndex_]; return types_[sigIndex_].funcType();
} }
// Implement HashPolicy: // Implement HashPolicy:
@ -1666,8 +1666,8 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED ModuleValidator
PropertyName* name_; PropertyName* name_;
public: public:
NamedSig(PropertyName* name, uint32_t sigIndex, const SigWithIdVector& sigs) NamedSig(PropertyName* name, uint32_t sigIndex, const TypeDefVector& types)
: HashableSig(sigIndex, sigs), name_(name) : HashableSig(sigIndex, types), name_(name)
{} {}
PropertyName* name() const { PropertyName* name() const {
return name_; return name_;
@ -1755,22 +1755,22 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED ModuleValidator
return standardLibrarySimdOpNames_.putNew(atom->asPropertyName(), op); return standardLibrarySimdOpNames_.putNew(atom->asPropertyName(), op);
} }
bool newSig(Sig&& sig, uint32_t* sigIndex) { bool newSig(Sig&& sig, uint32_t* sigIndex) {
if (env_.sigs.length() >= MaxTypes) if (env_.types.length() >= MaxTypes)
return failCurrentOffset("too many signatures"); return failCurrentOffset("too many signatures");
*sigIndex = env_.sigs.length(); *sigIndex = env_.types.length();
return env_.sigs.append(std::move(sig)); return env_.types.append(std::move(sig));
} }
bool declareSig(Sig&& sig, uint32_t* sigIndex) { bool declareSig(Sig&& sig, uint32_t* sigIndex) {
SigSet::AddPtr p = sigSet_.lookupForAdd(sig); SigSet::AddPtr p = sigSet_.lookupForAdd(sig);
if (p) { if (p) {
*sigIndex = p->sigIndex(); *sigIndex = p->sigIndex();
MOZ_ASSERT(env_.sigs[*sigIndex] == sig); MOZ_ASSERT(env_.types[*sigIndex].funcType() == sig);
return true; return true;
} }
return newSig(std::move(sig), sigIndex) && return newSig(std::move(sig), sigIndex) &&
sigSet_.add(p, HashableSig(*sigIndex, env_.sigs)); sigSet_.add(p, HashableSig(*sigIndex, env_.types));
} }
public: public:
@ -2308,7 +2308,7 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED ModuleValidator
if (!declareSig(std::move(sig), &sigIndex)) if (!declareSig(std::move(sig), &sigIndex))
return false; return false;
return funcImportMap_.add(p, NamedSig(name, sigIndex, env_.sigs), *importIndex); return funcImportMap_.add(p, NamedSig(name, sigIndex, env_.types), *importIndex);
} }
bool tryConstantAccess(uint64_t start, uint64_t width) { bool tryConstantAccess(uint64_t start, uint64_t width) {
@ -2457,12 +2457,12 @@ class MOZ_STACK_CLASS JS_HAZ_ROOTED ModuleValidator
for (FuncImportMap::Range r = funcImportMap_.all(); !r.empty(); r.popFront()) { for (FuncImportMap::Range r = funcImportMap_.all(); !r.empty(); r.popFront()) {
uint32_t funcIndex = r.front().value(); uint32_t funcIndex = r.front().value();
MOZ_ASSERT(!env_.funcSigs[funcIndex]); MOZ_ASSERT(!env_.funcSigs[funcIndex]);
env_.funcSigs[funcIndex] = &env_.sigs[r.front().key().sigIndex()]; env_.funcSigs[funcIndex] = &env_.types[r.front().key().sigIndex()].funcType();
} }
for (const Func& func : funcDefs_) { for (const Func& func : funcDefs_) {
uint32_t funcIndex = funcImportMap_.count() + func.funcDefIndex(); uint32_t funcIndex = funcImportMap_.count() + func.funcDefIndex();
MOZ_ASSERT(!env_.funcSigs[funcIndex]); MOZ_ASSERT(!env_.funcSigs[funcIndex]);
env_.funcSigs[funcIndex] = &env_.sigs[func.sigIndex()]; env_.funcSigs[funcIndex] = &env_.types[func.sigIndex()].funcType();
} }
if (!env_.funcImportGlobalDataOffsets.resize(funcImportMap_.count())) if (!env_.funcImportGlobalDataOffsets.resize(funcImportMap_.count()))
@ -4893,7 +4893,7 @@ CheckFunctionSignature(ModuleValidator& m, ParseNode* usepn, Sig&& sig, Property
return m.addFuncDef(name, usepn->pn_pos.begin, std::move(sig), func); return m.addFuncDef(name, usepn->pn_pos.begin, std::move(sig), func);
} }
const SigWithId& existingSig = m.env().sigs[existing->sigIndex()]; const SigWithId& existingSig = m.env().types[existing->sigIndex()].funcType();
if (!CheckSignatureAgainstExisting(m, usepn, sig, existingSig)) if (!CheckSignatureAgainstExisting(m, usepn, sig, existingSig))
return false; return false;
@ -4951,7 +4951,7 @@ CheckFuncPtrTableAgainstExisting(ModuleValidator& m, ParseNode* usepn, PropertyN
if (mask != table.mask()) if (mask != table.mask())
return m.failf(usepn, "mask does not match previous value (%u)", table.mask()); return m.failf(usepn, "mask does not match previous value (%u)", table.mask());
if (!CheckSignatureAgainstExisting(m, usepn, sig, m.env().sigs[table.sigIndex()])) if (!CheckSignatureAgainstExisting(m, usepn, sig, m.env().types[table.sigIndex()].funcType()))
return false; return false;
*tableIndex = existing->tableIndex(); *tableIndex = existing->tableIndex();
@ -7376,7 +7376,7 @@ CheckFuncPtrTable(ModuleValidator& m, ParseNode* var)
if (!func) if (!func)
return m.fail(elem, "function-pointer table's elements must be names of functions"); return m.fail(elem, "function-pointer table's elements must be names of functions");
const Sig& funcSig = m.env().sigs[func->sigIndex()]; const Sig& funcSig = m.env().types[func->sigIndex()].funcType();
if (sig) { if (sig) {
if (*sig != funcSig) if (*sig != funcSig)
return m.fail(elem, "all functions in table must have same signature"); return m.fail(elem, "all functions in table must have same signature");

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

@ -126,7 +126,29 @@ struct AstBase
} }
}; };
class AstSig : public AstBase class AstSig;
class AstStruct;
class AstTypeDef : public AstBase
{
protected:
enum class Which { IsSig, IsStruct };
private:
Which which_;
public:
explicit AstTypeDef(Which which) : which_(which) {}
bool isSig() const { return which_ == Which::IsSig; }
bool isStruct() const { return which_ == Which::IsStruct; }
inline AstSig& asSig();
inline AstStruct& asStruct();
inline const AstSig& asSig() const;
inline const AstStruct& asStruct() const;
};
class AstSig : public AstTypeDef
{ {
AstName name_; AstName name_;
AstValTypeVector args_; AstValTypeVector args_;
@ -134,15 +156,18 @@ class AstSig : public AstBase
public: public:
explicit AstSig(LifoAlloc& lifo) explicit AstSig(LifoAlloc& lifo)
: args_(lifo), : AstTypeDef(Which::IsSig),
args_(lifo),
ret_(ExprType::Void) ret_(ExprType::Void)
{} {}
AstSig(AstValTypeVector&& args, ExprType ret) AstSig(AstValTypeVector&& args, ExprType ret)
: args_(std::move(args)), : AstTypeDef(Which::IsSig),
args_(std::move(args)),
ret_(ret) ret_(ret)
{} {}
AstSig(AstName name, AstSig&& rhs) AstSig(AstName name, AstSig&& rhs)
: name_(name), : AstTypeDef(Which::IsSig),
name_(name),
args_(std::move(rhs.args_)), args_(std::move(rhs.args_)),
ret_(rhs.ret_) ret_(rhs.ret_)
{} {}
@ -171,6 +196,68 @@ class AstSig : public AstBase
} }
}; };
class AstStruct : public AstTypeDef
{
AstName name_;
AstNameVector fieldNames_;
AstValTypeVector fieldTypes_;
public:
explicit AstStruct(LifoAlloc& lifo)
: AstTypeDef(Which::IsStruct),
fieldNames_(lifo),
fieldTypes_(lifo)
{}
AstStruct(AstNameVector&& names, AstValTypeVector&& types)
: AstTypeDef(Which::IsStruct),
fieldNames_(std::move(names)),
fieldTypes_(std::move(types))
{}
AstStruct(AstName name, AstStruct&& rhs)
: AstTypeDef(Which::IsStruct),
name_(name),
fieldNames_(std::move(rhs.fieldNames_)),
fieldTypes_(std::move(rhs.fieldTypes_))
{}
AstName name() const {
return name_;
}
const AstNameVector& fieldNames() const {
return fieldNames_;
}
const AstValTypeVector& fieldTypes() const {
return fieldTypes_;
}
};
inline AstSig&
AstTypeDef::asSig()
{
MOZ_ASSERT(isSig());
return *static_cast<AstSig*>(this);
}
inline AstStruct&
AstTypeDef::asStruct()
{
MOZ_ASSERT(isStruct());
return *static_cast<AstStruct*>(this);
}
inline const AstSig&
AstTypeDef::asSig() const
{
MOZ_ASSERT(isSig());
return *static_cast<const AstSig*>(this);
}
inline const AstStruct&
AstTypeDef::asStruct() const
{
MOZ_ASSERT(isStruct());
return *static_cast<const AstStruct*>(this);
}
const uint32_t AstNodeUnknownOffset = 0; const uint32_t AstNodeUnknownOffset = 0;
class AstNode : public AstBase class AstNode : public AstBase
@ -948,7 +1035,7 @@ class AstModule : public AstNode
typedef AstVector<AstFunc*> FuncVector; typedef AstVector<AstFunc*> FuncVector;
typedef AstVector<AstImport*> ImportVector; typedef AstVector<AstImport*> ImportVector;
typedef AstVector<AstExport*> ExportVector; typedef AstVector<AstExport*> ExportVector;
typedef AstVector<AstSig*> SigVector; typedef AstVector<AstTypeDef*> TypeDefVector;
typedef AstVector<AstName> NameVector; typedef AstVector<AstName> NameVector;
typedef AstVector<AstResizable> AstResizableVector; typedef AstVector<AstResizable> AstResizableVector;
@ -956,7 +1043,7 @@ class AstModule : public AstNode
typedef AstHashMap<AstSig*, uint32_t, AstSig> SigMap; typedef AstHashMap<AstSig*, uint32_t, AstSig> SigMap;
LifoAlloc& lifo_; LifoAlloc& lifo_;
SigVector sigs_; TypeDefVector types_;
SigMap sigMap_; SigMap sigMap_;
ImportVector imports_; ImportVector imports_;
NameVector funcImportNames_; NameVector funcImportNames_;
@ -974,7 +1061,7 @@ class AstModule : public AstNode
public: public:
explicit AstModule(LifoAlloc& lifo) explicit AstModule(LifoAlloc& lifo)
: lifo_(lifo), : lifo_(lifo),
sigs_(lifo), types_(lifo),
sigMap_(lifo), sigMap_(lifo),
imports_(lifo), imports_(lifo),
funcImportNames_(lifo), funcImportNames_(lifo),
@ -1038,21 +1125,21 @@ class AstModule : public AstNode
*sigIndex = p->value(); *sigIndex = p->value();
return true; return true;
} }
*sigIndex = sigs_.length(); *sigIndex = types_.length();
auto* lifoSig = new (lifo_) AstSig(AstName(), std::move(sig)); auto* lifoSig = new (lifo_) AstSig(AstName(), std::move(sig));
return lifoSig && return lifoSig &&
sigs_.append(lifoSig) && types_.append(lifoSig) &&
sigMap_.add(p, sigs_.back(), *sigIndex); sigMap_.add(p, static_cast<AstSig*>(types_.back()), *sigIndex);
} }
bool append(AstSig* sig) { bool append(AstSig* sig) {
uint32_t sigIndex = sigs_.length(); uint32_t sigIndex = types_.length();
if (!sigs_.append(sig)) if (!types_.append(sig))
return false; return false;
SigMap::AddPtr p = sigMap_.lookupForAdd(*sig); SigMap::AddPtr p = sigMap_.lookupForAdd(*sig);
return p || sigMap_.add(p, sig, sigIndex); return p || sigMap_.add(p, sig, sigIndex);
} }
const SigVector& sigs() const { const TypeDefVector& types() const {
return sigs_; return types_;
} }
bool append(AstFunc* func) { bool append(AstFunc* func) {
return funcs_.append(func); return funcs_.append(func);
@ -1060,6 +1147,9 @@ class AstModule : public AstNode
const FuncVector& funcs() const { const FuncVector& funcs() const {
return funcs_; return funcs_;
} }
bool append(AstStruct* str) {
return types_.append(str);
}
bool append(AstImport* imp) { bool append(AstImport* imp) {
switch (imp->kind()) { switch (imp->kind()) {
case DefinitionKind::Function: case DefinitionKind::Function:

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

@ -3762,7 +3762,7 @@ class BaseCompiler final : public BaseCompilerInterface
void callIndirect(uint32_t sigIndex, const Stk& indexVal, const FunctionCall& call) void callIndirect(uint32_t sigIndex, const Stk& indexVal, const FunctionCall& call)
{ {
const SigWithId& sig = env_.sigs[sigIndex]; const SigWithId& sig = env_.types[sigIndex].funcType();
MOZ_ASSERT(sig.id.kind() != SigIdDesc::Kind::None); MOZ_ASSERT(sig.id.kind() != SigIdDesc::Kind::None);
MOZ_ASSERT(env_.tables.length() == 1); MOZ_ASSERT(env_.tables.length() == 1);
@ -7861,7 +7861,7 @@ BaseCompiler::emitCallIndirect()
sync(); sync();
const SigWithId& sig = env_.sigs[sigIndex]; const SigWithId& sig = env_.types[sigIndex].funcType();
// Stack: ... arg1 .. argn callee // Stack: ... arg1 .. argn callee
@ -8830,6 +8830,9 @@ BaseCompiler::emitInstanceCall(uint32_t lineOrBytecode, const MIRTypeVector& sig
popValueStackBy(numArgs); popValueStackBy(numArgs);
// Note, a number of clients of emitInstanceCall currently assume that the
// following operation does not destroy ReturnReg.
pushReturnedIfNonVoid(baselineCall, retType); pushReturnedIfNonVoid(baselineCall, retType);
} }

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

@ -68,6 +68,9 @@ enum class TypeCode
// Type constructor for function types // Type constructor for function types
Func = 0x60, // SLEB128(-0x20) Func = 0x60, // SLEB128(-0x20)
// Type constructor for structure types - unofficial
Struct = 0x50, // SLEB128(-0x30)
// Special code representing the block signature ()->() // Special code representing the block signature ()->()
BlockVoid = 0x40, // SLEB128(-0x40) BlockVoid = 0x40, // SLEB128(-0x40)
@ -603,6 +606,7 @@ static const unsigned MaxElemSegments = 10000000;
static const unsigned MaxTableMaximumLength = 10000000; static const unsigned MaxTableMaximumLength = 10000000;
static const unsigned MaxLocals = 50000; static const unsigned MaxLocals = 50000;
static const unsigned MaxParams = 1000; static const unsigned MaxParams = 1000;
static const unsigned MaxStructFields = 1000;
static const unsigned MaxMemoryMaximumPages = 65536; static const unsigned MaxMemoryMaximumPages = 65536;
static const unsigned MaxStringBytes = 100000; static const unsigned MaxStringBytes = 100000;
static const unsigned MaxModuleBytes = 1024 * 1024 * 1024; static const unsigned MaxModuleBytes = 1024 * 1024 * 1024;

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

@ -97,12 +97,12 @@ class AstDecodeContext
public: public:
AstDecodeContext(JSContext* cx, LifoAlloc& lifo, Decoder& d, AstModule& module, AstDecodeContext(JSContext* cx, LifoAlloc& lifo, Decoder& d, AstModule& module,
bool generateNames) bool generateNames, HasGcTypes hasGcTypes)
: cx(cx), : cx(cx),
lifo(lifo), lifo(lifo),
d(d), d(d),
generateNames(generateNames), generateNames(generateNames),
env_(CompileMode::Once, Tier::Ion, DebugEnabled::False, HasGcTypes::False, env_(CompileMode::Once, Tier::Ion, DebugEnabled::False, hasGcTypes,
cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled() cx->realm()->creationOptions().getSharedMemoryAndAtomicsEnabled()
? Shareable::True ? Shareable::True
: Shareable::False), : Shareable::False),
@ -351,7 +351,7 @@ AstDecodeCallIndirect(AstDecodeContext& c)
if (!GenerateRef(c, AstName(u"type"), sigIndex, &sigRef)) if (!GenerateRef(c, AstName(u"type"), sigIndex, &sigRef))
return false; return false;
const SigWithId& sig = c.env().sigs[sigIndex]; const SigWithId& sig = c.env().types[sigIndex].funcType();
AstExprVector args(c.lifo); AstExprVector args(c.lifo);
if (!AstDecodeCallArgs(c, sig, &args)) if (!AstDecodeCallArgs(c, sig, &args))
return false; return false;
@ -1958,26 +1958,59 @@ AstDecodeFunctionBody(AstDecodeContext &c, uint32_t funcIndex, AstFunc** func)
// wasm decoding and generation // wasm decoding and generation
static bool static bool
AstCreateSignatures(AstDecodeContext& c) AstCreateTypes(AstDecodeContext& c)
{ {
SigWithIdVector& sigs = c.env().sigs; uint32_t typeIndexForNames = 0;
for (const TypeDef& td : c.env().types) {
if (td.isFuncType()) {
const Sig& sig = td.funcType();
for (size_t sigIndex = 0; sigIndex < sigs.length(); sigIndex++) { AstValTypeVector args(c.lifo);
const Sig& sig = sigs[sigIndex]; if (!args.appendAll(sig.args()))
return false;
AstValTypeVector args(c.lifo); AstSig sigNoName(std::move(args), sig.ret());
if (!args.appendAll(sig.args()))
return false;
AstSig sigNoName(std::move(args), sig.ret()); AstName sigName;
if (!GenerateName(c, AstName(u"type"), typeIndexForNames, &sigName))
return false;
AstName sigName; AstSig* astSig = new(c.lifo) AstSig(sigName, std::move(sigNoName));
if (!GenerateName(c, AstName(u"type"), sigIndex, &sigName)) if (!astSig || !c.module().append(astSig))
return false; return false;
} else if (td.isStructType()) {
const StructType& str = td.structType();
AstSig* astSig = new(c.lifo) AstSig(sigName, std::move(sigNoName)); AstValTypeVector fieldTypes(c.lifo);
if (!astSig || !c.module().append(astSig)) if (!fieldTypes.appendAll(str.fields_))
return false; return false;
AstNameVector fieldNames(c.lifo);
if (!fieldNames.resize(fieldTypes.length()))
return false;
// The multiplication ensures that generated field names are unique
// within the module, though the resulting namespace is very sparse.
for (size_t fieldIndex = 0; fieldIndex < fieldTypes.length(); fieldIndex++) {
size_t idx = (typeIndexForNames * MaxStructFields) + fieldIndex;
if (!GenerateName(c, AstName(u"f"), idx, &fieldNames[fieldIndex]))
return false;
}
AstStruct structNoName(std::move(fieldNames), std::move(fieldTypes));
AstName structName;
if (!GenerateName(c, AstName(u"type"), typeIndexForNames, &structName))
return false;
AstStruct* astStruct = new(c.lifo) AstStruct(structName, std::move(structNoName));
if (!astStruct || !c.module().append(astStruct))
return false;
} else {
MOZ_CRASH();
}
typeIndexForNames++;
} }
return true; return true;
@ -2230,7 +2263,7 @@ AstDecodeEnvironment(AstDecodeContext& c)
if (!DecodeModuleEnvironment(c.d, &c.env())) if (!DecodeModuleEnvironment(c.d, &c.env()))
return false; return false;
if (!AstCreateSignatures(c)) if (!AstCreateTypes(c))
return false; return false;
if (!AstCreateImports(c)) if (!AstCreateImports(c))
@ -2333,7 +2366,7 @@ wasm::BinaryToAst(JSContext* cx, const uint8_t* bytes, uint32_t length, LifoAllo
UniqueChars error; UniqueChars error;
Decoder d(bytes, bytes + length, 0, &error, nullptr, /* resilient */ true); Decoder d(bytes, bytes + length, 0, &error, nullptr, /* resilient */ true);
AstDecodeContext c(cx, lifo, d, *result, true); AstDecodeContext c(cx, lifo, d, *result, /* generateNames */ true, HasGcTypes::True);
if (!AstDecodeEnvironment(c) || if (!AstDecodeEnvironment(c) ||
!AstDecodeCodeSection(c) || !AstDecodeCodeSection(c) ||

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

@ -191,6 +191,7 @@ RenderExprType(WasmRenderContext& c, ExprType type)
case ExprType::I64: return c.buffer.append("i64"); case ExprType::I64: return c.buffer.append("i64");
case ExprType::F32: return c.buffer.append("f32"); case ExprType::F32: return c.buffer.append("f32");
case ExprType::F64: return c.buffer.append("f64"); case ExprType::F64: return c.buffer.append("f64");
case ExprType::AnyRef: return c.buffer.append("anyref");
default:; default:;
} }
@ -209,6 +210,12 @@ RenderName(WasmRenderContext& c, const AstName& name)
return c.buffer.append(name.begin(), name.end()); return c.buffer.append(name.begin(), name.end());
} }
static bool
RenderNonemptyName(WasmRenderContext& c, const AstName& name)
{
return name.empty() || (RenderName(c, name) && c.buffer.append(' '));
}
static bool static bool
RenderRef(WasmRenderContext& c, const AstRef& ref) RenderRef(WasmRenderContext& c, const AstRef& ref)
{ {
@ -1481,12 +1488,8 @@ RenderSignature(WasmRenderContext& c, const AstSig& sig, const AstNameVector* ma
if (!c.buffer.append(" (param ")) if (!c.buffer.append(" (param "))
return false; return false;
const AstName& name = (*maybeLocals)[i]; const AstName& name = (*maybeLocals)[i];
if (!name.empty()) { if (!RenderNonemptyName(c, name))
if (!RenderName(c, name)) return false;
return false;
if (!c.buffer.append(" "))
return false;
}
ValType arg = sig.args()[i]; ValType arg = sig.args()[i];
if (!RenderValType(c, arg)) if (!RenderValType(c, arg))
return false; return false;
@ -1518,34 +1521,76 @@ RenderSignature(WasmRenderContext& c, const AstSig& sig, const AstNameVector* ma
} }
static bool static bool
RenderTypeSection(WasmRenderContext& c, const AstModule::SigVector& sigs) RenderFields(WasmRenderContext& c, const AstStruct& str)
{ {
uint32_t numSigs = sigs.length(); const AstNameVector& fieldNames = str.fieldNames();
if (!numSigs) const AstValTypeVector& fieldTypes = str.fieldTypes();
return true;
for (uint32_t sigIndex = 0; sigIndex < numSigs; sigIndex++) { for (uint32_t fieldIndex = 0; fieldIndex < fieldTypes.length(); fieldIndex++) {
const AstSig* sig = sigs[sigIndex]; if (!c.buffer.append("\n"))
return false;
if (!RenderIndent(c)) if (!RenderIndent(c))
return false; return false;
if (!c.buffer.append("(type")) if (!c.buffer.append("(field "))
return false; return false;
if (!sig->name().empty()) { if (!RenderNonemptyName(c, fieldNames[fieldIndex]))
if (!c.buffer.append(" "))
return false;
if (!RenderName(c, sig->name()))
return false;
}
if (!c.buffer.append(" (func"))
return false; return false;
if (!RenderSignature(c, *sig)) if (!RenderValType(c, fieldTypes[fieldIndex]))
return false; return false;
if (!c.buffer.append("))\n")) if (!c.buffer.append(')'))
return false; return false;
} }
return true; return true;
} }
template<size_t ArrayLength>
static bool
RenderTypeStart(WasmRenderContext& c, const AstName& name, const char (&keyword)[ArrayLength])
{
if (!RenderIndent(c))
return false;
if (!c.buffer.append("(type "))
return false;
if (!RenderNonemptyName(c, name))
return false;
if (!c.buffer.append("("))
return false;
return c.buffer.append(keyword);
}
static bool
RenderTypeEnd(WasmRenderContext& c)
{
return c.buffer.append("))\n");
}
static bool
RenderTypeSection(WasmRenderContext& c, const AstModule::TypeDefVector& types)
{
for (uint32_t typeIndex = 0; typeIndex < types.length(); typeIndex++) {
const AstTypeDef* type = types[typeIndex];
if (type->isSig()) {
const AstSig* sig = &type->asSig();
if (!RenderTypeStart(c, sig->name(), "func"))
return false;
if (!RenderSignature(c, *sig))
return false;
} else {
const AstStruct* strukt = &type->asStruct();
if (!RenderTypeStart(c, strukt->name(), "struct"))
return false;
c.indent++;
if (!RenderFields(c, *strukt))
return false;
c.indent--;
}
if (!RenderTypeEnd(c))
return false;
}
return true;
}
static bool static bool
RenderLimits(WasmRenderContext& c, const Limits& limits) RenderLimits(WasmRenderContext& c, const Limits& limits)
{ {
@ -1755,7 +1800,7 @@ RenderImport(WasmRenderContext& c, AstImport& import, const AstModule& module)
case DefinitionKind::Function: { case DefinitionKind::Function: {
if (!c.buffer.append("(func")) if (!c.buffer.append("(func"))
return false; return false;
const AstSig* sig = module.sigs()[import.funcSig().index()]; const AstSig* sig = &module.types()[import.funcSig().index()]->asSig();
if (!RenderSignature(c, *sig)) if (!RenderSignature(c, *sig))
return false; return false;
if (!c.buffer.append(")")) if (!c.buffer.append(")"))
@ -1858,9 +1903,9 @@ RenderExportSection(WasmRenderContext& c, const AstModule::ExportVector& exports
} }
static bool static bool
RenderFunctionBody(WasmRenderContext& c, AstFunc& func, const AstModule::SigVector& sigs) RenderFunctionBody(WasmRenderContext& c, AstFunc& func, const AstModule::TypeDefVector& types)
{ {
const AstSig* sig = sigs[func.sig().index()]; const AstSig* sig = &types[func.sig().index()]->asSig();
uint32_t argsNum = sig->args().length(); uint32_t argsNum = sig->args().length();
uint32_t localsNum = func.vars().length(); uint32_t localsNum = func.vars().length();
@ -1904,13 +1949,13 @@ RenderFunctionBody(WasmRenderContext& c, AstFunc& func, const AstModule::SigVect
static bool static bool
RenderCodeSection(WasmRenderContext& c, const AstModule::FuncVector& funcs, RenderCodeSection(WasmRenderContext& c, const AstModule::FuncVector& funcs,
const AstModule::SigVector& sigs) const AstModule::TypeDefVector& types)
{ {
uint32_t numFuncBodies = funcs.length(); uint32_t numFuncBodies = funcs.length();
for (uint32_t funcIndex = 0; funcIndex < numFuncBodies; funcIndex++) { for (uint32_t funcIndex = 0; funcIndex < numFuncBodies; funcIndex++) {
AstFunc* func = funcs[funcIndex]; AstFunc* func = funcs[funcIndex];
uint32_t sigIndex = func->sig().index(); uint32_t sigIndex = func->sig().index();
AstSig* sig = sigs[sigIndex]; AstSig* sig = &types[sigIndex]->asSig();
if (!RenderIndent(c)) if (!RenderIndent(c))
return false; return false;
@ -1929,7 +1974,7 @@ RenderCodeSection(WasmRenderContext& c, const AstModule::FuncVector& funcs,
c.currentFuncIndex = funcIndex; c.currentFuncIndex = funcIndex;
c.indent++; c.indent++;
if (!RenderFunctionBody(c, *func, sigs)) if (!RenderFunctionBody(c, *func, types))
return false; return false;
c.indent--; c.indent--;
if (!RenderIndent(c)) if (!RenderIndent(c))
@ -2026,7 +2071,7 @@ RenderModule(WasmRenderContext& c, AstModule& module)
c.indent++; c.indent++;
if (!RenderTypeSection(c, module.sigs())) if (!RenderTypeSection(c, module.types()))
return false; return false;
if (!RenderImportSection(c, module)) if (!RenderImportSection(c, module))
@ -2050,7 +2095,7 @@ RenderModule(WasmRenderContext& c, AstModule& module)
if (!RenderElemSection(c, module)) if (!RenderElemSection(c, module))
return false; return false;
if (!RenderCodeSection(c, module.funcs(), module.sigs())) if (!RenderCodeSection(c, module.funcs(), module.types()))
return false; return false;
if (!RenderDataSection(c, module)) if (!RenderDataSection(c, module))

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

@ -243,7 +243,11 @@ ModuleGenerator::init(Metadata* maybeAsmJSMetadata)
} }
if (!isAsmJS()) { if (!isAsmJS()) {
for (SigWithId& sig : env_->sigs) { for (TypeDef& td : env_->types) {
if (!td.isFuncType())
continue;
SigWithId& sig = td.funcType();
if (SigIdDesc::isGlobal(sig)) { if (SigIdDesc::isGlobal(sig)) {
uint32_t globalDataOffset; uint32_t globalDataOffset;
if (!allocateGlobalBytes(sizeof(void*), sizeof(void*), &globalDataOffset)) if (!allocateGlobalBytes(sizeof(void*), sizeof(void*), &globalDataOffset))
@ -979,6 +983,12 @@ ModuleGenerator::finishModule(const ShareableBytes& bytecode)
if (!code || !code->initialize(bytecode, *linkDataTier_)) if (!code || !code->initialize(bytecode, *linkDataTier_))
return nullptr; return nullptr;
StructTypeVector structTypes;
for (TypeDef& td : env_->types) {
if (td.isStructType() && !structTypes.append(std::move(td.structType())))
return nullptr;
}
SharedModule module(js_new<Module>(std::move(assumptions_), SharedModule module(js_new<Module>(std::move(assumptions_),
*code, *code,
std::move(maybeDebuggingBytes), std::move(maybeDebuggingBytes),
@ -987,6 +997,7 @@ ModuleGenerator::finishModule(const ShareableBytes& bytecode)
std::move(env_->exports), std::move(env_->exports),
std::move(env_->dataSegments), std::move(env_->dataSegments),
std::move(env_->elemSegments), std::move(env_->elemSegments),
std::move(structTypes),
bytecode)); bytecode));
if (!module) if (!module)
return nullptr; return nullptr;

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

@ -1212,7 +1212,7 @@ class FunctionCompiler
return true; return true;
} }
const SigWithId& sig = env_.sigs[sigIndex]; const SigWithId& sig = env_.types[sigIndex].funcType();
CalleeDesc callee; CalleeDesc callee;
if (env_.isAsmJS()) { if (env_.isAsmJS()) {
@ -2235,7 +2235,7 @@ EmitCallIndirect(FunctionCompiler& f, bool oldStyle)
if (f.inDeadCode()) if (f.inDeadCode())
return true; return true;
const Sig& sig = f.env().sigs[sigIndex]; const Sig& sig = f.env().types[sigIndex].funcType();
CallCompileState call(f, lineOrBytecode); CallCompileState call(f, lineOrBytecode);
if (!EmitCallArgs(f, sig, args, &call)) if (!EmitCallArgs(f, sig, args, &call))

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

@ -398,6 +398,7 @@ Module::compiledSerializedSize() const
SerializedVectorSize(exports_) + SerializedVectorSize(exports_) +
SerializedPodVectorSize(dataSegments_) + SerializedPodVectorSize(dataSegments_) +
SerializedVectorSize(elemSegments_) + SerializedVectorSize(elemSegments_) +
SerializedVectorSize(structTypes_) +
code_->serializedSize(); code_->serializedSize();
} }
@ -423,6 +424,7 @@ Module::compiledSerialize(uint8_t* compiledBegin, size_t compiledSize) const
cursor = SerializeVector(cursor, exports_); cursor = SerializeVector(cursor, exports_);
cursor = SerializePodVector(cursor, dataSegments_); cursor = SerializePodVector(cursor, dataSegments_);
cursor = SerializeVector(cursor, elemSegments_); cursor = SerializeVector(cursor, elemSegments_);
cursor = SerializeVector(cursor, structTypes_);
cursor = code_->serialize(cursor, linkData_); cursor = code_->serialize(cursor, linkData_);
MOZ_RELEASE_ASSERT(cursor == compiledBegin + compiledSize); MOZ_RELEASE_ASSERT(cursor == compiledBegin + compiledSize);
} }
@ -486,6 +488,11 @@ Module::deserialize(const uint8_t* bytecodeBegin, size_t bytecodeSize,
if (!cursor) if (!cursor)
return nullptr; return nullptr;
StructTypeVector structTypes;
cursor = DeserializeVector(cursor, &structTypes);
if (!cursor)
return nullptr;
SharedCode code; SharedCode code;
cursor = Code::deserialize(cursor, *bytecode, linkData, *metadata, &code); cursor = Code::deserialize(cursor, *bytecode, linkData, *metadata, &code);
if (!cursor) if (!cursor)
@ -502,6 +509,7 @@ Module::deserialize(const uint8_t* bytecodeBegin, size_t bytecodeSize,
std::move(exports), std::move(exports),
std::move(dataSegments), std::move(dataSegments),
std::move(elemSegments), std::move(elemSegments),
std::move(structTypes),
*bytecode); *bytecode);
} }
@ -627,6 +635,7 @@ Module::addSizeOfMisc(MallocSizeOf mallocSizeOf,
SizeOfVectorExcludingThis(exports_, mallocSizeOf) + SizeOfVectorExcludingThis(exports_, mallocSizeOf) +
dataSegments_.sizeOfExcludingThis(mallocSizeOf) + dataSegments_.sizeOfExcludingThis(mallocSizeOf) +
SizeOfVectorExcludingThis(elemSegments_, mallocSizeOf) + SizeOfVectorExcludingThis(elemSegments_, mallocSizeOf) +
SizeOfVectorExcludingThis(structTypes_, mallocSizeOf) +
bytecode_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenBytes); bytecode_->sizeOfIncludingThisIfNotSeen(mallocSizeOf, seenBytes);
if (unlinkedCodeForDebugging_) if (unlinkedCodeForDebugging_)
*data += unlinkedCodeForDebugging_->sizeOfExcludingThis(mallocSizeOf); *data += unlinkedCodeForDebugging_->sizeOfExcludingThis(mallocSizeOf);

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

@ -135,6 +135,7 @@ class Module : public JS::WasmModule
const ExportVector exports_; const ExportVector exports_;
const DataSegmentVector dataSegments_; const DataSegmentVector dataSegments_;
const ElemSegmentVector elemSegments_; const ElemSegmentVector elemSegments_;
const StructTypeVector structTypes_;
const SharedBytes bytecode_; const SharedBytes bytecode_;
ExclusiveTiering tiering_; ExclusiveTiering tiering_;
@ -170,6 +171,7 @@ class Module : public JS::WasmModule
ExportVector&& exports, ExportVector&& exports,
DataSegmentVector&& dataSegments, DataSegmentVector&& dataSegments,
ElemSegmentVector&& elemSegments, ElemSegmentVector&& elemSegments,
StructTypeVector&& structTypes,
const ShareableBytes& bytecode) const ShareableBytes& bytecode)
: assumptions_(std::move(assumptions)), : assumptions_(std::move(assumptions)),
code_(&code), code_(&code),
@ -179,6 +181,7 @@ class Module : public JS::WasmModule
exports_(std::move(exports)), exports_(std::move(exports)),
dataSegments_(std::move(dataSegments)), dataSegments_(std::move(dataSegments)),
elemSegments_(std::move(elemSegments)), elemSegments_(std::move(elemSegments)),
structTypes_(std::move(structTypes)),
bytecode_(&bytecode), bytecode_(&bytecode),
tiering_(mutexid::WasmModuleTieringLock), tiering_(mutexid::WasmModuleTieringLock),
codeIsBusy_(false) codeIsBusy_(false)

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

@ -1732,7 +1732,7 @@ OpIter<Policy>::readCallIndirect(uint32_t* sigIndex, Value* callee, ValueVector*
if (!readVarU32(sigIndex)) if (!readVarU32(sigIndex))
return fail("unable to read call_indirect signature index"); return fail("unable to read call_indirect signature index");
if (*sigIndex >= env_.numSigs()) if (*sigIndex >= env_.numTypes())
return fail("signature index out of range"); return fail("signature index out of range");
uint8_t flags; uint8_t flags;
@ -1745,7 +1745,10 @@ OpIter<Policy>::readCallIndirect(uint32_t* sigIndex, Value* callee, ValueVector*
if (!popWithType(ValType::I32, callee)) if (!popWithType(ValType::I32, callee))
return false; return false;
const Sig& sig = env_.sigs[*sigIndex]; if (!env_.types[*sigIndex].isFuncType())
return fail("expected signature type");
const Sig& sig = env_.types[*sigIndex].funcType();
if (!popCallArgs(sig.args(), argValues)) if (!popCallArgs(sig.args(), argValues))
return false; return false;
@ -1789,10 +1792,13 @@ OpIter<Policy>::readOldCallIndirect(uint32_t* sigIndex, Value* callee, ValueVect
if (!readVarU32(sigIndex)) if (!readVarU32(sigIndex))
return fail("unable to read call_indirect signature index"); return fail("unable to read call_indirect signature index");
if (*sigIndex >= env_.numSigs()) if (*sigIndex >= env_.numTypes())
return fail("signature index out of range"); return fail("signature index out of range");
const Sig& sig = env_.sigs[*sigIndex]; if (!env_.types[*sigIndex].isFuncType())
return fail("expected signature type");
const Sig& sig = env_.types[*sigIndex].funcType();
if (!popCallArgs(sig.args(), argValues)) if (!popCallArgs(sig.args(), argValues))
return false; return false;

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

@ -94,6 +94,7 @@ class WasmToken
#ifdef ENABLE_WASM_SATURATING_TRUNC_OPS #ifdef ENABLE_WASM_SATURATING_TRUNC_OPS
ExtraConversionOpcode, ExtraConversionOpcode,
#endif #endif
Field,
Float, Float,
Func, Func,
GetGlobal, GetGlobal,
@ -127,6 +128,7 @@ class WasmToken
Shared, Shared,
SignedInteger, SignedInteger,
Start, Start,
Struct,
Store, Store,
Table, Table,
TeeLocal, TeeLocal,
@ -364,6 +366,7 @@ class WasmToken
case End: case End:
case Error: case Error:
case Export: case Export:
case Field:
case Float: case Float:
case Func: case Func:
case Global: case Global:
@ -382,6 +385,7 @@ class WasmToken
case Shared: case Shared:
case SignedInteger: case SignedInteger:
case Start: case Start:
case Struct:
case Table: case Table:
case Text: case Text:
case Then: case Then:
@ -951,6 +955,9 @@ WasmTokenStream::next()
break; break;
case 'f': case 'f':
if (consume(u"field"))
return WasmToken(WasmToken::Field, begin, cur_);
if (consume(u"func")) if (consume(u"func"))
return WasmToken(WasmToken::Func, begin, cur_); return WasmToken(WasmToken::Func, begin, cur_);
@ -1708,6 +1715,8 @@ WasmTokenStream::next()
#endif #endif
if (consume(u"start")) if (consume(u"start"))
return WasmToken(WasmToken::Start, begin, cur_); return WasmToken(WasmToken::Start, begin, cur_);
if (consume(u"struct"))
return WasmToken(WasmToken::Struct, begin, cur_);
break; break;
case 't': case 't':
@ -3331,24 +3340,71 @@ ParseFunc(WasmParseContext& c, AstModule* module)
return func && module->append(func); return func && module->append(func);
} }
static AstSig* static bool
ParseGlobalType(WasmParseContext& c, WasmToken* typeToken, bool* isMutable);
static bool
ParseStructFields(WasmParseContext& c, AstStruct* str)
{
AstNameVector names(c.lifo);
AstValTypeVector types(c.lifo);
while (true) {
if (!c.ts.getIf(WasmToken::OpenParen))
break;
if (!c.ts.match(WasmToken::Field, c.error))
return false;
AstName name = c.ts.getIfName();
WasmToken typeToken;
bool isMutable;
if (!ParseGlobalType(c, &typeToken, &isMutable))
return false;
if (!c.ts.match(WasmToken::CloseParen, c.error))
return false;
if (!names.append(name))
return false;
if (!types.append(typeToken.valueType()))
return false;
}
*str = AstStruct(std::move(names), std::move(types));
return true;
}
static AstTypeDef*
ParseTypeDef(WasmParseContext& c) ParseTypeDef(WasmParseContext& c)
{ {
AstName name = c.ts.getIfName(); AstName name = c.ts.getIfName();
if (!c.ts.match(WasmToken::OpenParen, c.error)) if (!c.ts.match(WasmToken::OpenParen, c.error))
return nullptr; return nullptr;
if (!c.ts.match(WasmToken::Func, c.error))
return nullptr;
AstSig sig(c.lifo); AstTypeDef* type = nullptr;
if (!ParseFuncSig(c, &sig)) if (c.ts.getIf(WasmToken::Func)) {
AstSig sig(c.lifo);
if (!ParseFuncSig(c, &sig))
return nullptr;
type = new(c.lifo) AstSig(name, std::move(sig));
} else if (c.ts.getIf(WasmToken::Struct)) {
AstStruct str(c.lifo);
if (!ParseStructFields(c, &str))
return nullptr;
type = new(c.lifo) AstStruct(name, std::move(str));
} else {
c.ts.generateError(c.ts.peek(), "bad type definition", c.error);
return nullptr; return nullptr;
}
if (!c.ts.match(WasmToken::CloseParen, c.error)) if (!c.ts.match(WasmToken::CloseParen, c.error))
return nullptr; return nullptr;
return new(c.lifo) AstSig(name, std::move(sig)); return type;
} }
static bool static bool
@ -3922,8 +3978,10 @@ ParseModule(const char16_t* text, uintptr_t stackLimit, LifoAlloc& lifo, UniqueC
switch (section.kind()) { switch (section.kind()) {
case WasmToken::Type: { case WasmToken::Type: {
AstSig* sig = ParseTypeDef(c); AstTypeDef* typeDef = ParseTypeDef(c);
if (!sig || !module->append(sig)) if (!typeDef)
return nullptr;
if (!module->append(static_cast<AstSig*>(typeDef)))
return nullptr; return nullptr;
break; break;
} }
@ -4008,6 +4066,7 @@ class Resolver
AstNameMap importMap_; AstNameMap importMap_;
AstNameMap tableMap_; AstNameMap tableMap_;
AstNameMap memoryMap_; AstNameMap memoryMap_;
AstNameMap typeMap_;
AstNameVector targetStack_; AstNameVector targetStack_;
bool registerName(AstNameMap& map, AstName name, size_t index) { bool registerName(AstNameMap& map, AstName name, size_t index) {
@ -4045,6 +4104,7 @@ class Resolver
importMap_(lifo), importMap_(lifo),
tableMap_(lifo), tableMap_(lifo),
memoryMap_(lifo), memoryMap_(lifo),
typeMap_(lifo),
targetStack_(lifo) targetStack_(lifo)
{} {}
bool init() { bool init() {
@ -4053,6 +4113,7 @@ class Resolver
importMap_.init() && importMap_.init() &&
tableMap_.init() && tableMap_.init() &&
memoryMap_.init() && memoryMap_.init() &&
typeMap_.init() &&
varMap_.init() && varMap_.init() &&
globalMap_.init(); globalMap_.init();
} }
@ -4072,6 +4133,7 @@ class Resolver
REGISTER(Global, globalMap_) REGISTER(Global, globalMap_)
REGISTER(Table, tableMap_) REGISTER(Table, tableMap_)
REGISTER(Memory, memoryMap_) REGISTER(Memory, memoryMap_)
REGISTER(Type, typeMap_)
#undef REGISTER #undef REGISTER
@ -4097,6 +4159,7 @@ class Resolver
RESOLVE(globalMap_, Global) RESOLVE(globalMap_, Global)
RESOLVE(tableMap_, Table) RESOLVE(tableMap_, Table)
RESOLVE(memoryMap_, Memory) RESOLVE(memoryMap_, Memory)
RESOLVE(typeMap_, Type)
#undef RESOLVE #undef RESOLVE
@ -4538,11 +4601,18 @@ ResolveModule(LifoAlloc& lifo, AstModule* module, UniqueChars* error)
if (!r.init()) if (!r.init())
return false; return false;
size_t numSigs = module->sigs().length(); size_t numTypes = module->types().length();
for (size_t i = 0; i < numSigs; i++) { for (size_t i = 0; i < numTypes; i++) {
AstSig* sig = module->sigs()[i]; AstTypeDef* ty = module->types()[i];
if (!r.registerSigName(sig->name(), i)) if (ty->isSig()) {
return r.fail("duplicate signature"); AstSig* sig = static_cast<AstSig*>(ty);
if (!r.registerSigName(sig->name(), i))
return r.fail("duplicate signature");
} else if (ty->isStruct()) {
AstStruct* str = static_cast<AstStruct*>(ty);
if (!r.registerTypeName(str->name(), i))
return r.fail("duplicate struct");
}
} }
size_t lastFuncIndex = 0; size_t lastFuncIndex = 0;
@ -5169,34 +5239,51 @@ EncodeExpr(Encoder& e, AstExpr& expr)
static bool static bool
EncodeTypeSection(Encoder& e, AstModule& module) EncodeTypeSection(Encoder& e, AstModule& module)
{ {
if (module.sigs().empty()) if (module.types().empty())
return true; return true;
size_t offset; size_t offset;
if (!e.startSection(SectionId::Type, &offset)) if (!e.startSection(SectionId::Type, &offset))
return false; return false;
if (!e.writeVarU32(module.sigs().length())) if (!e.writeVarU32(module.types().length()))
return false; return false;
for (AstSig* sig : module.sigs()) { for (AstTypeDef* ty : module.types()) {
if (!e.writeVarU32(uint32_t(TypeCode::Func))) if (ty->isSig()) {
return false; AstSig* sig = static_cast<AstSig*>(ty);
if (!e.writeVarU32(uint32_t(TypeCode::Func)))
if (!e.writeVarU32(sig->args().length()))
return false;
for (ValType t : sig->args()) {
if (!e.writeValType(t))
return false; return false;
}
if (!e.writeVarU32(!IsVoid(sig->ret()))) if (!e.writeVarU32(sig->args().length()))
return false;
if (!IsVoid(sig->ret())) {
if (!e.writeValType(NonVoidToValType(sig->ret())))
return false; return false;
for (ValType t : sig->args()) {
if (!e.writeValType(t))
return false;
}
if (!e.writeVarU32(!IsVoid(sig->ret())))
return false;
if (!IsVoid(sig->ret())) {
if (!e.writeValType(NonVoidToValType(sig->ret())))
return false;
}
} else if (ty->isStruct()) {
AstStruct* str = static_cast<AstStruct*>(ty);
if (!e.writeVarU32(uint32_t(TypeCode::Struct)))
return false;
if (!e.writeVarU32(str->fieldTypes().length()))
return false;
for (ValType t : str->fieldTypes()) {
if (!e.writeValType(t))
return false;
}
} else {
MOZ_CRASH();
} }
} }

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

@ -327,6 +327,36 @@ SigWithId::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
return Sig::sizeOfExcludingThis(mallocSizeOf); return Sig::sizeOfExcludingThis(mallocSizeOf);
} }
size_t
StructType::serializedSize() const
{
return SerializedPodVectorSize(fields_) +
SerializedPodVectorSize(fieldOffsets_);
}
uint8_t*
StructType::serialize(uint8_t* cursor) const
{
cursor = SerializePodVector(cursor, fields_);
cursor = SerializePodVector(cursor, fieldOffsets_);
return cursor;
}
const uint8_t*
StructType::deserialize(const uint8_t* cursor)
{
(cursor = DeserializePodVector(cursor, &fields_));
(cursor = DeserializePodVector(cursor, &fieldOffsets_));
return cursor;
}
size_t
StructType::sizeOfExcludingThis(MallocSizeOf mallocSizeOf) const
{
return fields_.sizeOfExcludingThis(mallocSizeOf) +
fieldOffsets_.sizeOfExcludingThis(mallocSizeOf);
}
size_t size_t
Import::serializedSize() const Import::serializedSize() const
{ {

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

@ -24,7 +24,6 @@
#include "mozilla/EnumeratedArray.h" #include "mozilla/EnumeratedArray.h"
#include "mozilla/HashFunctions.h" #include "mozilla/HashFunctions.h"
#include "mozilla/Maybe.h" #include "mozilla/Maybe.h"
#include "mozilla/Move.h"
#include "mozilla/RefPtr.h" #include "mozilla/RefPtr.h"
#include "mozilla/Unused.h" #include "mozilla/Unused.h"
@ -753,6 +752,31 @@ struct SigHashPolicy
static bool match(const Sig* lhs, Lookup rhs) { return *lhs == rhs; } static bool match(const Sig* lhs, Lookup rhs) { return *lhs == rhs; }
}; };
// Structure type.
//
// The Module owns a dense array of Struct values that represent the structure
// types that the module knows about. It is created from the sparse array of
// types in the ModuleEnvironment when the Module is created.
class StructType
{
public:
ValTypeVector fields_; // Scalar types of fields
Uint32Vector fieldOffsets_; // Byte offsets into an object for corresponding field
public:
StructType() : fields_(), fieldOffsets_() {}
StructType(ValTypeVector&& fields, Uint32Vector&& fieldOffsets)
: fields_(std::move(fields)),
fieldOffsets_(std::move(fieldOffsets))
{}
WASM_DECLARE_SERIALIZABLE(StructType)
};
typedef Vector<StructType, 0, SystemAllocPolicy> StructTypeVector;
// An InitExpr describes a deferred initializer expression, used to initialize // An InitExpr describes a deferred initializer expression, used to initialize
// a global or a table element offset. Such expressions are created during // a global or a table element offset. Such expressions are created during
// decoding and actually executed on module instantiation. // decoding and actually executed on module instantiation.
@ -1113,6 +1137,97 @@ struct SigWithId : Sig
typedef Vector<SigWithId, 0, SystemAllocPolicy> SigWithIdVector; typedef Vector<SigWithId, 0, SystemAllocPolicy> SigWithIdVector;
typedef Vector<const SigWithId*, 0, SystemAllocPolicy> SigWithIdPtrVector; typedef Vector<const SigWithId*, 0, SystemAllocPolicy> SigWithIdPtrVector;
// A tagged container for the various types that can be present in a wasm
// module's type section.
class TypeDef
{
enum { IsFuncType, IsStructType, IsNone } tag_;
union {
SigWithId funcType_;
StructType structType_;
};
public:
TypeDef() : tag_(IsNone), structType_(StructType()) {}
explicit TypeDef(Sig&& sig) : tag_(IsFuncType), funcType_(SigWithId(std::move(sig))) {}
explicit TypeDef(StructType&& structType) : tag_(IsStructType), structType_(std::move(structType)) {}
TypeDef(TypeDef&& td) : tag_(td.tag_), structType_(StructType()) {
switch (tag_) {
case IsFuncType: funcType_ = std::move(td.funcType_); break;
case IsStructType: structType_ = std::move(td.structType_); break;
case IsNone: break;
}
}
~TypeDef() {
switch (tag_) {
case IsFuncType: funcType_.~SigWithId(); break;
case IsStructType: structType_.~StructType(); break;
case IsNone: break;
}
}
TypeDef& operator=(TypeDef&& that) {
tag_ = that.tag_;
switch (tag_) {
case IsFuncType: funcType_ = std::move(that.funcType_); break;
case IsStructType: structType_ = std::move(that.structType_); break;
case IsNone: break;
}
return *this;
}
bool isFuncType() const {
return tag_ == IsFuncType;
}
bool isStructType() const {
return tag_ == IsStructType;
}
const SigWithId& funcType() const {
MOZ_ASSERT(isFuncType());
return funcType_;
}
SigWithId& funcType() {
MOZ_ASSERT(isFuncType());
return funcType_;
}
// p has to point to the sig_ embedded within a TypeDef for this to be
// valid.
static const TypeDef* fromSigWithIdPtr(const SigWithId* p) {
const TypeDef* q = (const TypeDef*)((char*)p - offsetof(TypeDef, funcType_));
MOZ_ASSERT(q->tag_ == IsFuncType);
return q;
}
const StructType& structType() const {
MOZ_ASSERT(isStructType());
return structType_;
}
StructType& structType() {
MOZ_ASSERT(isStructType());
return structType_;
}
// p has to point to the struct_ embedded within a TypeDef for this to be
// valid.
static const TypeDef* fromStructPtr(const StructType* p) {
const TypeDef* q = (const TypeDef*)((char*)p - offsetof(TypeDef, structType_));
MOZ_ASSERT(q->tag_ == IsStructType);
return q;
}
};
typedef Vector<TypeDef, 0, SystemAllocPolicy> TypeDefVector;
// A wasm::Trap represents a wasm-defined trap that can occur during execution // A wasm::Trap represents a wasm-defined trap that can occur during execution
// which triggers a WebAssembly.RuntimeError. Generated code may jump to a Trap // which triggers a WebAssembly.RuntimeError. Generated code may jump to a Trap
// symbolically, passing the bytecode offset to report as the trap offset. The // symbolically, passing the bytecode offset to report as the trap offset. The

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

@ -1063,6 +1063,78 @@ DecodePreamble(Decoder& d)
return true; return true;
} }
static bool
DecodeFuncType(Decoder& d, ModuleEnvironment* env, uint32_t typeIndex)
{
uint32_t numArgs;
if (!d.readVarU32(&numArgs))
return d.fail("bad number of function args");
if (numArgs > MaxParams)
return d.fail("too many arguments in signature");
ValTypeVector args;
if (!args.resize(numArgs))
return false;
for (uint32_t i = 0; i < numArgs; i++) {
if (!DecodeValType(d, ModuleKind::Wasm, env->gcTypesEnabled, &args[i]))
return false;
}
uint32_t numRets;
if (!d.readVarU32(&numRets))
return d.fail("bad number of function returns");
if (numRets > 1)
return d.fail("too many returns in signature");
ExprType result = ExprType::Void;
if (numRets == 1) {
ValType type;
if (!DecodeValType(d, ModuleKind::Wasm, env->gcTypesEnabled, &type))
return false;
result = ToExprType(type);
}
env->types[typeIndex] = TypeDef(Sig(std::move(args), result));
return true;
}
static bool
DecodeStructType(Decoder& d, ModuleEnvironment* env, uint32_t typeIndex)
{
if (env->gcTypesEnabled == HasGcTypes::False)
return d.fail("Structure types not enabled");
uint32_t numFields;
if (!d.readVarU32(&numFields))
return d.fail("Bad number of fields");
if (numFields > MaxStructFields)
return d.fail("too many fields in structure");
ValTypeVector fields;
if (!fields.resize(numFields))
return false;
Uint32Vector fieldOffsets;
if (!fieldOffsets.resize(numFields))
return false;
// TODO (subsequent patch): lay out the fields.
for (uint32_t i = 0; i < numFields; i++) {
if (!DecodeValType(d, ModuleKind::Wasm, env->gcTypesEnabled, &fields[i]))
return false;
}
env->types[typeIndex] = TypeDef(StructType(std::move(fields), std::move(fieldOffsets)));
return true;
}
static bool static bool
DecodeTypeSection(Decoder& d, ModuleEnvironment* env) DecodeTypeSection(Decoder& d, ModuleEnvironment* env)
{ {
@ -1072,55 +1144,33 @@ DecodeTypeSection(Decoder& d, ModuleEnvironment* env)
if (!range) if (!range)
return true; return true;
uint32_t numSigs; uint32_t numTypes;
if (!d.readVarU32(&numSigs)) if (!d.readVarU32(&numTypes))
return d.fail("expected number of signatures"); return d.fail("expected number of types");
if (numSigs > MaxTypes) if (numTypes > MaxTypes)
return d.fail("too many signatures"); return d.fail("too many types");
if (!env->sigs.resize(numSigs)) if (!env->types.resize(numTypes))
return false; return false;
for (uint32_t sigIndex = 0; sigIndex < numSigs; sigIndex++) { for (uint32_t typeIndex = 0; typeIndex < numTypes; typeIndex++) {
uint8_t form; uint8_t form;
if (!d.readFixedU8(&form) || form != uint8_t(TypeCode::Func)) if (!d.readFixedU8(&form))
return d.fail("expected function form"); return d.fail("expected type form");
uint32_t numArgs; switch (form) {
if (!d.readVarU32(&numArgs)) case uint8_t(TypeCode::Func):
return d.fail("bad number of function args"); if (!DecodeFuncType(d, env, typeIndex))
if (numArgs > MaxParams)
return d.fail("too many arguments in signature");
ValTypeVector args;
if (!args.resize(numArgs))
return false;
for (uint32_t i = 0; i < numArgs; i++) {
if (!DecodeValType(d, ModuleKind::Wasm, env->gcTypesEnabled, &args[i]))
return false; return false;
} break;
case uint8_t(TypeCode::Struct):
uint32_t numRets; if (!DecodeStructType(d, env, typeIndex))
if (!d.readVarU32(&numRets))
return d.fail("bad number of function returns");
if (numRets > 1)
return d.fail("too many returns in signature");
ExprType result = ExprType::Void;
if (numRets == 1) {
ValType type;
if (!DecodeValType(d, ModuleKind::Wasm, env->gcTypesEnabled, &type))
return false; return false;
break;
result = ToExprType(type); default:
return d.fail("expected type form");
} }
env->sigs[sigIndex] = Sig(std::move(args), result);
} }
return d.finishSection(*range, "type"); return d.finishSection(*range, "type");
@ -1154,14 +1204,17 @@ DecodeName(Decoder& d)
} }
static bool static bool
DecodeSignatureIndex(Decoder& d, const SigWithIdVector& sigs, uint32_t* sigIndex) DecodeSignatureIndex(Decoder& d, const TypeDefVector& types, uint32_t* sigIndex)
{ {
if (!d.readVarU32(sigIndex)) if (!d.readVarU32(sigIndex))
return d.fail("expected signature index"); return d.fail("expected signature index");
if (*sigIndex >= sigs.length()) if (*sigIndex >= types.length())
return d.fail("signature index out of range"); return d.fail("signature index out of range");
if (!types[*sigIndex].isFuncType())
return d.fail("signature index references non-signature");
return true; return true;
} }
@ -1335,9 +1388,9 @@ DecodeImport(Decoder& d, ModuleEnvironment* env)
switch (importKind) { switch (importKind) {
case DefinitionKind::Function: { case DefinitionKind::Function: {
uint32_t sigIndex; uint32_t sigIndex;
if (!DecodeSignatureIndex(d, env->sigs, &sigIndex)) if (!DecodeSignatureIndex(d, env->types, &sigIndex))
return false; return false;
if (!env->funcSigs.append(&env->sigs[sigIndex])) if (!env->funcSigs.append(&env->types[sigIndex].funcType()))
return false; return false;
if (env->funcSigs.length() > MaxFuncs) if (env->funcSigs.length() > MaxFuncs)
return d.fail("too many functions"); return d.fail("too many functions");
@ -1428,9 +1481,9 @@ DecodeFunctionSection(Decoder& d, ModuleEnvironment* env)
for (uint32_t i = 0; i < numDefs; i++) { for (uint32_t i = 0; i < numDefs; i++) {
uint32_t sigIndex; uint32_t sigIndex;
if (!DecodeSignatureIndex(d, env->sigs, &sigIndex)) if (!DecodeSignatureIndex(d, env->types, &sigIndex))
return false; return false;
env->funcSigs.infallibleAppend(&env->sigs[sigIndex]); env->funcSigs.infallibleAppend(&env->types[sigIndex].funcType());
} }
return d.finishSection(*range, "function"); return d.finishSection(*range, "function");

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

@ -68,7 +68,7 @@ struct ModuleEnvironment
MemoryUsage memoryUsage; MemoryUsage memoryUsage;
uint32_t minMemoryLength; uint32_t minMemoryLength;
Maybe<uint32_t> maxMemoryLength; Maybe<uint32_t> maxMemoryLength;
SigWithIdVector sigs; TypeDefVector types;
SigWithIdPtrVector funcSigs; SigWithIdPtrVector funcSigs;
Uint32Vector funcImportGlobalDataOffsets; Uint32Vector funcImportGlobalDataOffsets;
GlobalDescVector globals; GlobalDescVector globals;
@ -105,8 +105,8 @@ struct ModuleEnvironment
size_t numTables() const { size_t numTables() const {
return tables.length(); return tables.length();
} }
size_t numSigs() const { size_t numTypes() const {
return sigs.length(); return types.length();
} }
size_t numFuncs() const { size_t numFuncs() const {
return funcSigs.length(); return funcSigs.length();
@ -133,7 +133,7 @@ struct ModuleEnvironment
return funcIndex < funcImportGlobalDataOffsets.length(); return funcIndex < funcImportGlobalDataOffsets.length();
} }
uint32_t funcIndexToSigIndex(uint32_t funcIndex) const { uint32_t funcIndexToSigIndex(uint32_t funcIndex) const {
return funcSigs[funcIndex] - sigs.begin(); return TypeDef::fromSigWithIdPtr(funcSigs[funcIndex]) - types.begin();
} }
}; };