Ejemplo n.º 1
0
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;
}
Ejemplo n.º 2
0
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;
}
Ejemplo n.º 3
0
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;
}
Ejemplo n.º 4
0
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;
}
Ejemplo n.º 5
0
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;
}
Ejemplo n.º 6
0
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;
}
Ejemplo n.º 7
0
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;
}
Ejemplo n.º 8
0
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;
}