зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1243252 - Baldr: add import section (r=bbouvier)
--HG-- extra : commitid : 64BPCvQPUL7 extra : rebase_source : 5cc46dd463ea6be7795c4787a99460fa9d19a4cc
This commit is contained in:
Родитель
38c56107c6
Коммит
c3fde7cc15
|
@ -2084,10 +2084,7 @@ class MOZ_STACK_CLASS ModuleValidator
|
|||
uint32_t sigIndex;
|
||||
if (!declareSig(Move(sig), &sigIndex))
|
||||
return false;
|
||||
uint32_t globalDataOffset;
|
||||
if (!mg_.allocateGlobalBytes(Module::SizeOfImportExit, sizeof(void*), &globalDataOffset))
|
||||
return false;
|
||||
if (!mg_.initImport(*importIndex, sigIndex, globalDataOffset))
|
||||
if (!mg_.initImport(*importIndex, sigIndex))
|
||||
return false;
|
||||
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))
|
||||
return false;
|
||||
|
||||
if (!v.isObject() || !v.toObject().is<JSFunction>())
|
||||
if (!IsFunctionObject(v))
|
||||
return LinkFail(cx, "FFI imports must be functions");
|
||||
|
||||
ffis[global.ffiIndex()].set(&v.toObject().as<JSFunction>());
|
||||
|
|
|
@ -314,6 +314,21 @@ DecodeSignatureSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init)
|
|||
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
|
||||
DecodeDeclarationSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init)
|
||||
{
|
||||
|
@ -332,14 +347,8 @@ DecodeDeclarationSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init)
|
|||
return false;
|
||||
|
||||
for (uint32_t i = 0; i < numDecls; i++) {
|
||||
uint32_t sigIndex;
|
||||
if (!d.readVarU32(&sigIndex))
|
||||
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 (!DecodeSignatureIndex(cx, d, *init, &init->funcSigs[i]))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!d.finishSection(sectionStart))
|
||||
|
@ -348,6 +357,81 @@ DecodeDeclarationSection(JSContext* cx, Decoder& d, ModuleGeneratorData* init)
|
|||
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(§ionStart))
|
||||
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
|
||||
DecodeExport(JSContext* cx, Decoder& d, ModuleGenerator& mg, ExportMap* exportMap)
|
||||
{
|
||||
|
@ -518,7 +602,8 @@ DecodeUnknownSection(JSContext* cx, Decoder& d)
|
|||
|
||||
static bool
|
||||
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);
|
||||
|
||||
|
@ -539,6 +624,9 @@ DecodeModule(JSContext* cx, UniqueChars filename, const uint8_t* bytes, uint32_t
|
|||
if (!DecodeDeclarationSection(cx, d, init.get()))
|
||||
return false;
|
||||
|
||||
if (!DecodeImportSection(cx, d, init.get(), importNames))
|
||||
return false;
|
||||
|
||||
ModuleGenerator mg(cx);
|
||||
if (!mg.init(Move(init)))
|
||||
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
|
||||
SupportsWasm(JSContext* cx)
|
||||
|
@ -636,15 +766,6 @@ WasmEval(JSContext* cx, unsigned argc, Value* vp)
|
|||
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>());
|
||||
const uint8_t* bytes = code->dataPointer();
|
||||
uint32_t length = code->byteLength();
|
||||
|
@ -656,9 +777,23 @@ WasmEval(JSContext* cx, unsigned argc, Value* vp)
|
|||
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;
|
||||
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())
|
||||
ReportOutOfMemory(cx);
|
||||
return false;
|
||||
|
@ -671,8 +806,8 @@ WasmEval(JSContext* cx, unsigned argc, Value* vp)
|
|||
return Fail(cx, "Heap not implemented yet");
|
||||
|
||||
Rooted<FunctionVector> imports(cx, FunctionVector(cx));
|
||||
if (module.imports().length() > 0)
|
||||
return Fail(cx, "Imports not implemented yet");
|
||||
if (!ImportFunctions(cx, importObj, importNames, &imports))
|
||||
return false;
|
||||
|
||||
RootedObject exportObj(cx);
|
||||
if (!module.dynamicallyLink(cx, moduleObj, heap, imports, exportMap, &exportObj))
|
||||
|
|
|
@ -35,6 +35,7 @@ static const uint32_t EncodingVersion = -1; // experimental
|
|||
// Module section names:
|
||||
static const char SigSection[] = "sig";
|
||||
static const char DeclSection[] = "decl";
|
||||
static const char ImportSection[] = "import";
|
||||
static const char ExportSection[] = "export";
|
||||
static const char CodeSection[] = "code";
|
||||
static const char EndSection[] = "";
|
||||
|
|
|
@ -128,8 +128,12 @@ ModuleGenerator::init(UniqueModuleGeneratorData shared, ModuleKind kind)
|
|||
if (kind == ModuleKind::Wasm) {
|
||||
numSigs_ = shared_->sigs.length();
|
||||
module_->numFuncs = shared_->funcSigs.length();
|
||||
for (uint32_t i = 0; i < shared_->imports.length(); i++) {
|
||||
if (!addImport(*shared_->imports[i].sig, shared_->imports[i].globalDataOffset))
|
||||
module_->globalBytes = AlignBytes(module_->globalBytes, sizeof(void*));
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
@ -302,8 +306,12 @@ ModuleGenerator::funcSig(uint32_t funcIndex) const
|
|||
}
|
||||
|
||||
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(importIndex == module_->imports.length());
|
||||
if (!addImport(sig(sigIndex), globalDataOffset))
|
||||
|
|
|
@ -48,8 +48,8 @@ struct SlowFunction
|
|||
typedef Vector<SlowFunction> SlowFunctionVector;
|
||||
|
||||
// The ModuleGeneratorData holds all the state shared between the
|
||||
// ModuleGenerator and ModuleGeneratorThreadView. The ModuleGeneratorData is
|
||||
// encapsulated by ModuleGenerator/ModuleGeneratorThreadView classes which
|
||||
// ModuleGenerator and ModuleGeneratorThreadView. The ModuleGeneratorData
|
||||
// is encapsulated by ModuleGenerator/ModuleGeneratorThreadView classes which
|
||||
// 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 written to Bytecode sent to a ModuleGeneratorThreadView thread.
|
||||
|
@ -57,15 +57,17 @@ typedef Vector<SlowFunction> SlowFunctionVector;
|
|||
|
||||
struct ModuleImportGeneratorData
|
||||
{
|
||||
DeclaredSig* sig;
|
||||
const DeclaredSig* sig;
|
||||
uint32_t globalDataOffset;
|
||||
|
||||
ModuleImportGeneratorData() : sig(nullptr), globalDataOffset(0) {}
|
||||
explicit ModuleImportGeneratorData(const DeclaredSig* sig) : sig(sig), globalDataOffset(0) {}
|
||||
};
|
||||
|
||||
typedef Vector<ModuleImportGeneratorData, 0, SystemAllocPolicy> ModuleImportGeneratorDataVector;
|
||||
|
||||
// Global variable descriptor, in asm.js only.
|
||||
|
||||
struct AsmJSGlobalVariable {
|
||||
struct AsmJSGlobalVariable
|
||||
{
|
||||
ExprType type;
|
||||
unsigned globalDataOffset;
|
||||
bool isConst;
|
||||
|
@ -159,6 +161,7 @@ class MOZ_STACK_CLASS ModuleGenerator
|
|||
bool finishTask(IonCompileTask* task);
|
||||
bool addImport(const Sig& sig, uint32_t globalDataOffset);
|
||||
bool startedFuncDefs() const { return !!threadView_; }
|
||||
bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOffset);
|
||||
|
||||
public:
|
||||
explicit ModuleGenerator(ExclusiveContext* cx);
|
||||
|
@ -171,8 +174,7 @@ class MOZ_STACK_CLASS ModuleGenerator
|
|||
jit::MacroAssembler& masm() { return masm_; }
|
||||
const Uint32Vector& funcEntryOffsets() const { return funcEntryOffsets_; }
|
||||
|
||||
// Global data:
|
||||
bool allocateGlobalBytes(uint32_t bytes, uint32_t align, uint32_t* globalDataOffset);
|
||||
// asm.js global variables:
|
||||
bool allocateGlobalVar(ValType type, bool isConst, uint32_t* 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;
|
||||
|
||||
// Imports:
|
||||
bool initImport(uint32_t importIndex, uint32_t sigIndex, uint32_t globalDataOffset);
|
||||
bool initImport(uint32_t importIndex, uint32_t sigIndex);
|
||||
uint32_t numImports() const;
|
||||
const ModuleImportGeneratorData& import(uint32_t index) const;
|
||||
bool defineImport(uint32_t index, ProfilingOffsets interpExit, ProfilingOffsets jitExit);
|
||||
|
|
|
@ -177,11 +177,9 @@ class Import
|
|||
return pod.exitGlobalDataOffset_;
|
||||
}
|
||||
uint32_t interpExitCodeOffset() const {
|
||||
MOZ_ASSERT(pod.interpExitCodeOffset_);
|
||||
return pod.interpExitCodeOffset_;
|
||||
}
|
||||
uint32_t jitExitCodeOffset() const {
|
||||
MOZ_ASSERT(pod.jitExitCodeOffset_);
|
||||
return pod.jitExitCodeOffset_;
|
||||
}
|
||||
|
||||
|
|
|
@ -95,6 +95,7 @@ enum class WasmAstKind
|
|||
Export,
|
||||
Func,
|
||||
GetLocal,
|
||||
Import,
|
||||
Module,
|
||||
Nop,
|
||||
SetLocal
|
||||
|
@ -213,6 +214,21 @@ class WasmAstFunc : public WasmAstNode
|
|||
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
|
||||
{
|
||||
TwoByteChars name_;
|
||||
|
@ -229,12 +245,14 @@ class WasmAstExport : public WasmAstNode
|
|||
class WasmAstModule : public WasmAstNode
|
||||
{
|
||||
typedef WasmAstVector<WasmAstFunc*> FuncVector;
|
||||
typedef WasmAstVector<WasmAstImport*> ImportVector;
|
||||
typedef WasmAstVector<WasmAstExport*> ExportVector;
|
||||
typedef WasmAstVector<WasmAstSig*> SigVector;
|
||||
typedef WasmAstHashMap<WasmAstSig*, uint32_t, WasmAstSig> SigMap;
|
||||
|
||||
LifoAlloc& lifo_;
|
||||
FuncVector funcs_;
|
||||
ImportVector imports_;
|
||||
ExportVector exports_;
|
||||
SigVector sigs_;
|
||||
SigMap sigMap_;
|
||||
|
@ -244,6 +262,7 @@ class WasmAstModule : public WasmAstNode
|
|||
: WasmAstNode(WasmAstKind::Module),
|
||||
lifo_(lifo),
|
||||
funcs_(lifo),
|
||||
imports_(lifo),
|
||||
exports_(lifo),
|
||||
sigs_(lifo),
|
||||
sigMap_(lifo)
|
||||
|
@ -270,6 +289,12 @@ class WasmAstModule : public WasmAstNode
|
|||
const FuncVector& funcs() const {
|
||||
return funcs_;
|
||||
}
|
||||
const ImportVector& imports() const {
|
||||
return imports_;
|
||||
}
|
||||
bool append(WasmAstImport* imp) {
|
||||
return imports_.append(imp);
|
||||
}
|
||||
bool append(WasmAstExport* exp) {
|
||||
return exports_.append(exp);
|
||||
}
|
||||
|
@ -294,6 +319,7 @@ class WasmToken
|
|||
Export,
|
||||
Func,
|
||||
GetLocal,
|
||||
Import,
|
||||
Integer,
|
||||
Local,
|
||||
Module,
|
||||
|
@ -496,14 +522,12 @@ class WasmTokenStream
|
|||
if (consume(end_, MOZ_UTF16("32"))) {
|
||||
if (consume(end_, MOZ_UTF16(".const")))
|
||||
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(".const")))
|
||||
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;
|
||||
|
||||
|
@ -516,15 +540,15 @@ class WasmTokenStream
|
|||
if (consume(end_, MOZ_UTF16("32"))) {
|
||||
if (consume(end_, MOZ_UTF16(".const")))
|
||||
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(".const")))
|
||||
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;
|
||||
|
||||
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*
|
||||
ParseFunc(WasmParseContext& c, WasmAstModule* module)
|
||||
{
|
||||
|
@ -746,44 +794,27 @@ ParseFunc(WasmParseContext& c, WasmAstModule* module)
|
|||
|
||||
WasmAstExpr* maybeBody = nullptr;
|
||||
while (c.ts.getIf(WasmToken::OpenParen) && !maybeBody) {
|
||||
WasmToken field = c.ts.get();
|
||||
|
||||
switch (field.kind()) {
|
||||
case WasmToken::Local: {
|
||||
WasmToken valueType;
|
||||
if (!c.ts.match(WasmToken::ValueType, &valueType, c.error))
|
||||
return nullptr;
|
||||
if (!vars.append(valueType.valueType()))
|
||||
WasmToken token = c.ts.get();
|
||||
switch (token.kind()) {
|
||||
case WasmToken::Local:
|
||||
if (!ParseValueType(c, &vars))
|
||||
return nullptr;
|
||||
break;
|
||||
}
|
||||
case WasmToken::Param: {
|
||||
WasmToken valueType;
|
||||
if (!c.ts.match(WasmToken::ValueType, &valueType, c.error))
|
||||
return nullptr;
|
||||
if (!args.append(valueType.valueType()))
|
||||
case WasmToken::Param:
|
||||
if (!ParseValueType(c, &args))
|
||||
return nullptr;
|
||||
break;
|
||||
}
|
||||
case WasmToken::Result: {
|
||||
if (result != ExprType::Void) {
|
||||
c.ts.generateError(field, c.error);
|
||||
case WasmToken::Result:
|
||||
if (!ParseResult(c, &result))
|
||||
return nullptr;
|
||||
}
|
||||
WasmToken valueType;
|
||||
if (!c.ts.match(WasmToken::ValueType, &valueType, c.error))
|
||||
return nullptr;
|
||||
result = ToExprType(valueType.valueType());
|
||||
break;
|
||||
}
|
||||
default:
|
||||
c.ts.unget(field);
|
||||
c.ts.unget(token);
|
||||
maybeBody = ParseExprInsideParens(c);
|
||||
if (!maybeBody)
|
||||
return nullptr;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!c.ts.match(WasmToken::CloseParen, c.error))
|
||||
return nullptr;
|
||||
}
|
||||
|
@ -795,6 +826,46 @@ ParseFunc(WasmParseContext& c, WasmAstModule* module)
|
|||
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*
|
||||
ParseExport(WasmParseContext& c)
|
||||
{
|
||||
|
@ -827,6 +898,12 @@ TextToAst(const char16_t* text, LifoAlloc& lifo, UniqueChars* error)
|
|||
WasmToken section = c.ts.get();
|
||||
|
||||
switch (section.kind()) {
|
||||
case WasmToken::Import: {
|
||||
WasmAstImport* imp = ParseImport(c, module);
|
||||
if (!imp || !module->append(imp))
|
||||
return nullptr;
|
||||
break;
|
||||
}
|
||||
case WasmToken::Export: {
|
||||
WasmAstExport* exp = ParseExport(c);
|
||||
if (!exp || !module->append(exp))
|
||||
|
@ -933,7 +1010,7 @@ EncodeExpr(Encoder& e, WasmAstExpr& expr)
|
|||
static bool
|
||||
EncodeSignatureSection(Encoder& e, WasmAstModule& module)
|
||||
{
|
||||
if (module.funcs().empty())
|
||||
if (module.sigs().empty())
|
||||
return true;
|
||||
|
||||
if (!e.writeCString(SigSection))
|
||||
|
@ -988,6 +1065,57 @@ EncodeDeclarationSection(Encoder& e, WasmAstModule& module)
|
|||
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
|
||||
EncodeExport(Encoder& e, WasmAstExport& exp)
|
||||
{
|
||||
|
@ -1109,6 +1237,9 @@ AstToBinary(WasmAstModule& module)
|
|||
if (!EncodeDeclarationSection(e, module))
|
||||
return nullptr;
|
||||
|
||||
if (!EncodeImportSection(e, module))
|
||||
return nullptr;
|
||||
|
||||
if (!EncodeExportSection(e, module))
|
||||
return nullptr;
|
||||
|
||||
|
|
|
@ -95,6 +95,37 @@ wasmEvalText('(module (func (param f64)))');
|
|||
assertErrorMessage(() => wasmEvalText('(module (func (param 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
|
||||
|
||||
|
|
|
@ -18,8 +18,10 @@ const ver3 = 0xff;
|
|||
// Section names
|
||||
const sigSectionStr = "sig";
|
||||
const declSectionStr = "decl";
|
||||
const importSectionStr = "import";
|
||||
const exportSectionStr = "export";
|
||||
const codeSectionStr = "code";
|
||||
const funcSubsectionStr = "func";
|
||||
|
||||
const magicError = /failed to match magic number/;
|
||||
const versionError = /failed to match binary version/;
|
||||
|
@ -36,7 +38,7 @@ const B32x4Code = 6;
|
|||
const VoidCode = 7;
|
||||
|
||||
function toBuf(array) {
|
||||
for (var b of array)
|
||||
for (let b of array)
|
||||
assertEq(b < 256, true);
|
||||
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(0, 1))), TypeError, extraError);
|
||||
|
||||
function sectionName(name) {
|
||||
function cstring(name) {
|
||||
return (name + '\0').split('').map(c => c.charCodeAt(0));
|
||||
}
|
||||
|
||||
|
@ -78,8 +80,8 @@ function sectionLength(length) {
|
|||
|
||||
function moduleWithSections(sectionArray) {
|
||||
var bytes = moduleHeaderThen();
|
||||
for (section of sectionArray) {
|
||||
bytes.push(...sectionName(section.name));
|
||||
for (let section of sectionArray) {
|
||||
bytes.push(...cstring(section.name));
|
||||
bytes.push(...sectionLength(section.body.length));
|
||||
bytes.push(...section.body);
|
||||
}
|
||||
|
@ -90,15 +92,53 @@ function moduleWithSections(sectionArray) {
|
|||
function sigSection(sigs) {
|
||||
var body = [];
|
||||
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.ret));
|
||||
for (var arg of sig.args)
|
||||
for (let arg of sig.args)
|
||||
body.push(...varU32(arg));
|
||||
}
|
||||
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, 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:[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([{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:()=>{}});
|
||||
|
|
Загрузка…
Ссылка в новой задаче