diff --git a/js/src/asmjs/AsmJS.cpp b/js/src/asmjs/AsmJS.cpp index 085790f91ba3..ab274bfc76c2 100644 --- a/js/src/asmjs/AsmJS.cpp +++ b/js/src/asmjs/AsmJS.cpp @@ -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()) + if (!IsFunctionObject(v)) return LinkFail(cx, "FFI imports must be functions"); ffis[global.ffiIndex()].set(&v.toObject().as()); diff --git a/js/src/asmjs/Wasm.cpp b/js/src/asmjs/Wasm.cpp index c7c9eb6cb2f2..5dbef88f7c27 100644 --- a/js/src/asmjs/Wasm.cpp +++ b/js/src/asmjs/Wasm.cpp @@ -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 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 moduleObj, ExportMap* exportMap) + ImportNameVector* importNames, ExportMap* exportMap, + MutableHandle 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 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())) + 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 code(cx, &args[0].toObject().as()); 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 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 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 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)) diff --git a/js/src/asmjs/WasmBinary.h b/js/src/asmjs/WasmBinary.h index 4c9a5fd94750..4a8e85ffd00e 100644 --- a/js/src/asmjs/WasmBinary.h +++ b/js/src/asmjs/WasmBinary.h @@ -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[] = ""; diff --git a/js/src/asmjs/WasmGenerator.cpp b/js/src/asmjs/WasmGenerator.cpp index 505a9899bb08..39fd1817dded 100644 --- a/js/src/asmjs/WasmGenerator.cpp +++ b/js/src/asmjs/WasmGenerator.cpp @@ -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)) diff --git a/js/src/asmjs/WasmGenerator.h b/js/src/asmjs/WasmGenerator.h index 508982ccf4b2..cc1be7e8d358 100644 --- a/js/src/asmjs/WasmGenerator.h +++ b/js/src/asmjs/WasmGenerator.h @@ -48,8 +48,8 @@ struct SlowFunction typedef Vector 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 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 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); diff --git a/js/src/asmjs/WasmModule.h b/js/src/asmjs/WasmModule.h index 4f19ae13e8be..9352ab66f8fe 100644 --- a/js/src/asmjs/WasmModule.h +++ b/js/src/asmjs/WasmModule.h @@ -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_; } diff --git a/js/src/asmjs/WasmText.cpp b/js/src/asmjs/WasmText.cpp index df2a3e7ba4c6..ddc8e2a708f3 100644 --- a/js/src/asmjs/WasmText.cpp +++ b/js/src/asmjs/WasmText.cpp @@ -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 FuncVector; + typedef WasmAstVector ImportVector; typedef WasmAstVector ExportVector; typedef WasmAstVector SigVector; typedef WasmAstHashMap 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; diff --git a/js/src/jit-test/tests/wasm/basic.js b/js/src/jit-test/tests/wasm/basic.js index 27969eee93e5..46ad5f5b04e4 100644 --- a/js/src/jit-test/tests/wasm/basic.js +++ b/js/src/jit-test/tests/wasm/basic.js @@ -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 diff --git a/js/src/jit-test/tests/wasm/binary.js b/js/src/jit-test/tests/wasm/binary.js index 99cfaaaa5a3d..719b71d9d1fa 100644 --- a/js/src/jit-test/tests/wasm/binary.js +++ b/js/src/jit-test/tests/wasm/binary.js @@ -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:()=>{}});