static bool GetFunctionExport(JSContext* cx, HandleWasmInstanceObject instanceObj, Handle<FunctionVector> funcImports, const Export& exp, MutableHandleValue val) { if (exp.funcIndex() < funcImports.length() && IsExportedWasmFunction(funcImports[exp.funcIndex()])) { val.setObject(*funcImports[exp.funcIndex()]); return true; } RootedFunction fun(cx); if (!instanceObj->getExportedFunction(cx, instanceObj, exp.funcIndex(), &fun)) return false; val.setObject(*fun); return true; }
bool Compartment::registerInstance(JSContext* cx, HandleWasmInstanceObject instanceObj) { Instance& instance = instanceObj->instance(); MOZ_ASSERT(this == &instance.compartment()->wasm); if (!instance.ensureProfilingState(cx, profilingEnabled_)) return false; size_t index; if (BinarySearchIf(instances_, 0, instances_.length(), InstanceComparator(instance), &index)) MOZ_CRASH("duplicate registration"); { AutoMutateInstances guard(*this); if (!instances_.insert(instances_.begin() + index, &instance)) { ReportOutOfMemory(cx); return false; } } Debugger::onNewWasmInstance(cx, instanceObj); return true; }
static bool CreateExportObject(JSContext* cx, HandleWasmInstanceObject instanceObj, MutableHandleWasmTableObject tableObj, HandleWasmMemoryObject memoryObj, const ValVector& globalImports, const ExportVector& exports, MutableHandleObject exportObj) { const Instance& instance = instanceObj->instance(); const Metadata& metadata = instance.metadata(); if (metadata.isAsmJS() && exports.length() == 1 && strlen(exports[0].fieldName()) == 0) { RootedFunction fun(cx); if (!instanceObj->getExportedFunction(cx, instanceObj, exports[0].funcIndex(), &fun)) return false; exportObj.set(fun); return true; } exportObj.set(JS_NewPlainObject(cx)); if (!exportObj) return false; for (const Export& exp : exports) { JSAtom* atom = AtomizeUTF8Chars(cx, exp.fieldName(), strlen(exp.fieldName())); if (!atom) return false; RootedId id(cx, AtomToId(atom)); RootedValue val(cx); switch (exp.kind()) { case DefinitionKind::Function: { RootedFunction fun(cx); if (!instanceObj->getExportedFunction(cx, instanceObj, exp.funcIndex(), &fun)) return false; val = ObjectValue(*fun); break; } case DefinitionKind::Table: { if (!tableObj) { MOZ_ASSERT(instance.tables().length() == 1); tableObj.set(WasmTableObject::create(cx, *instance.tables()[0])); if (!tableObj) return false; } val = ObjectValue(*tableObj); break; } case DefinitionKind::Memory: { if (metadata.assumptions.newFormat) val = ObjectValue(*memoryObj); else val = ObjectValue(memoryObj->buffer()); break; } case DefinitionKind::Global: { if (!ExportGlobalValue(cx, metadata.globals, exp.globalIndex(), globalImports, &val)) return false; break; } } if (!JS_DefinePropertyById(cx, exportObj, id, val, JSPROP_ENUMERATE)) return false; } return true; }
bool Module::initElems(JSContext* cx, HandleWasmInstanceObject instanceObj, const ValVector& globalImports, HandleWasmTableObject tableObj) const { Instance& instance = instanceObj->instance(); const CodeSegment& codeSegment = instance.codeSegment(); const SharedTableVector& tables = instance.tables(); // Initialize tables that have a WasmTableObject first, so that this // initialization can be done atomically. if (tableObj && !tableObj->initialized() && !tableObj->init(cx, instanceObj)) return false; // Initialize all remaining Tables that do not have objects. for (const SharedTable& table : tables) { if (!table->initialized()) table->init(codeSegment); } // Now that all tables have been initialized, write elements. Vector<uint32_t> prevEnds(cx); if (!prevEnds.appendN(0, tables.length())) return false; for (const ElemSegment& seg : elemSegments_) { Table& table = *tables[seg.tableIndex]; uint32_t offset; switch (seg.offset.kind()) { case InitExpr::Kind::Constant: { offset = seg.offset.val().i32(); break; } case InitExpr::Kind::GetGlobal: { const GlobalDesc& global = metadata_->globals[seg.offset.globalIndex()]; offset = globalImports[global.importIndex()].i32(); break; } } uint32_t& prevEnd = prevEnds[seg.tableIndex]; if (offset < prevEnd) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_FAIL, "elem segments must be disjoint and ordered"); return false; } uint32_t tableLength = instance.metadata().tables[seg.tableIndex].initial; if (offset > tableLength || tableLength - offset < seg.elems.length()) { JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_WASM_FAIL, "element segment does not fit"); return false; } if (tableObj) { MOZ_ASSERT(seg.tableIndex == 0); for (uint32_t i = 0; i < seg.elems.length(); i++) { if (!tableObj->setInstance(cx, offset + i, instanceObj)) return false; } } for (uint32_t i = 0; i < seg.elems.length(); i++) table.array()[offset + i] = codeSegment.code() + seg.elems[i]; prevEnd = offset + seg.elems.length(); } return true; }
bool Module::instantiate(JSContext* cx, Handle<FunctionVector> funcImports, HandleWasmMemoryObject memImport, HandleWasmInstanceObject instanceObj) const { MOZ_ASSERT(funcImports.length() == metadata_->funcImports.length()); RootedWasmMemoryObject memory(cx, memImport); if (!instantiateMemory(cx, &memory)) return false; auto cs = CodeSegment::create(cx, code_, linkData_, *metadata_, memory); if (!cs) return false; SharedTableVector tables; if (!instantiateTable(cx, *cs, &tables)) return false; // To support viewing the source of an instance (Instance::createText), the // instance must hold onto a ref of the bytecode (keeping it alive). This // wastes memory for most users, so we try to only save the source when a // developer actually cares: when the compartment is debuggable (which is // true when the web console is open) or a names section is present (since // this going to be stripped for non-developer builds). const ShareableBytes* maybeBytecode = nullptr; if (cx->compartment()->isDebuggee() || !metadata_->funcNames.empty()) maybeBytecode = bytecode_.get(); // Create the Instance, ensuring that it is traceable via 'instanceObj' // before any GC can occur and invalidate the pointers stored in global // memory. { auto instance = cx->make_unique<Instance>(Move(cs), *metadata_, maybeBytecode, memory, Move(tables), funcImports); if (!instance) return false; instanceObj->init(Move(instance)); } // Create the export object. RootedObject exportObj(cx); if (!CreateExportObject(cx, instanceObj, memory, exports_, &exportObj)) return false; instanceObj->initExportsObject(exportObj); JSAtom* atom = Atomize(cx, ExportField, strlen(ExportField)); if (!atom) return false; RootedId id(cx, AtomToId(atom)); RootedValue val(cx, ObjectValue(*exportObj)); if (!JS_DefinePropertyById(cx, instanceObj, id, val, JSPROP_ENUMERATE)) return false; // Done! Notify the Debugger of the new Instance. Debugger::onNewWasmInstance(cx, instanceObj); // Call the start function, if there's one. By specification, it does not // take any arguments nor does it return a value, so just create a dummy // arguments object. if (metadata_->hasStartFunction()) { FixedInvokeArgs<0> args(cx); if (!instanceObj->instance().callExport(cx, metadata_->startFuncExportIndex(), args)) return false; } return true; }
static bool CreateExportObject(JSContext* cx, HandleWasmInstanceObject instanceObj, HandleWasmMemoryObject memoryObj, const ExportVector& exports, MutableHandleObject exportObj) { const Instance& instance = instanceObj->instance(); const Metadata& metadata = instance.metadata(); const SharedTableVector& tables = instance.tables(); if (metadata.isAsmJS() && exports.length() == 1 && strlen(exports[0].fieldName()) == 0) { exportObj.set(NewExportedFunction(cx, instanceObj, 0)); return !!exportObj; } exportObj.set(JS_NewPlainObject(cx)); if (!exportObj) return false; Rooted<ValueVector> vals(cx, ValueVector(cx)); for (size_t i = 0; i < metadata.funcExports.length(); i++) { JSFunction* fun = NewExportedFunction(cx, instanceObj, i); if (!fun || !vals.append(ObjectValue(*fun))) return false; } RootedWasmTableObject tableObj(cx); for (const Export& exp : exports) { JSAtom* atom = AtomizeUTF8Chars(cx, exp.fieldName(), strlen(exp.fieldName())); if (!atom) return false; RootedId id(cx, AtomToId(atom)); RootedValue val(cx); switch (exp.kind()) { case DefinitionKind::Function: val = vals[exp.funcExportIndex()]; break; case DefinitionKind::Table: MOZ_ASSERT(tables.length() == 1); if (!tableObj) { tableObj = WasmTableObject::create(cx, *tables[0]); if (!tableObj) return false; } val = ObjectValue(*tableObj); break; case DefinitionKind::Memory: if (metadata.assumptions.newFormat) val = ObjectValue(*memoryObj); else val = ObjectValue(memoryObj->buffer()); break; } if (!JS_DefinePropertyById(cx, exportObj, id, val, JSPROP_ENUMERATE)) return false; } return true; }
static bool CreateExportObject(JSContext* cx, HandleWasmInstanceObject instanceObj, Handle<FunctionVector> funcImports, HandleWasmTableObject tableObj, HandleWasmMemoryObject memoryObj, const ValVector& globalImports, const ExportVector& exports, MutableHandleObject exportObj) { const Instance& instance = instanceObj->instance(); const Metadata& metadata = instance.metadata(); if (metadata.isAsmJS() && exports.length() == 1 && strlen(exports[0].fieldName()) == 0) { RootedValue val(cx); if (!GetFunctionExport(cx, instanceObj, funcImports, exports[0], &val)) return false; exportObj.set(&val.toObject()); return true; } if (metadata.isAsmJS()) exportObj.set(NewBuiltinClassInstance<PlainObject>(cx)); else exportObj.set(NewObjectWithGivenProto<PlainObject>(cx, nullptr)); if (!exportObj) return false; for (const Export& exp : exports) { JSAtom* atom = AtomizeUTF8Chars(cx, exp.fieldName(), strlen(exp.fieldName())); if (!atom) return false; RootedId id(cx, AtomToId(atom)); RootedValue val(cx); switch (exp.kind()) { case DefinitionKind::Function: if (!GetFunctionExport(cx, instanceObj, funcImports, exp, &val)) return false; break; case DefinitionKind::Table: val = ObjectValue(*tableObj); break; case DefinitionKind::Memory: val = ObjectValue(*memoryObj); break; case DefinitionKind::Global: if (!GetGlobalExport(cx, metadata.globals, exp.globalIndex(), globalImports, &val)) return false; break; } if (!JS_DefinePropertyById(cx, exportObj, id, val, JSPROP_ENUMERATE)) return false; } if (!metadata.isAsmJS()) { if (!JS_FreezeObject(cx, exportObj)) return false; } return true; }
bool Module::initSegments(JSContext* cx, HandleWasmInstanceObject instanceObj, Handle<FunctionVector> funcImports, HandleWasmMemoryObject memoryObj, const ValVector& globalImports) const { Instance& instance = instanceObj->instance(); const SharedTableVector& tables = instance.tables(); // Perform all error checks up front so that this function does not perform // partial initialization if an error is reported. for (const ElemSegment& seg : elemSegments_) { uint32_t numElems = seg.elemCodeRangeIndices.length(); if (!numElems) continue; uint32_t tableLength = tables[seg.tableIndex]->length(); uint32_t offset = EvaluateInitExpr(globalImports, seg.offset); if (offset > tableLength || tableLength - offset < numElems) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_FIT, "elem", "table"); return false; } } if (memoryObj) { for (const DataSegment& seg : dataSegments_) { if (!seg.length) continue; uint32_t memoryLength = memoryObj->buffer().byteLength(); uint32_t offset = EvaluateInitExpr(globalImports, seg.offset); if (offset > memoryLength || memoryLength - offset < seg.length) { JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_WASM_BAD_FIT, "data", "memory"); return false; } } } else { MOZ_ASSERT(dataSegments_.empty()); } // Now that initialization can't fail partway through, write data/elem // segments into memories/tables. for (const ElemSegment& seg : elemSegments_) { Table& table = *tables[seg.tableIndex]; uint32_t offset = EvaluateInitExpr(globalImports, seg.offset); bool profilingEnabled = instance.code().profilingEnabled(); const CodeRangeVector& codeRanges = metadata().codeRanges; uint8_t* codeBase = instance.codeBase(); for (uint32_t i = 0; i < seg.elemCodeRangeIndices.length(); i++) { uint32_t funcIndex = seg.elemFuncIndices[i]; if (funcIndex < funcImports.length() && IsExportedWasmFunction(funcImports[funcIndex])) { MOZ_ASSERT(!metadata().isAsmJS()); MOZ_ASSERT(!table.isTypedFunction()); HandleFunction f = funcImports[funcIndex]; WasmInstanceObject* exportInstanceObj = ExportedFunctionToInstanceObject(f); const CodeRange& cr = exportInstanceObj->getExportedFunctionCodeRange(f); Instance& exportInstance = exportInstanceObj->instance(); table.set(offset + i, exportInstance.codeBase() + cr.funcTableEntry(), exportInstance); } else { const CodeRange& cr = codeRanges[seg.elemCodeRangeIndices[i]]; uint32_t entryOffset = table.isTypedFunction() ? profilingEnabled ? cr.funcProfilingEntry() : cr.funcNonProfilingEntry() : cr.funcTableEntry(); table.set(offset + i, codeBase + entryOffset, instance); } } } if (memoryObj) { uint8_t* memoryBase = memoryObj->buffer().dataPointerEither().unwrap(/* memcpy */); for (const DataSegment& seg : dataSegments_) { MOZ_ASSERT(seg.bytecodeOffset <= bytecode_->length()); MOZ_ASSERT(seg.length <= bytecode_->length() - seg.bytecodeOffset); uint32_t offset = EvaluateInitExpr(globalImports, seg.offset); memcpy(memoryBase + offset, bytecode_->begin() + seg.bytecodeOffset, seg.length); } } return true; }