Bug 1286517: Implement text-to-binary for Globals; r=luke

MozReview-Commit-ID: ajyuriWJK5

--HG--
extra : rebase_source : e9c33a0e729ebc3dda24c18fe5fe3a3db019e30d
This commit is contained in:
Benjamin Bouvier 2016-07-25 18:10:58 +02:00
Родитель 3bb9e21ec3
Коммит bcbc873b13
8 изменённых файлов: 593 добавлений и 31 удалений

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

@ -195,11 +195,13 @@ enum class AstExprKind
ComparisonOperator,
Const,
ConversionOperator,
GetGlobal,
GetLocal,
If,
Load,
Nop,
Return,
SetGlobal,
SetLocal,
Store,
TernaryOperator,
@ -290,6 +292,41 @@ class AstSetLocal : public AstExpr
}
};
class AstGetGlobal : public AstExpr
{
AstRef global_;
public:
static const AstExprKind Kind = AstExprKind::GetGlobal;
explicit AstGetGlobal(AstRef global)
: AstExpr(Kind),
global_(global)
{}
AstRef& global() {
return global_;
}
};
class AstSetGlobal : public AstExpr
{
AstRef global_;
AstExpr& value_;
public:
static const AstExprKind Kind = AstExprKind::SetGlobal;
AstSetGlobal(AstRef global, AstExpr& value)
: AstExpr(Kind),
global_(global),
value_(value)
{}
AstRef& global() {
return global_;
}
AstExpr& value() const {
return value_;
}
};
class AstBlock : public AstExpr
{
Expr expr_;
@ -527,14 +564,42 @@ class AstResizable
const Maybe<uint32_t>& maximum() const { return maximum_; }
};
class AstGlobal : public AstNode
{
AstName name_;
uint32_t flags_;
ValType type_;
Maybe<AstExpr*> init_;
public:
AstGlobal() : flags_(0), type_(ValType::Limit)
{}
explicit AstGlobal(AstName name, ValType type, uint32_t flags,
Maybe<AstExpr*> init = Maybe<AstExpr*>())
: name_(name), flags_(flags), type_(type), init_(init)
{}
AstName name() const { return name_; }
uint32_t flags() const { return flags_; }
ValType type() const { return type_; }
bool hasInit() const { return !!init_; }
AstExpr& init() const { MOZ_ASSERT(hasInit()); return **init_; }
};
typedef AstVector<AstGlobal*> AstGlobalVector;
class AstImport : public AstNode
{
AstName name_;
AstName module_;
AstName field_;
DefinitionKind kind_;
AstRef funcSig_;
AstResizable resizable_;
AstGlobal global_;
public:
AstImport(AstName name, AstName module, AstName field, AstRef funcSig)
@ -543,30 +608,48 @@ class AstImport : public AstNode
AstImport(AstName name, AstName module, AstName field, DefinitionKind kind, AstResizable resizable)
: name_(name), module_(module), field_(field), kind_(kind), resizable_(resizable)
{}
AstImport(AstName name, AstName module, AstName field, AstGlobal global)
: name_(name), module_(module), field_(field), kind_(DefinitionKind::Global), global_(global)
{}
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 resizable() const { MOZ_ASSERT(kind_ != DefinitionKind::Function); return resizable_; }
AstRef& funcSig() {
MOZ_ASSERT(kind_ == DefinitionKind::Function);
return funcSig_;
}
AstResizable resizable() const {
MOZ_ASSERT(kind_ == DefinitionKind::Memory || kind_ == DefinitionKind::Table);
return resizable_;
}
const AstGlobal& global() const {
MOZ_ASSERT(kind_ == DefinitionKind::Global);
return global_;
}
};
class AstExport : public AstNode
{
AstName name_;
DefinitionKind kind_;
AstRef func_;
AstRef ref_;
public:
AstExport(AstName name, AstRef func)
: name_(name), kind_(DefinitionKind::Function), func_(func)
AstExport(AstName name, DefinitionKind kind, AstRef ref)
: name_(name), kind_(kind), ref_(ref)
{}
explicit AstExport(AstName name, DefinitionKind kind)
: name_(name), kind_(kind)
{}
AstName name() const { return name_; }
DefinitionKind kind() const { return kind_; }
AstRef& func() { MOZ_ASSERT(kind_ == DefinitionKind::Function); return func_; }
AstRef& ref() {
MOZ_ASSERT(kind_ == DefinitionKind::Function || kind_ == DefinitionKind::Global);
return ref_;
}
};
class AstDataSegment : public AstNode
@ -636,6 +719,7 @@ class AstModule : public AstNode
FuncVector funcs_;
AstDataSegmentVector dataSegments_;
AstElemSegmentVector elemSegments_;
AstGlobalVector globals_;
public:
explicit AstModule(LifoAlloc& lifo)
@ -646,7 +730,8 @@ class AstModule : public AstNode
exports_(lifo),
funcs_(lifo),
dataSegments_(lifo),
elemSegments_(lifo)
elemSegments_(lifo),
globals_(lifo)
{}
bool init() {
return sigMap_.init();
@ -727,18 +812,24 @@ class AstModule : public AstNode
const FuncVector& funcs() const {
return funcs_;
}
const ImportVector& imports() const {
return imports_;
}
bool append(AstImport* imp) {
return imports_.append(imp);
}
const ImportVector& imports() const {
return imports_;
}
bool append(AstExport* exp) {
return exports_.append(exp);
}
const ExportVector& exports() const {
return exports_;
}
bool append(AstGlobal* glob) {
return globals_.append(glob);
}
const AstGlobalVector& globals() const {
return globals_;
}
};
class AstUnaryOperator final : public AstExpr

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

@ -1391,6 +1391,71 @@ AstDecodeMemorySection(AstDecodeContext& c)
return true;
}
static bool
AstDecodeGlobal(AstDecodeContext& c, uint32_t i, AstGlobal* global)
{
AstName name;
if (!AstDecodeGenerateName(c, AstName(u"global"), i, &name))
return false;
ValType type;
if (!c.d.readValType(&type))
return AstDecodeFail(c, "bad global type");
uint32_t flags;
if (!c.d.readVarU32(&flags))
return AstDecodeFail(c, "expected flags");
if (flags & ~uint32_t(GlobalFlags::AllowedMask))
return AstDecodeFail(c, "unexpected bits set in flags");
if (!AstDecodeExpr(c))
return false;
AstDecodeStackItem item = c.iter().getResult();
MOZ_ASSERT(item.popped == 0);
if (!item.expr)
return AstDecodeFail(c, "missing initializer expression");
AstExpr* init = item.expr;
if (!AstDecodeEnd(c))
return AstDecodeFail(c, "missing initializer end");
item = c.iter().getResult();
MOZ_ASSERT(item.terminationKind == AstDecodeTerminationKind::End);
*global = AstGlobal(name, type, flags, Some(init));
return true;
}
static bool
AstDecodeGlobalSection(AstDecodeContext& c)
{
uint32_t sectionStart, sectionSize;
if (!c.d.startSection(GlobalSectionId, &sectionStart, &sectionSize))
return AstDecodeFail(c, "failed to start section");
if (sectionStart == Decoder::NotStarted)
return true;
uint32_t numGlobals;
if (!c.d.readVarU32(&numGlobals))
return AstDecodeFail(c, "expected number of globals");
for (uint32_t i = 0; i < numGlobals; i++) {
auto* global = new(c.lifo) AstGlobal;
if (!AstDecodeGlobal(c, i, global))
return false;
if (!c.module().append(global))
return false;
}
if (!c.d.finishSection(sectionStart, sectionSize))
return AstDecodeFail(c, "globals section byte size mismatch");
return true;
}
static bool
AstDecodeFunctionExport(AstDecodeContext& c, AstExport** export_)
{
@ -1405,7 +1470,8 @@ AstDecodeFunctionExport(AstDecodeContext& c, AstExport** export_)
if (!AstDecodeName(c, &fieldName))
return AstDecodeFail(c, "expected export name");
*export_ = new(c.lifo) AstExport(fieldName, AstRef(AstName(), funcIndex));
*export_ = new(c.lifo) AstExport(fieldName, DefinitionKind::Function,
AstRef(AstName(), funcIndex));
if (!*export_)
return false;
@ -1647,6 +1713,9 @@ wasm::BinaryToAst(JSContext* cx, const uint8_t* bytes, uint32_t length,
if (!AstDecodeMemorySection(c))
return false;
if (!AstDecodeGlobalSection(c))
return false;
if (!AstDecodeExportSection(c))
return false;

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

@ -1565,9 +1565,9 @@ PrintExport(WasmPrintContext& c, AstExport& export_, const AstModule::FuncVector
if (!c.buffer.append("memory"))
return false;
} else {
const AstFunc* func = funcs[export_.func().index()];
const AstFunc* func = funcs[export_.ref().index()];
if (func->name().empty()) {
if (!PrintInt32(c, export_.func().index()))
if (!PrintInt32(c, export_.ref().index()))
return false;
} else {
if (!PrintName(c, func->name()))

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

@ -1174,9 +1174,9 @@ RenderExport(WasmRenderContext& c, AstExport& export_, const AstModule::FuncVect
if (!c.buffer.append("memory"))
return false;
} else {
const AstFunc* func = funcs[export_.func().index()];
const AstFunc* func = funcs[export_.ref().index()];
if (func->name().empty()) {
if (!RenderInt32(c, export_.func().index()))
if (!RenderInt32(c, export_.ref().index()))
return false;
} else {
if (!RenderName(c, func->name()))

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

@ -86,8 +86,11 @@ class WasmToken
Export,
Float,
Func,
GetGlobal,
GetLocal,
Global,
If,
Immutable,
Import,
Index,
Memory,
@ -105,6 +108,7 @@ class WasmToken
Result,
Return,
Segment,
SetGlobal,
SetLocal,
SignedInteger,
Start,
@ -978,8 +982,12 @@ WasmTokenStream::next()
break;
case 'g':
if (consume(u"get_global"))
return WasmToken(WasmToken::GetGlobal, begin, cur_);
if (consume(u"get_local"))
return WasmToken(WasmToken::GetLocal, begin, cur_);
if (consume(u"global"))
return WasmToken(WasmToken::Global, begin, cur_);
break;
case 'i':
@ -1266,6 +1274,8 @@ WasmTokenStream::next()
}
break;
}
if (consume(u"immutable"))
return WasmToken(WasmToken::Immutable, begin, cur_);
if (consume(u"import"))
return WasmToken(WasmToken::Import, begin, cur_);
if (consume(u"infinity"))
@ -1317,6 +1327,8 @@ WasmTokenStream::next()
case 's':
if (consume(u"select"))
return WasmToken(WasmToken::TernaryOpcode, Expr::Select, begin, cur_);
if (consume(u"set_global"))
return WasmToken(WasmToken::SetGlobal, begin, cur_);
if (consume(u"set_local"))
return WasmToken(WasmToken::SetLocal, begin, cur_);
if (consume(u"segment"))
@ -1841,6 +1853,29 @@ ParseGetLocal(WasmParseContext& c)
return new(c.lifo) AstGetLocal(local);
}
static AstGetGlobal*
ParseGetGlobal(WasmParseContext& c)
{
AstRef local;
if (!c.ts.matchRef(&local, c.error))
return nullptr;
return new(c.lifo) AstGetGlobal(local);
}
static AstSetGlobal*
ParseSetGlobal(WasmParseContext& c)
{
AstRef global;
if (!c.ts.matchRef(&global, c.error))
return nullptr;
AstExpr* value = ParseExpr(c);
if (!value)
return nullptr;
return new(c.lifo) AstSetGlobal(global, *value);
}
static AstSetLocal*
ParseSetLocal(WasmParseContext& c)
{
@ -2176,6 +2211,8 @@ ParseExprInsideParens(WasmParseContext& c)
return ParseConversionOperator(c, token.expr());
case WasmToken::If:
return ParseIf(c);
case WasmToken::GetGlobal:
return ParseGetGlobal(c);
case WasmToken::GetLocal:
return ParseGetLocal(c);
case WasmToken::Load:
@ -2184,6 +2221,8 @@ ParseExprInsideParens(WasmParseContext& c)
return ParseBlock(c, Expr::Loop);
case WasmToken::Return:
return ParseReturn(c);
case WasmToken::SetGlobal:
return ParseSetGlobal(c);
case WasmToken::SetLocal:
return ParseSetLocal(c);
case WasmToken::Store:
@ -2423,6 +2462,20 @@ ParseStartFunc(WasmParseContext& c, WasmToken token, AstModule* module)
return true;
}
static bool
ParseGlobalType(WasmParseContext& c, WasmToken* typeToken, uint32_t* flags)
{
if (!c.ts.match(WasmToken::ValueType, typeToken, c.error))
return false;
// Mutable by default.
*flags = 0x1;
if (c.ts.getIf(WasmToken::Immutable))
*flags = 0x0;
return true;
}
static AstImport*
ParseImport(WasmParseContext& c, bool newFormat, AstModule* module)
{
@ -2458,6 +2511,16 @@ ParseImport(WasmParseContext& c, bool newFormat, AstModule* module)
return new(c.lifo) AstImport(name, moduleName.text(), fieldName.text(),
DefinitionKind::Table, table);
}
if (c.ts.getIf(WasmToken::Global)) {
WasmToken typeToken;
uint32_t flags = 0;
if (!ParseGlobalType(c, &typeToken, &flags))
return nullptr;
if (!c.ts.match(WasmToken::CloseParen, c.error))
return nullptr;
return new(c.lifo) AstImport(name, moduleName.text(), fieldName.text(),
AstGlobal(AstName(), typeToken.valueType(), flags));
}
}
if (c.ts.getIf(WasmToken::Type)) {
@ -2494,13 +2557,21 @@ ParseExport(WasmParseContext& c)
WasmToken exportee = c.ts.get();
switch (exportee.kind()) {
case WasmToken::Index:
return new(c.lifo) AstExport(name.text(), AstRef(AstName(), exportee.index()));
return new(c.lifo) AstExport(name.text(), DefinitionKind::Function,
AstRef(AstName(), exportee.index()));
case WasmToken::Name:
return new(c.lifo) AstExport(name.text(), AstRef(exportee.name(), AstNoIndex));
return new(c.lifo) AstExport(name.text(), DefinitionKind::Function,
AstRef(exportee.name(), AstNoIndex));
case WasmToken::Table:
return new(c.lifo) AstExport(name.text(), DefinitionKind::Table);
case WasmToken::Memory:
return new(c.lifo) AstExport(name.text(), DefinitionKind::Memory);
case WasmToken::Global: {
AstRef ref;
if (!c.ts.matchRef(&ref, c.error))
return nullptr;
return new(c.lifo) AstExport(name.text(), DefinitionKind::Global, ref);
}
default:
break;
}
@ -2563,6 +2634,23 @@ ParseElemSegment(WasmParseContext& c)
return new(c.lifo) AstElemSegment(offset.index(), Move(elems));
}
static AstGlobal*
ParseGlobal(WasmParseContext& c)
{
AstName name = c.ts.getIfName();
WasmToken typeToken;
uint32_t flags = 0;
if (!ParseGlobalType(c, &typeToken, &flags))
return nullptr;
AstExpr* init = ParseExpr(c);
if (!init)
return nullptr;
return new(c.lifo) AstGlobal(name, typeToken.valueType(), flags, Some(init));
}
static AstModule*
ParseModule(const char16_t* text, bool newFormat, LifoAlloc& lifo, UniqueChars* error)
{
@ -2597,6 +2685,12 @@ ParseModule(const char16_t* text, bool newFormat, LifoAlloc& lifo, UniqueChars*
return nullptr;
break;
}
case WasmToken::Global: {
AstGlobal* global = ParseGlobal(c);
if (!global || !module->append(global))
return nullptr;
break;
}
case WasmToken::Data: {
AstDataSegment* segment = ParseDataSegment(c);
if (!segment || !module->append(segment))
@ -2658,6 +2752,7 @@ class Resolver
{
UniqueChars* error_;
AstNameMap varMap_;
AstNameMap globalMap_;
AstNameMap sigMap_;
AstNameMap funcMap_;
AstNameMap importMap_;
@ -2703,13 +2798,18 @@ class Resolver
explicit Resolver(LifoAlloc& lifo, UniqueChars* error)
: error_(error),
varMap_(lifo),
globalMap_(lifo),
sigMap_(lifo),
funcMap_(lifo),
importMap_(lifo),
targetStack_(lifo)
{}
bool init() {
return sigMap_.init() && funcMap_.init() && importMap_.init() && varMap_.init();
return sigMap_.init() &&
funcMap_.init() &&
importMap_.init() &&
varMap_.init() &&
globalMap_.init();
}
void beginFunc() {
varMap_.clear();
@ -2727,6 +2827,9 @@ class Resolver
bool registerVarName(AstName name, size_t index) {
return name.empty() || registerName(varMap_, name, index);
}
bool registerGlobalName(AstName name, size_t index) {
return name.empty() || registerName(globalMap_, name, index);
}
bool pushTarget(AstName name) {
return targetStack_.append(name);
}
@ -2755,6 +2858,11 @@ class Resolver
return failResolveLabel("local", ref.name());
return true;
}
bool resolveGlobal(AstRef& ref) {
if (!ref.name().empty() && !resolveRef(globalMap_, ref))
return failResolveLabel("global", ref.name());
return true;
}
bool resolveBranchTarget(AstRef& ref) {
if (ref.name().empty())
return true;
@ -2887,6 +2995,24 @@ ResolveSetLocal(Resolver& r, AstSetLocal& sl)
return true;
}
static bool
ResolveGetGlobal(Resolver& r, AstGetGlobal& gl)
{
return r.resolveGlobal(gl.global());
}
static bool
ResolveSetGlobal(Resolver& r, AstSetGlobal& sl)
{
if (!ResolveExpr(r, sl.value()))
return false;
if (!r.resolveGlobal(sl.global()))
return false;
return true;
}
static bool
ResolveUnaryOperator(Resolver& r, AstUnaryOperator& b)
{
@ -3006,6 +3132,8 @@ ResolveExpr(Resolver& r, AstExpr& expr)
return true;
case AstExprKind::ConversionOperator:
return ResolveConversionOperator(r, expr.as<AstConversionOperator>());
case AstExprKind::GetGlobal:
return ResolveGetGlobal(r, expr.as<AstGetGlobal>());
case AstExprKind::GetLocal:
return ResolveGetLocal(r, expr.as<AstGetLocal>());
case AstExprKind::If:
@ -3014,6 +3142,8 @@ ResolveExpr(Resolver& r, AstExpr& expr)
return ResolveLoad(r, expr.as<AstLoad>());
case AstExprKind::Return:
return ResolveReturn(r, expr.as<AstReturn>());
case AstExprKind::SetGlobal:
return ResolveSetGlobal(r, expr.as<AstSetGlobal>());
case AstExprKind::SetLocal:
return ResolveSetLocal(r, expr.as<AstSetLocal>());
case AstExprKind::Store:
@ -3033,8 +3163,7 @@ ResolveFunc(Resolver& r, AstFunc& func)
{
r.beginFunc();
size_t numVars = func.locals().length();
for (size_t i = 0; i < numVars; i++) {
for (size_t i = 0; i < func.locals().length(); i++) {
if (!r.registerVarName(func.locals()[i], i))
return r.fail("duplicate var");
}
@ -3078,29 +3207,50 @@ ResolveModule(LifoAlloc& lifo, AstModule* module, UniqueChars* error)
}
size_t numImports = module->imports().length();
size_t lastGlobalIndex = 0;
for (size_t i = 0; i < numImports; i++) {
AstImport* imp = module->imports()[i];
if (!r.registerImportName(imp->name(), i))
return r.fail("duplicate import");
switch (imp->kind()) {
case DefinitionKind::Function:
if (!r.registerImportName(imp->name(), i))
return r.fail("duplicate import");
if (!r.resolveSignature(imp->funcSig()))
return false;
break;
case DefinitionKind::Global:
if (!r.registerGlobalName(imp->name(), lastGlobalIndex++))
return r.fail("duplicate import");
break;
case DefinitionKind::Memory:
case DefinitionKind::Table:
break;
}
}
for (AstExport* export_ : module->exports()) {
if (export_->kind() != DefinitionKind::Function)
continue;
if (!r.resolveFunction(export_->func()))
const AstGlobalVector& globals = module->globals();
for (const AstGlobal* global : globals) {
if (!r.registerGlobalName(global->name(), lastGlobalIndex++))
return r.fail("duplicate import");
if (global->hasInit() && !ResolveExpr(r, global->init()))
return false;
}
for (AstExport* export_ : module->exports()) {
switch (export_->kind()) {
case DefinitionKind::Function:
if (!r.resolveFunction(export_->ref()))
return false;
break;
case DefinitionKind::Global:
if (!r.resolveGlobal(export_->ref()))
return false;
break;
case DefinitionKind::Table:
case DefinitionKind::Memory:
break;
}
}
for (AstFunc* func : module->funcs()) {
if (!ResolveFunc(r, *func))
return false;
@ -3261,6 +3411,21 @@ EncodeSetLocal(Encoder& e, AstSetLocal& sl)
e.writeVarU32(sl.local().index());
}
static bool
EncodeGetGlobal(Encoder& e, AstGetGlobal& gg)
{
return e.writeExpr(Expr::GetGlobal) &&
e.writeVarU32(gg.global().index());
}
static bool
EncodeSetGlobal(Encoder& e, AstSetGlobal& sg)
{
return EncodeExpr(e, sg.value()) &&
e.writeExpr(Expr::SetGlobal) &&
e.writeVarU32(sg.global().index());
}
static bool
EncodeUnaryOperator(Encoder& e, AstUnaryOperator& b)
{
@ -3427,6 +3592,8 @@ EncodeExpr(Encoder& e, AstExpr& expr)
return EncodeConversionOperator(e, expr.as<AstConversionOperator>());
case AstExprKind::GetLocal:
return EncodeGetLocal(e, expr.as<AstGetLocal>());
case AstExprKind::GetGlobal:
return EncodeGetGlobal(e, expr.as<AstGetGlobal>());
case AstExprKind::If:
return EncodeIf(e, expr.as<AstIf>());
case AstExprKind::Load:
@ -3435,6 +3602,8 @@ EncodeExpr(Encoder& e, AstExpr& expr)
return EncodeReturn(e, expr.as<AstReturn>());
case AstExprKind::SetLocal:
return EncodeSetLocal(e, expr.as<AstSetLocal>());
case AstExprKind::SetGlobal:
return EncodeSetGlobal(e, expr.as<AstSetGlobal>());
case AstExprKind::Store:
return EncodeStore(e, expr.as<AstStore>());
case AstExprKind::BranchTable:
@ -3569,6 +3738,13 @@ EncodeImport(Encoder& e, bool newFormat, AstImport& imp)
if (!e.writeVarU32(imp.funcSig().index()))
return false;
break;
case DefinitionKind::Global:
MOZ_ASSERT(!imp.global().hasInit());
if (!e.writeValType(imp.global().type()))
return false;
if (!e.writeVarU32(imp.global().flags()))
return false;
break;
case DefinitionKind::Table:
case DefinitionKind::Memory:
if (!EncodeResizable(e, imp.resizable()))
@ -3640,6 +3816,34 @@ EncodeMemorySection(Encoder& e, bool newFormat, AstModule& module)
return true;
}
static bool
EncodeGlobalSection(Encoder& e, AstModule& module)
{
size_t offset;
if (!e.startSection(GlobalSectionId, &offset))
return false;
const AstGlobalVector& globals = module.globals();
if (!e.writeVarU32(globals.length()))
return false;
for (const AstGlobal* global : globals) {
MOZ_ASSERT(global->hasInit());
if (!e.writeValType(global->type()))
return false;
if (!e.writeVarU32(global->flags()))
return false;
if (!EncodeExpr(e, global->init()))
return false;
if (!e.writeExpr(Expr::End))
return false;
}
e.finishSection(offset);
return true;
}
static bool
EncodeExport(Encoder& e, bool newFormat, AstExport& exp)
{
@ -3647,7 +3851,7 @@ EncodeExport(Encoder& e, bool newFormat, AstExport& exp)
if (exp.kind() != DefinitionKind::Function)
return true;
if (!e.writeVarU32(exp.func().index()))
if (!e.writeVarU32(exp.ref().index()))
return false;
if (!EncodeBytes(e, exp.name()))
@ -3664,7 +3868,8 @@ EncodeExport(Encoder& e, bool newFormat, AstExport& exp)
switch (exp.kind()) {
case DefinitionKind::Function:
if (!e.writeVarU32(exp.func().index()))
case DefinitionKind::Global:
if (!e.writeVarU32(exp.ref().index()))
return false;
break;
case DefinitionKind::Table:
@ -3932,6 +4137,9 @@ EncodeModule(AstModule& module, bool newFormat, Bytes* bytes)
if (!EncodeMemorySection(e, newFormat, module))
return false;
if (!EncodeGlobalSection(e, module))
return false;
if (!EncodeExportSection(e, newFormat, module))
return false;

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

@ -36,8 +36,9 @@ function jsify(wasmVal) {
// - if the expected value is in the int32 range, it can be just a number.
// - otherwise, an object with the properties "high" and "low".
function assertEqI64(observed, expect) {
assertEq(typeof observed, 'object');
assertEq(typeof expect === 'object' || typeof expect === 'number', true);
assertEq(typeof observed, 'object', "observed must be an i64 object");
assertEq(typeof expect === 'object' || typeof expect === 'number', true,
"expect must be an i64 object or number");
let {low, high} = observed;
if (typeof expect === 'number') {

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

@ -0,0 +1,192 @@
// |jit-test| test-also-wasm-baseline
load(libdir + "wasm.js");
const { Instance, Module } = WebAssembly;
const evalText = (txt, imports = {}) => new Instance(new Module(wasmTextToBinary(txt, 'new-format')), imports).exports;
// Locally-defined globals
assertErrorMessage(() => evalText(`(module (global))`), SyntaxError, /parsing/);
assertErrorMessage(() => evalText(`(module (global i32))`), SyntaxError, /parsing/);
assertErrorMessage(() => evalText(`(module (global i32 immutable))`), SyntaxError, /parsing/);
// Initializer expressions.
assertErrorMessage(() => evalText(`(module (global i32 (f32.const 13.37)))`), TypeError, /type mismatch/);
assertErrorMessage(() => evalText(`(module (global f64 (f32.const 13.37)))`), TypeError, /type mismatch/);
assertErrorMessage(() => evalText(`(module (global i32 (i32.add (i32.const 13) (i32.const 37))))`), TypeError, /failed to read end/);
assertErrorMessage(() => evalText(`(module (global i32 (get_global 0)))`), TypeError, /out of range/);
assertErrorMessage(() => evalText(`(module (global i32 (get_global 1)) (global i32 immutable (i32.const 1)))`), TypeError, /out of range/);
// Test a well-defined global section.
function testInner(type, initialValue, nextValue, coercion, assertFunc = assertEq)
{
var module = evalText(`(module
(global ${type} (${type}.const ${initialValue}))
(global ${type} immutable (${type}.const ${initialValue}))
(func $get (result ${type}) (get_global 0))
(func $set (param ${type}) (set_global 0 (get_local 0)))
(func $get_cst (result ${type}) (get_global 1))
(export "get" $get)
(export "get_cst" $get_cst)
(export "set" $set)
)`);
assertFunc(module.get(), coercion(initialValue));
assertEq(module.set(coercion(nextValue)), undefined);
assertFunc(module.get(), coercion(nextValue));
assertFunc(module.get_cst(), coercion(initialValue));
}
testInner('i32', 13, 37, x => x|0);
testInner('f32', 13.37, 0.1989, Math.fround);
testInner('f64', 13.37, 0.1989, x => +x);
// Semantic errors.
assertErrorMessage(() => evalText(`(module (global i32 (i32.const 1337)) (func (set_global 1 (i32.const 0))))`), TypeError, /out of range/);
assertErrorMessage(() => evalText(`(module (global i32 immutable (i32.const 1337)) (func (set_global 0 (i32.const 0))))`), TypeError, /can't write an immutable global/);
// Big module with many variables: test that setting one doesn't overwrite the
// other ones.
function get_set(i, type) { return `
(func $get_${i} (result ${type}) (get_global ${i}))
(func $set_${i} (param ${type}) (set_global ${i} (get_local 0)))
`
}
var module = evalText(`(module
(global i32 (i32.const 42))
(global i32 (i32.const 10))
(global f32 (f32.const 13.37))
(global f64 (f64.const 13.37))
(global i32 (i32.const -18))
${get_set(0, 'i32')}
${get_set(1, 'i32')}
${get_set(2, 'f32')}
${get_set(3, 'f64')}
${get_set(4, 'i32')}
(export "get0" $get_0) (export "set0" $set_0)
(export "get1" $get_1) (export "set1" $set_1)
(export "get2" $get_2) (export "set2" $set_2)
(export "get3" $get_3) (export "set3" $set_3)
(export "get4" $get_4) (export "set4" $set_4)
)`);
let values = [42, 10, Math.fround(13.37), 13.37, -18];
let nextValues = [13, 37, Math.fround(-17.89), 9.3, -13];
for (let i = 0; i < 5; i++) {
assertEq(module[`get${i}`](), values[i]);
assertEq(module[`set${i}`](nextValues[i]), undefined);
assertEq(module[`get${i}`](), nextValues[i]);
for (let j = 0; j < 5; j++) {
if (i === j)
continue;
assertEq(module[`get${j}`](), values[j]);
}
assertEq(module[`set${i}`](values[i]), undefined);
assertEq(module[`get${i}`](), values[i]);
}
// Import/export rules.
assertErrorMessage(() => evalText(`(module (import "globals" "x" (global i32)))`), TypeError, /can't import.* mutable globals in the MVP/);
assertErrorMessage(() => evalText(`(module (global i32 (i32.const 42)) (export "" global 0))`), TypeError, /can't .*export mutable globals in the MVP/);
// Import/export semantics.
module = evalText(`(module
(import $g "globals" "x" (global i32 immutable))
(func $get (result i32) (get_global $g))
(export "getter" $get)
(export "value" global 0)
)`, { globals: {x: 42} });
assertEq(module.getter(), 42);
assertEq(module.value, 42);
// Imported globals and locally defined globals use the same index space.
module = evalText(`(module
(import "globals" "x" (global i32 immutable))
(global i32 immutable (i32.const 1337))
(export "imported" global 0)
(export "defined" global 1)
)`, { globals: {x: 42} });
assertEq(module.imported, 42);
assertEq(module.defined, 1337);
// Initializer expressions can reference an imported immutable global.
assertErrorMessage(() => evalText(`(module (global f32 immutable (f32.const 13.37)) (global i32 (get_global 0)))`), TypeError, /must reference a global immutable import/);
assertErrorMessage(() => evalText(`(module (global f32 (f32.const 13.37)) (global i32 (get_global 0)))`), TypeError, /must reference a global immutable import/);
assertErrorMessage(() => evalText(`(module (global i32 (i32.const 0)) (global i32 (get_global 0)))`), TypeError, /must reference a global immutable import/);
assertErrorMessage(() => evalText(`(module (import "globals" "a" (global f32 immutable)) (global i32 (get_global 0)))`), TypeError, /type mismatch/);
function testInitExpr(type, initialValue, nextValue, coercion, assertFunc = assertEq) {
var module = evalText(`(module
(import "globals" "a" (global ${type} immutable))
(global ${type} (get_global 0))
(global ${type} immutable (get_global 0))
(func $get0 (result ${type}) (get_global 0))
(func $get1 (result ${type}) (get_global 1))
(func $set1 (param ${type}) (set_global 1 (get_local 0)))
(func $get_cst (result ${type}) (get_global 2))
(export "get0" $get0)
(export "get1" $get1)
(export "get_cst" $get_cst)
(export "set1" $set1)
)`, {
globals: {
a: coercion(initialValue)
}
});
assertFunc(module.get0(), coercion(initialValue));
assertFunc(module.get1(), coercion(initialValue));
assertEq(module.set1(coercion(nextValue)), undefined);
assertFunc(module.get1(), coercion(nextValue));
assertFunc(module.get0(), coercion(initialValue));
assertFunc(module.get_cst(), coercion(initialValue));
}
testInitExpr('i32', 13, 37, x => x|0);
testInitExpr('f32', 13.37, 0.1989, Math.fround);
testInitExpr('f64', 13.37, 0.1989, x => +x);
// Int64.
if (hasI64()) {
assertErrorMessage(() => evalText(`(module (import "globals" "x" (global i64 immutable)))`), TypeError, /can't import.* an Int64 global/);
assertErrorMessage(() => evalText(`(module (global i64 immutable (i64.const 42)) (export "" global 0))`), TypeError, /can't .*export an Int64 global/);
setJitCompilerOption('wasm.test-mode', 1);
testInner('i64', '0x531642753864975F', '0x123456789abcdef0', createI64, assertEqI64);
testInitExpr('i64', '0x531642753864975F', '0x123456789abcdef0', createI64, assertEqI64);
module = evalText(`(module
(import "globals" "x" (global i64 immutable))
(global i64 immutable (i64.const 0xFAFADADABABA))
(export "imported" global 0)
(export "defined" global 1)
)`, { globals: {x: createI64('0x1234567887654321')} });
assertEqI64(module.imported, createI64('0x1234567887654321'));
assertEqI64(module.defined, createI64('0xFAFADADABABA'));
setJitCompilerOption('wasm.test-mode', 0);
} else {
assertErrorMessage(() => evalText(`(module (global i64 (i64.const 0)))`), TypeError, /NYI/);
assertErrorMessage(() => evalText(`(module (import "globals" "x" (global i64 immutable)))`), TypeError, /NYI/);
}

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

@ -267,6 +267,7 @@ 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" global 0))')), TypeError, /exported global 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/);