Bug 1243252 - Baldr: add import section (r=bbouvier)

--HG--
extra : commitid : 64BPCvQPUL7
extra : rebase_source : 5cc46dd463ea6be7795c4787a99460fa9d19a4cc
This commit is contained in:
Luke Wagner 2016-01-28 10:30:41 -06:00
Родитель 38c56107c6
Коммит c3fde7cc15
9 изменённых файлов: 434 добавлений и 93 удалений

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

@ -2084,10 +2084,7 @@ class MOZ_STACK_CLASS ModuleValidator
uint32_t sigIndex; uint32_t sigIndex;
if (!declareSig(Move(sig), &sigIndex)) if (!declareSig(Move(sig), &sigIndex))
return false; return false;
uint32_t globalDataOffset; if (!mg_.initImport(*importIndex, sigIndex))
if (!mg_.allocateGlobalBytes(Module::SizeOfImportExit, sizeof(void*), &globalDataOffset))
return false;
if (!mg_.initImport(*importIndex, sigIndex, globalDataOffset))
return false; return false;
return importMap_.add(p, NamedSig(name, mg_.sig(sigIndex)), *importIndex); return importMap_.add(p, NamedSig(name, mg_.sig(sigIndex)), *importIndex);
} }
@ -7148,7 +7145,7 @@ ValidateFFI(JSContext* cx, const AsmJSGlobal& global, HandleValue importVal,
if (!GetDataProperty(cx, importVal, field, &v)) if (!GetDataProperty(cx, importVal, field, &v))
return false; return false;
if (!v.isObject() || !v.toObject().is<JSFunction>()) if (!IsFunctionObject(v))
return LinkFail(cx, "FFI imports must be functions"); return LinkFail(cx, "FFI imports must be functions");
ffis[global.ffiIndex()].set(&v.toObject().as<JSFunction>()); ffis[global.ffiIndex()].set(&v.toObject().as<JSFunction>());

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

@ -314,6 +314,21 @@ DecodeSignatureSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init)
return true; return true;
} }
static bool
DecodeSignatureIndex(JSContext* cx, Decoder& d, const ModuleGeneratorData& init,
const DeclaredSig** sig)
{
uint32_t sigIndex;
if (!d.readVarU32(&sigIndex))
return Fail(cx, d, "expected signature index");
if (sigIndex >= init.sigs.length())
return Fail(cx, d, "signature index out of range");
*sig = &init.sigs[sigIndex];
return true;
}
static bool static bool
DecodeDeclarationSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init) DecodeDeclarationSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init)
{ {
@ -332,14 +347,8 @@ DecodeDeclarationSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init)
return false; return false;
for (uint32_t i = 0; i < numDecls; i++) { for (uint32_t i = 0; i < numDecls; i++) {
uint32_t sigIndex; if (!DecodeSignatureIndex(cx, d, *init, &init->funcSigs[i]))
if (!d.readVarU32(&sigIndex)) return false;
return Fail(cx, d, "expected declaration signature index");
if (sigIndex >= init->sigs.length())
return Fail(cx, d, "declaration signature index out of range");
init->funcSigs[i] = &init->sigs[sigIndex];
} }
if (!d.finishSection(sectionStart)) if (!d.finishSection(sectionStart))
@ -348,6 +357,81 @@ DecodeDeclarationSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init)
return true; return true;
} }
struct ImportName
{
UniqueChars module;
UniqueChars func;
ImportName(UniqueChars module, UniqueChars func)
: module(Move(module)), func(Move(func))
{}
ImportName(ImportName&& rhs)
: module(Move(rhs.module)), func(Move(rhs.func))
{}
};
typedef Vector<ImportName, 0, SystemAllocPolicy> ImportNameVector;
static bool
DecodeImport(JSContext* cx, Decoder& d, ModuleGeneratorData* init, ImportNameVector* importNames)
{
if (!d.readCStringIf(FuncSubsection))
return Fail(cx, d, "expected 'func' tag");
const DeclaredSig* sig;
if (!DecodeSignatureIndex(cx, d, *init, &sig))
return false;
if (!init->imports.emplaceBack(sig))
return false;
const char* moduleStr;
if (!d.readCString(&moduleStr))
return Fail(cx, d, "expected import module name");
if (!*moduleStr)
return Fail(cx, d, "module name cannot be empty");
UniqueChars moduleName = DuplicateString(moduleStr);
if (!moduleName)
return false;
const char* funcStr;
if (!d.readCString(&funcStr))
return Fail(cx, d, "expected import func name");
UniqueChars funcName = DuplicateString(funcStr);
if (!funcName)
return false;
return importNames->emplaceBack(Move(moduleName), Move(funcName));
}
static bool
DecodeImportSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init, ImportNameVector* imports)
{
if (!d.readCStringIf(ImportSection))
return true;
uint32_t sectionStart;
if (!d.startSection(&sectionStart))
return Fail(cx, d, "expected import section byte size");
uint32_t numImports;
if (!d.readVarU32(&numImports))
return Fail(cx, d, "expected number of imports");
for (uint32_t i = 0; i < numImports; i++) {
if (!DecodeImport(cx, d, init, imports))
return false;
}
if (!d.finishSection(sectionStart))
return Fail(cx, d, "import section byte size mismatch");
return true;
}
static bool static bool
DecodeExport(JSContext* cx, Decoder& d, ModuleGenerator& mg, ExportMap* exportMap) DecodeExport(JSContext* cx, Decoder& d, ModuleGenerator& mg, ExportMap* exportMap)
{ {
@ -518,7 +602,8 @@ DecodeUnknownSection(JSContext* cx, Decoder& d)
static bool static bool
DecodeModule(JSContext* cx, UniqueChars filename, const uint8_t* bytes, uint32_t length, DecodeModule(JSContext* cx, UniqueChars filename, const uint8_t* bytes, uint32_t length,
MutableHandle<WasmModuleObject*> moduleObj, ExportMap* exportMap) ImportNameVector* importNames, ExportMap* exportMap,
MutableHandle<WasmModuleObject*> moduleObj)
{ {
Decoder d(bytes, bytes + length); Decoder d(bytes, bytes + length);
@ -539,6 +624,9 @@ DecodeModule(JSContext* cx, UniqueChars filename, const uint8_t* bytes, uint32_t
if (!DecodeDeclarationSection(cx, d, init.get())) if (!DecodeDeclarationSection(cx, d, init.get()))
return false; return false;
if (!DecodeImportSection(cx, d, init.get(), importNames))
return false;
ModuleGenerator mg(cx); ModuleGenerator mg(cx);
if (!mg.init(Move(init))) if (!mg.init(Move(init)))
return false; return false;
@ -578,7 +666,49 @@ DecodeModule(JSContext* cx, UniqueChars filename, const uint8_t* bytes, uint32_t
} }
/*****************************************************************************/ /*****************************************************************************/
// JS entry poitns // JS entry points
static bool
GetProperty(JSContext* cx, HandleObject obj, const char* utf8Chars, MutableHandleValue v)
{
JSAtom* atom = AtomizeUTF8Chars(cx, utf8Chars, strlen(utf8Chars));
if (!atom)
return false;
RootedId id(cx, AtomToId(atom));
return GetProperty(cx, obj, obj, id, v);
}
static bool
ImportFunctions(JSContext* cx, HandleObject importObj, const ImportNameVector& importNames,
MutableHandle<FunctionVector> imports)
{
if (!importNames.empty() && !importObj)
return Fail(cx, "no import object given");
for (const ImportName& name : importNames) {
RootedValue v(cx);
if (!GetProperty(cx, importObj, name.module.get(), &v))
return false;
if (*name.func.get()) {
if (!v.isObject())
return Fail(cx, "import object field is not an Object");
RootedObject obj(cx, &v.toObject());
if (!GetProperty(cx, obj, name.func.get(), &v))
return false;
}
if (!IsFunctionObject(v))
return Fail(cx, "import object field is not a Function");
if (!imports.append(&v.toObject().as<JSFunction>()))
return false;
}
return true;
}
static bool static bool
SupportsWasm(JSContext* cx) SupportsWasm(JSContext* cx)
@ -636,15 +766,6 @@ WasmEval(JSContext* cx, unsigned argc, Value* vp)
return false; return false;
} }
if (!args.get(1).isUndefined() && !args.get(1).isObject()) {
ReportUsageError(cx, callee, "Second argument, if present, must be an Object");
return false;
}
UniqueChars filename;
if (!DescribeScriptedCaller(cx, &filename))
return false;
Rooted<ArrayBufferObject*> code(cx, &args[0].toObject().as<ArrayBufferObject>()); Rooted<ArrayBufferObject*> code(cx, &args[0].toObject().as<ArrayBufferObject>());
const uint8_t* bytes = code->dataPointer(); const uint8_t* bytes = code->dataPointer();
uint32_t length = code->byteLength(); uint32_t length = code->byteLength();
@ -656,9 +777,23 @@ WasmEval(JSContext* cx, unsigned argc, Value* vp)
bytes = copy.begin(); bytes = copy.begin();
} }
Rooted<WasmModuleObject*> moduleObj(cx); RootedObject importObj(cx);
if (!args.get(1).isUndefined()) {
if (!args.get(1).isObject()) {
ReportUsageError(cx, callee, "Second argument, if present, must be an Object");
return false;
}
importObj = &args[1].toObject();
}
UniqueChars filename;
if (!DescribeScriptedCaller(cx, &filename))
return false;
ImportNameVector importNames;
ExportMap exportMap; ExportMap exportMap;
if (!DecodeModule(cx, Move(filename), bytes, length, &moduleObj, &exportMap)) { Rooted<WasmModuleObject*> moduleObj(cx);
if (!DecodeModule(cx, Move(filename), bytes, length, &importNames, &exportMap, &moduleObj)) {
if (!cx->isExceptionPending()) if (!cx->isExceptionPending())
ReportOutOfMemory(cx); ReportOutOfMemory(cx);
return false; return false;
@ -671,8 +806,8 @@ WasmEval(JSContext* cx, unsigned argc, Value* vp)
return Fail(cx, "Heap not implemented yet"); return Fail(cx, "Heap not implemented yet");
Rooted<FunctionVector> imports(cx, FunctionVector(cx)); Rooted<FunctionVector> imports(cx, FunctionVector(cx));
if (module.imports().length() > 0) if (!ImportFunctions(cx, importObj, importNames, &imports))
return Fail(cx, "Imports not implemented yet"); return false;
RootedObject exportObj(cx); RootedObject exportObj(cx);
if (!module.dynamicallyLink(cx, moduleObj, heap, imports, exportMap, &exportObj)) if (!module.dynamicallyLink(cx, moduleObj, heap, imports, exportMap, &exportObj))

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

@ -35,6 +35,7 @@ static const uint32_t EncodingVersion = -1; // experimental
// Module section names: // Module section names:
static const char SigSection[] = "sig"; static const char SigSection[] = "sig";
static const char DeclSection[] = "decl"; static const char DeclSection[] = "decl";
static const char ImportSection[] = "import";
static const char ExportSection[] = "export"; static const char ExportSection[] = "export";
static const char CodeSection[] = "code"; static const char CodeSection[] = "code";
static const char EndSection[] = ""; static const char EndSection[] = "";

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

@ -128,8 +128,12 @@ ModuleGenerator::init(UniqueModuleGeneratorData shared, ModuleKind kind)
if (kind == ModuleKind::Wasm) { if (kind == ModuleKind::Wasm) {
numSigs_ = shared_->sigs.length(); numSigs_ = shared_->sigs.length();
module_->numFuncs = shared_->funcSigs.length(); module_->numFuncs = shared_->funcSigs.length();
for (uint32_t i = 0; i < shared_->imports.length(); i++) { module_->globalBytes = AlignBytes(module_->globalBytes, sizeof(void*));
if (!addImport(*shared_->imports[i].sig, shared_->imports[i].globalDataOffset)) for (ModuleImportGeneratorData& import : shared_->imports) {
MOZ_ASSERT(!import.globalDataOffset);
import.globalDataOffset = module_->globalBytes;
module_->globalBytes += Module::SizeOfImportExit;
if (!addImport(*import.sig, import.globalDataOffset))
return false; return false;
} }
} }
@ -302,8 +306,12 @@ ModuleGenerator::funcSig(uint32_t funcIndex) const
} }
bool bool
ModuleGenerator::initImport(uint32_t importIndex, uint32_t sigIndex, uint32_t globalDataOffset) ModuleGenerator::initImport(uint32_t importIndex, uint32_t sigIndex)
{ {
uint32_t globalDataOffset;
if (!allocateGlobalBytes(Module::SizeOfImportExit, sizeof(void*), &globalDataOffset))
return false;
MOZ_ASSERT(isAsmJS()); MOZ_ASSERT(isAsmJS());
MOZ_ASSERT(importIndex == module_->imports.length()); MOZ_ASSERT(importIndex == module_->imports.length());
if (!addImport(sig(sigIndex), globalDataOffset)) if (!addImport(sig(sigIndex), globalDataOffset))

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

@ -48,8 +48,8 @@ struct SlowFunction
typedef Vector<SlowFunction> SlowFunctionVector; typedef Vector<SlowFunction> SlowFunctionVector;
// The ModuleGeneratorData holds all the state shared between the // The ModuleGeneratorData holds all the state shared between the
// ModuleGenerator and ModuleGeneratorThreadView. The ModuleGeneratorData is // ModuleGenerator and ModuleGeneratorThreadView. The ModuleGeneratorData
// encapsulated by ModuleGenerator/ModuleGeneratorThreadView classes which // is encapsulated by ModuleGenerator/ModuleGeneratorThreadView classes which
// present a race-free interface to the code in each thread assuming any given // present a race-free interface to the code in each thread assuming any given
// element is initialized by the ModuleGenerator thread before an index to that // element is initialized by the ModuleGenerator thread before an index to that
// element is written to Bytecode sent to a ModuleGeneratorThreadView thread. // element is written to Bytecode sent to a ModuleGeneratorThreadView thread.
@ -57,15 +57,17 @@ typedef Vector<SlowFunction> SlowFunctionVector;
struct ModuleImportGeneratorData struct ModuleImportGeneratorData
{ {
DeclaredSig* sig; const DeclaredSig* sig;
uint32_t globalDataOffset; uint32_t globalDataOffset;
ModuleImportGeneratorData() : sig(nullptr), globalDataOffset(0) {}
explicit ModuleImportGeneratorData(const DeclaredSig* sig) : sig(sig), globalDataOffset(0) {}
}; };
typedef Vector<ModuleImportGeneratorData, 0, SystemAllocPolicy> ModuleImportGeneratorDataVector; typedef Vector<ModuleImportGeneratorData, 0, SystemAllocPolicy> ModuleImportGeneratorDataVector;
// Global variable descriptor, in asm.js only. struct AsmJSGlobalVariable
{
struct AsmJSGlobalVariable {
ExprType type; ExprType type;
unsigned globalDataOffset; unsigned globalDataOffset;
bool isConst; bool isConst;
@ -159,6 +161,7 @@ class MOZ_STACK_CLASS ModuleGenerator
bool finishTask(IonCompileTask* task); bool finishTask(IonCompileTask* task);
bool addImport(const Sig& sig, uint32_t globalDataOffset); bool addImport(const Sig& sig, uint32_t globalDataOffset);
bool startedFuncDefs() const { return !!threadView_; } bool startedFuncDefs() const { return !!threadView_; }
bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOffset);
public: public:
explicit ModuleGenerator(ExclusiveContext* cx); explicit ModuleGenerator(ExclusiveContext* cx);
@ -171,8 +174,7 @@ class MOZ_STACK_CLASS ModuleGenerator
jit::MacroAssembler& masm() { return masm_; } jit::MacroAssembler& masm() { return masm_; }
const Uint32Vector& funcEntryOffsets() const { return funcEntryOffsets_; } const Uint32Vector& funcEntryOffsets() const { return funcEntryOffsets_; }
// Global data: // asm.js global variables:
bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOffset);
bool allocateGlobalVar(ValType type, bool isConst, uint32_t* index); bool allocateGlobalVar(ValType type, bool isConst, uint32_t* index);
const AsmJSGlobalVariable& globalVar(unsigned index) const { return shared_->globals[index]; } const AsmJSGlobalVariable& globalVar(unsigned index) const { return shared_->globals[index]; }
@ -187,7 +189,7 @@ class MOZ_STACK_CLASS ModuleGenerator
const DeclaredSig& funcSig(uint32_t funcIndex) const; const DeclaredSig& funcSig(uint32_t funcIndex) const;
// Imports: // Imports:
bool initImport(uint32_t importIndex, uint32_t sigIndex, uint32_t globalDataOffset); bool initImport(uint32_t importIndex, uint32_t sigIndex);
uint32_t numImports() const; uint32_t numImports() const;
const ModuleImportGeneratorData& import(uint32_t index) const; const ModuleImportGeneratorData& import(uint32_t index) const;
bool defineImport(uint32_t index, ProfilingOffsets interpExit, ProfilingOffsets jitExit); bool defineImport(uint32_t index, ProfilingOffsets interpExit, ProfilingOffsets jitExit);

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

@ -177,11 +177,9 @@ class Import
return pod.exitGlobalDataOffset_; return pod.exitGlobalDataOffset_;
} }
uint32_t interpExitCodeOffset() const { uint32_t interpExitCodeOffset() const {
MOZ_ASSERT(pod.interpExitCodeOffset_);
return pod.interpExitCodeOffset_; return pod.interpExitCodeOffset_;
} }
uint32_t jitExitCodeOffset() const { uint32_t jitExitCodeOffset() const {
MOZ_ASSERT(pod.jitExitCodeOffset_);
return pod.jitExitCodeOffset_; return pod.jitExitCodeOffset_;
} }

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

@ -95,6 +95,7 @@ enum class WasmAstKind
Export, Export,
Func, Func,
GetLocal, GetLocal,
Import,
Module, Module,
Nop, Nop,
SetLocal SetLocal
@ -213,6 +214,21 @@ class WasmAstFunc : public WasmAstNode
WasmAstExpr* maybeBody() const { return maybeBody_; } WasmAstExpr* maybeBody() const { return maybeBody_; }
}; };
class WasmAstImport : public WasmAstNode
{
TwoByteChars module_;
TwoByteChars func_;
uint32_t sigIndex_;
public:
WasmAstImport(TwoByteChars module, TwoByteChars func, uint32_t sigIndex)
: WasmAstNode(WasmAstKind::Import), module_(module), func_(func), sigIndex_(sigIndex)
{}
TwoByteChars module() const { return module_; }
TwoByteChars func() const { return func_; }
uint32_t sigIndex() const { return sigIndex_; }
};
class WasmAstExport : public WasmAstNode class WasmAstExport : public WasmAstNode
{ {
TwoByteChars name_; TwoByteChars name_;
@ -229,12 +245,14 @@ class WasmAstExport : public WasmAstNode
class WasmAstModule : public WasmAstNode class WasmAstModule : public WasmAstNode
{ {
typedef WasmAstVector<WasmAstFunc*> FuncVector; typedef WasmAstVector<WasmAstFunc*> FuncVector;
typedef WasmAstVector<WasmAstImport*> ImportVector;
typedef WasmAstVector<WasmAstExport*> ExportVector; typedef WasmAstVector<WasmAstExport*> ExportVector;
typedef WasmAstVector<WasmAstSig*> SigVector; typedef WasmAstVector<WasmAstSig*> SigVector;
typedef WasmAstHashMap<WasmAstSig*, uint32_t, WasmAstSig> SigMap; typedef WasmAstHashMap<WasmAstSig*, uint32_t, WasmAstSig> SigMap;
LifoAlloc& lifo_; LifoAlloc& lifo_;
FuncVector funcs_; FuncVector funcs_;
ImportVector imports_;
ExportVector exports_; ExportVector exports_;
SigVector sigs_; SigVector sigs_;
SigMap sigMap_; SigMap sigMap_;
@ -244,6 +262,7 @@ class WasmAstModule : public WasmAstNode
: WasmAstNode(WasmAstKind::Module), : WasmAstNode(WasmAstKind::Module),
lifo_(lifo), lifo_(lifo),
funcs_(lifo), funcs_(lifo),
imports_(lifo),
exports_(lifo), exports_(lifo),
sigs_(lifo), sigs_(lifo),
sigMap_(lifo) sigMap_(lifo)
@ -270,6 +289,12 @@ class WasmAstModule : public WasmAstNode
const FuncVector& funcs() const { const FuncVector& funcs() const {
return funcs_; return funcs_;
} }
const ImportVector& imports() const {
return imports_;
}
bool append(WasmAstImport* imp) {
return imports_.append(imp);
}
bool append(WasmAstExport* exp) { bool append(WasmAstExport* exp) {
return exports_.append(exp); return exports_.append(exp);
} }
@ -294,6 +319,7 @@ class WasmToken
Export, Export,
Func, Func,
GetLocal, GetLocal,
Import,
Integer, Integer,
Local, Local,
Module, Module,
@ -496,14 +522,12 @@ class WasmTokenStream
if (consume(end_, MOZ_UTF16("32"))) { if (consume(end_, MOZ_UTF16("32"))) {
if (consume(end_, MOZ_UTF16(".const"))) if (consume(end_, MOZ_UTF16(".const")))
return WasmToken(WasmToken::Const, ValType::F32, begin, cur_); return WasmToken(WasmToken::Const, ValType::F32, begin, cur_);
else return WasmToken(WasmToken::ValueType, ValType::F32, begin, cur_);
return WasmToken(WasmToken::ValueType, ValType::F32, begin, cur_);
} }
if (consume(end_, MOZ_UTF16("64"))) { if (consume(end_, MOZ_UTF16("64"))) {
if (consume(end_, MOZ_UTF16(".const"))) if (consume(end_, MOZ_UTF16(".const")))
return WasmToken(WasmToken::Const, ValType::F64, begin, cur_); return WasmToken(WasmToken::Const, ValType::F64, begin, cur_);
else return WasmToken(WasmToken::ValueType, ValType::F64, begin, cur_);
return WasmToken(WasmToken::ValueType, ValType::F64, begin, cur_);
} }
break; break;
@ -516,15 +540,15 @@ class WasmTokenStream
if (consume(end_, MOZ_UTF16("32"))) { if (consume(end_, MOZ_UTF16("32"))) {
if (consume(end_, MOZ_UTF16(".const"))) if (consume(end_, MOZ_UTF16(".const")))
return WasmToken(WasmToken::Const, ValType::I32, begin, cur_); return WasmToken(WasmToken::Const, ValType::I32, begin, cur_);
else return WasmToken(WasmToken::ValueType, ValType::I32, begin, cur_);
return WasmToken(WasmToken::ValueType, ValType::I32, begin, cur_);
} }
if (consume(end_, MOZ_UTF16("64"))) { if (consume(end_, MOZ_UTF16("64"))) {
if (consume(end_, MOZ_UTF16(".const"))) if (consume(end_, MOZ_UTF16(".const")))
return WasmToken(WasmToken::Const, ValType::I64, begin, cur_); return WasmToken(WasmToken::Const, ValType::I64, begin, cur_);
else return WasmToken(WasmToken::ValueType, ValType::I64, begin, cur_);
return WasmToken(WasmToken::ValueType, ValType::I64, begin, cur_);
} }
if (consume(end_, MOZ_UTF16("mport")))
return WasmToken(WasmToken::Import, begin, cur_);
break; break;
case 'l': case 'l':
@ -737,6 +761,30 @@ ParseExprInsideParens(WasmParseContext& c)
} }
} }
static bool
ParseValueType(WasmParseContext& c, WasmAstValTypeVector* vec)
{
WasmToken valueType;
return c.ts.match(WasmToken::ValueType, &valueType, c.error) &&
vec->append(valueType.valueType());
}
static bool
ParseResult(WasmParseContext& c, ExprType* result)
{
if (*result != ExprType::Void) {
c.ts.generateError(c.ts.peek(), c.error);
return false;
}
WasmToken valueType;
if (!c.ts.match(WasmToken::ValueType, &valueType, c.error))
return false;
*result = ToExprType(valueType.valueType());
return true;
}
static WasmAstFunc* static WasmAstFunc*
ParseFunc(WasmParseContext& c, WasmAstModule* module) ParseFunc(WasmParseContext& c, WasmAstModule* module)
{ {
@ -746,44 +794,27 @@ ParseFunc(WasmParseContext& c, WasmAstModule* module)
WasmAstExpr* maybeBody = nullptr; WasmAstExpr* maybeBody = nullptr;
while (c.ts.getIf(WasmToken::OpenParen) && !maybeBody) { while (c.ts.getIf(WasmToken::OpenParen) && !maybeBody) {
WasmToken field = c.ts.get(); WasmToken token = c.ts.get();
switch (token.kind()) {
switch (field.kind()) { case WasmToken::Local:
case WasmToken::Local: { if (!ParseValueType(c, &vars))
WasmToken valueType;
if (!c.ts.match(WasmToken::ValueType, &valueType, c.error))
return nullptr;
if (!vars.append(valueType.valueType()))
return nullptr; return nullptr;
break; break;
} case WasmToken::Param:
case WasmToken::Param: { if (!ParseValueType(c, &args))
WasmToken valueType;
if (!c.ts.match(WasmToken::ValueType, &valueType, c.error))
return nullptr;
if (!args.append(valueType.valueType()))
return nullptr; return nullptr;
break; break;
} case WasmToken::Result:
case WasmToken::Result: { if (!ParseResult(c, &result))
if (result != ExprType::Void) {
c.ts.generateError(field, c.error);
return nullptr; return nullptr;
}
WasmToken valueType;
if (!c.ts.match(WasmToken::ValueType, &valueType, c.error))
return nullptr;
result = ToExprType(valueType.valueType());
break; break;
}
default: default:
c.ts.unget(field); c.ts.unget(token);
maybeBody = ParseExprInsideParens(c); maybeBody = ParseExprInsideParens(c);
if (!maybeBody) if (!maybeBody)
return nullptr; return nullptr;
break; break;
} }
if (!c.ts.match(WasmToken::CloseParen, c.error)) if (!c.ts.match(WasmToken::CloseParen, c.error))
return nullptr; return nullptr;
} }
@ -795,6 +826,46 @@ ParseFunc(WasmParseContext& c, WasmAstModule* module)
return new(c.lifo) WasmAstFunc(sigIndex, Move(vars), maybeBody); return new(c.lifo) WasmAstFunc(sigIndex, Move(vars), maybeBody);
} }
static WasmAstImport*
ParseImport(WasmParseContext& c, WasmAstModule* module)
{
WasmToken moduleName;
if (!c.ts.match(WasmToken::Text, &moduleName, c.error))
return nullptr;
WasmToken funcName;
if (!c.ts.match(WasmToken::Text, &funcName, c.error))
return nullptr;
WasmAstValTypeVector args(c.lifo);
ExprType result = ExprType::Void;
while (c.ts.getIf(WasmToken::OpenParen)) {
WasmToken token = c.ts.get();
switch (token.kind()) {
case WasmToken::Param:
if (!ParseValueType(c, &args))
return nullptr;
break;
case WasmToken::Result:
if (!ParseResult(c, &result))
return nullptr;
break;
default:
c.ts.generateError(token, c.error);
return nullptr;
}
if (!c.ts.match(WasmToken::CloseParen, c.error))
return nullptr;
}
uint32_t sigIndex;
if (!module->declare(WasmAstSig(Move(args), result), &sigIndex))
return nullptr;
return new(c.lifo) WasmAstImport(moduleName.text(), funcName.text(), sigIndex);
}
static WasmAstExport* static WasmAstExport*
ParseExport(WasmParseContext& c) ParseExport(WasmParseContext& c)
{ {
@ -827,6 +898,12 @@ TextToAst(const char16_t* text, LifoAlloc& lifo, UniqueChars* error)
WasmToken section = c.ts.get(); WasmToken section = c.ts.get();
switch (section.kind()) { switch (section.kind()) {
case WasmToken::Import: {
WasmAstImport* imp = ParseImport(c, module);
if (!imp || !module->append(imp))
return nullptr;
break;
}
case WasmToken::Export: { case WasmToken::Export: {
WasmAstExport* exp = ParseExport(c); WasmAstExport* exp = ParseExport(c);
if (!exp || !module->append(exp)) if (!exp || !module->append(exp))
@ -933,7 +1010,7 @@ EncodeExpr(Encoder& e, WasmAstExpr& expr)
static bool static bool
EncodeSignatureSection(Encoder& e, WasmAstModule& module) EncodeSignatureSection(Encoder& e, WasmAstModule& module)
{ {
if (module.funcs().empty()) if (module.sigs().empty())
return true; return true;
if (!e.writeCString(SigSection)) if (!e.writeCString(SigSection))
@ -988,6 +1065,57 @@ EncodeDeclarationSection(Encoder& e, WasmAstModule& module)
return true; return true;
} }
static bool
EncodeImport(Encoder& e, WasmAstImport& imp)
{
if (!e.writeCString(FuncSubsection))
return false;
if (!e.writeVarU32(imp.sigIndex()))
return false;
UniqueChars moduleChars(JS::CharsToNewUTF8CharsZ(nullptr, imp.module()).c_str());
if (!moduleChars)
return false;
if (!e.writeCString(moduleChars.get()))
return false;
UniqueChars funcChars(JS::CharsToNewUTF8CharsZ(nullptr, imp.func()).c_str());
if (!funcChars)
return false;
if (!e.writeCString(funcChars.get()))
return false;
return true;
}
static bool
EncodeImportSection(Encoder& e, WasmAstModule& module)
{
if (module.imports().empty())
return true;
if (!e.writeCString(ImportSection))
return false;
size_t offset;
if (!e.startSection(&offset))
return false;
if (!e.writeVarU32(module.imports().length()))
return false;
for (WasmAstImport* imp : module.imports()) {
if (!EncodeImport(e, *imp))
return false;
}
e.finishSection(offset);
return true;
}
static bool static bool
EncodeExport(Encoder& e, WasmAstExport& exp) EncodeExport(Encoder& e, WasmAstExport& exp)
{ {
@ -1109,6 +1237,9 @@ AstToBinary(WasmAstModule& module)
if (!EncodeDeclarationSection(e, module)) if (!EncodeDeclarationSection(e, module))
return nullptr; return nullptr;
if (!EncodeImportSection(e, module))
return nullptr;
if (!EncodeExportSection(e, module)) if (!EncodeExportSection(e, module))
return nullptr; return nullptr;

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

@ -95,6 +95,37 @@ wasmEvalText('(module (func (param f64)))');
assertErrorMessage(() => wasmEvalText('(module (func (param i64)))'), TypeError, /NYI/); assertErrorMessage(() => wasmEvalText('(module (func (param i64)))'), TypeError, /NYI/);
assertErrorMessage(() => wasmEvalText('(module (func (result i64)))'), TypeError, /NYI/); assertErrorMessage(() => wasmEvalText('(module (func (result i64)))'), TypeError, /NYI/);
// ----------------------------------------------------------------------------
// imports
assertErrorMessage(() => wasmEvalText('(module (import "a" "b"))', 1), Error, /Second argument, if present, must be an Object/);
assertErrorMessage(() => wasmEvalText('(module (import "a" "b"))', null), Error, /Second argument, if present, must be an Object/);
const noImportObj = /no import object given/;
const notObject = /import object field is not an Object/;
const notFunction = /import object field is not a Function/;
var code = '(module (import "a" "b"))';
assertErrorMessage(() => wasmEvalText(code), TypeError, noImportObj);
assertErrorMessage(() => wasmEvalText(code, {}), TypeError, notObject);
assertErrorMessage(() => wasmEvalText(code, {a:1}), TypeError, notObject);
assertErrorMessage(() => wasmEvalText(code, {a:{}}), TypeError, notFunction);
assertErrorMessage(() => wasmEvalText(code, {a:{b:1}}), TypeError, notFunction);
wasmEvalText(code, {a:{b:()=>{}}});
var code = '(module (import "" "b"))';
assertErrorMessage(() => wasmEvalText(code), TypeError, /module name cannot be empty/);
var code = '(module (import "a" ""))';
assertErrorMessage(() => wasmEvalText(code), TypeError, noImportObj);
assertErrorMessage(() => wasmEvalText(code, {}), TypeError, notFunction);
assertErrorMessage(() => wasmEvalText(code, {a:1}), TypeError, notFunction);
wasmEvalText(code, {a:()=>{}});
var code = '(module (import "a" "") (import "b" "c") (import "c" ""))';
assertErrorMessage(() => wasmEvalText(code, {a:()=>{}, b:{c:()=>{}}, c:{}}), TypeError, notFunction);
wasmEvalText(code, {a:()=>{}, b:{c:()=>{}}, c:()=>{}});
// ---------------------------------------------------------------------------- // ----------------------------------------------------------------------------
// locals // locals

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

@ -18,8 +18,10 @@ const ver3 = 0xff;
// Section names // Section names
const sigSectionStr = "sig"; const sigSectionStr = "sig";
const declSectionStr = "decl"; const declSectionStr = "decl";
const importSectionStr = "import";
const exportSectionStr = "export"; const exportSectionStr = "export";
const codeSectionStr = "code"; const codeSectionStr = "code";
const funcSubsectionStr = "func";
const magicError = /failed to match magic number/; const magicError = /failed to match magic number/;
const versionError = /failed to match binary version/; const versionError = /failed to match binary version/;
@ -36,7 +38,7 @@ const B32x4Code = 6;
const VoidCode = 7; const VoidCode = 7;
function toBuf(array) { function toBuf(array) {
for (var b of array) for (let b of array)
assertEq(b < 256, true); assertEq(b < 256, true);
return Uint8Array.from(array).buffer; return Uint8Array.from(array).buffer;
} }
@ -66,7 +68,7 @@ assertEq(Object.getOwnPropertyNames(o).length, 0);
assertErrorMessage(() => wasmEval(toBuf(moduleHeaderThen(1))), TypeError, sectionError); assertErrorMessage(() => wasmEval(toBuf(moduleHeaderThen(1))), TypeError, sectionError);
assertErrorMessage(() => wasmEval(toBuf(moduleHeaderThen(0, 1))), TypeError, extraError); assertErrorMessage(() => wasmEval(toBuf(moduleHeaderThen(0, 1))), TypeError, extraError);
function sectionName(name) { function cstring(name) {
return (name + '\0').split('').map(c => c.charCodeAt(0)); return (name + '\0').split('').map(c => c.charCodeAt(0));
} }
@ -78,8 +80,8 @@ function sectionLength(length) {
function moduleWithSections(sectionArray) { function moduleWithSections(sectionArray) {
var bytes = moduleHeaderThen(); var bytes = moduleHeaderThen();
for (section of sectionArray) { for (let section of sectionArray) {
bytes.push(...sectionName(section.name)); bytes.push(...cstring(section.name));
bytes.push(...sectionLength(section.body.length)); bytes.push(...sectionLength(section.body.length));
bytes.push(...section.body); bytes.push(...section.body);
} }
@ -90,15 +92,53 @@ function moduleWithSections(sectionArray) {
function sigSection(sigs) { function sigSection(sigs) {
var body = []; var body = [];
body.push(...varU32(sigs.length)); body.push(...varU32(sigs.length));
for (var sig of sigs) { for (let sig of sigs) {
body.push(...varU32(sig.args.length)); body.push(...varU32(sig.args.length));
body.push(...varU32(sig.ret)); body.push(...varU32(sig.ret));
for (var arg of sig.args) for (let arg of sig.args)
body.push(...varU32(arg)); body.push(...varU32(arg));
} }
return { name: sigSectionStr, body }; return { name: sigSectionStr, body };
} }
function declSection(decls) {
var body = [];
body.push(...varU32(decls.length));
for (let decl of decls)
body.push(...varU32(decl));
return { name: declSectionStr, body };
}
function codeSection(funcs) {
var body = [];
body.push(...varU32(funcs.length));
for (let func of funcs) {
body.push(...cstring(funcSubsectionStr));
var locals = varU32(func.locals.length);
for (let local of func.locals)
locals.push(...varU32(local));
body.push(...sectionLength(locals.length + func.body.length));
body = body.concat(locals, func.body);
}
return { name: codeSectionStr, body };
}
function importSection(imports) {
var body = [];
body.push(...varU32(imports.length));
for (let imp of imports) {
body.push(...cstring(funcSubsectionStr));
body.push(...varU32(imp.sigIndex));
body.push(...cstring(imp.module));
body.push(...cstring(imp.func));
}
return { name: importSectionStr, body };
}
const trivialSigSection = sigSection([{args:[], ret:VoidCode}]);
const trivialDeclSection = declSection([0]);
const trivialCodeSection = codeSection([{locals:[], body:[0, 0]}]);
assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([{name: sigSectionStr, body: [1]}]))), TypeError); assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([{name: sigSectionStr, body: [1]}]))), TypeError);
assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([{name: sigSectionStr, body: [1, 1, 0]}]))), TypeError); assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([{name: sigSectionStr, body: [1, 1, 0]}]))), TypeError);
@ -109,15 +149,13 @@ wasmEval(toBuf(moduleWithSections([sigSection([{args:[I32Code], ret:VoidCode}])]
assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([{args:[], ret:100}])]))), TypeError, /bad expression type/); assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([{args:[], ret:100}])]))), TypeError, /bad expression type/);
assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([{args:[100], ret:VoidCode}])]))), TypeError, /bad value type/); assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([{args:[100], ret:VoidCode}])]))), TypeError, /bad value type/);
function declSection(decls) {
var body = [];
body.push(...varU32(decls.length));
for (var decl of decls)
body.push(...varU32(decl));
return { name: declSectionStr, body };
}
assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([sigSection([]), declSection([0])]))), TypeError, /signature index out of range/); assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([sigSection([]), declSection([0])]))), TypeError, /signature index out of range/);
assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([sigSection([{args:[], ret:VoidCode}]), declSection([1])]))), TypeError, /signature index out of range/); assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([trivialSigSection, declSection([1])]))), TypeError, /signature index out of range/);
assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([trivialSigSection, declSection([0])]))), TypeError, /fewer function definitions than declarations/);
wasmEval(toBuf(moduleWithSections([trivialSigSection, trivialDeclSection, trivialCodeSection])));
assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([sigSection([{args:[], ret:VoidCode}]), declSection([0])]))), TypeError, /fewer function definitions than declarations/); assertThrowsInstanceOf(() => wasmEval(toBuf(moduleWithSections([trivialSigSection, {name: importSectionStr, body:[]}]))), TypeError);
assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([importSection([{sigIndex:0, module:"a", func:"b"}])]))), TypeError, /signature index out of range/);
assertErrorMessage(() => wasmEval(toBuf(moduleWithSections([trivialSigSection, importSection([{sigIndex:1, module:"a", func:"b"}])]))), TypeError, /signature index out of range/);
wasmEval(toBuf(moduleWithSections([trivialSigSection, importSection([])])));
wasmEval(toBuf(moduleWithSections([trivialSigSection, importSection([{sigIndex:0, module:"a", func:""}])])), {a:()=>{}});