Ejemplo n.º 1
0
void BSONInfo::resolve(JSContext* cx, JS::HandleObject obj, JS::HandleId id, bool* resolvedp) {
    auto holder = getHolder(obj);

    *resolvedp = false;

    if (!holder) {
        return;
    }

    IdWrapper idw(cx, id);

    if (!holder->_readOnly && holder->_removed.count(idw.toString())) {
        return;
    }

    ObjectWrapper o(cx, obj);

    std::string sname = IdWrapper(cx, id).toString();

    if (holder->_obj.hasField(sname)) {
        auto elem = holder->_obj[sname];

        JS::RootedValue vp(cx);

        ValueReader(cx, &vp).fromBSONElement(elem, holder->_readOnly);

        o.defineProperty(id, vp, JSPROP_ENUMERATE);

        if (!holder->_readOnly && (elem.type() == mongo::Object || elem.type() == mongo::Array)) {
            // if accessing a subobject, we have no way to know if
            // modifications are being made on writable objects

            holder->_altered = true;
        }

        *resolvedp = true;
    }
}
Ejemplo n.º 2
0
void DBInfo::getProperty(JSContext* cx,
                         JS::HandleObject obj,
                         JS::HandleId id,
                         JS::MutableHandleValue vp) {
    // 2nd look into real values, may be cached collection object
    if (!vp.isUndefined()) {
        auto scope = getScope(cx);
        auto opContext = scope->getOpContext();

        if (opContext && vp.isObject()) {
            ObjectWrapper o(cx, vp);

            if (o.hasOwnField(InternedString::_fullName)) {
                // need to check every time that the collection did not get sharded
                if (haveLocalShardingInfo(opContext, o.getString(InternedString::_fullName)))
                    uasserted(ErrorCodes::BadValue, "can't use sharded collection from db.eval");
            }
        }

        return;
    }

    JS::RootedObject parent(cx);
    if (!JS_GetPrototype(cx, obj, &parent))
        uasserted(ErrorCodes::JSInterpreterFailure, "Couldn't get prototype");

    ObjectWrapper parentWrapper(cx, parent);

    if (parentWrapper.hasOwnField(id)) {
        parentWrapper.getValue(id, vp);
        return;
    }

    IdWrapper idw(cx, id);

    // if starts with '_' we dont return collection, one must use getCollection()
    if (idw.isString()) {
        JSStringWrapper jsstr;
        auto sname = idw.toStringData(&jsstr);
        if (sname.size() == 0 || sname[0] == '_') {
            return;
        }
    }

    // no hit, create new collection
    JS::RootedValue getCollection(cx);
    parentWrapper.getValue(InternedString::getCollection, &getCollection);

    if (!(getCollection.isObject() && JS_ObjectIsFunction(cx, getCollection.toObjectOrNull()))) {
        uasserted(ErrorCodes::BadValue, "getCollection is not a function");
    }

    JS::AutoValueArray<1> args(cx);

    idw.toValue(args[0]);

    JS::RootedValue coll(cx);
    ObjectWrapper(cx, obj).callMethod(getCollection, args, &coll);

    uassert(16861,
            "getCollection returned something other than a collection",
            getScope(cx)->getProto<DBCollectionInfo>().instanceOf(coll));

    // cache collection for reuse, don't enumerate
    ObjectWrapper(cx, obj).defineProperty(id, coll, 0);

    vp.set(coll);
}
Ejemplo n.º 3
0
BSONObj ObjectWrapper::toBSON() {
    if (getScope(_context)->getProto<BSONInfo>().instanceOf(_object)) {
        BSONObj* originalBSON = nullptr;
        bool altered;

        std::tie(originalBSON, altered) = BSONInfo::originalBSON(_context, _object);

        if (originalBSON && !altered)
            return *originalBSON;
    }

    JS::RootedId id(_context);

    // INCREDIBLY SUBTLE BEHAVIOR:
    //
    // (jcarey): Be very careful about how the Rooting API is used in
    // relationship to WriteFieldRecursionFrames. Mozilla'a API more or less
    // demands that the rooting types are on the stack and only manipulated as
    // regular objects, which we aren't doing here. The reason they do this is
    // because the rooting types must be global created and destroyed in an
    // entirely linear order. This is impossible to screw up in regular use,
    // but our unwinding of the recursion frames makes it easy to do here.
    //
    // The roots above need to be before the first frame is emplaced (so
    // they'll be destroyed after it) and none of the roots in the below code
    // (or in ValueWriter::writeThis) can live longer than until the call to
    // emplace() inside ValueWriter. The runtime asserts enabled by MozJS's
    // debug mode will catch runtime errors, but be aware of how difficult this
    // is to get right and what to look for if one of them bites you.

    BSONObjBuilder b;

    {
        // NOTE: Keep the frames in a scope so that it is clear that
        // we always destroy them before we destroy 'b'. It is
        // important to do so: if 'b' is destroyed before the frames,
        // and we don't pop all of the frames (say, due to an
        // exeption), then the frame dtors would write to freed
        // memory.
        WriteFieldRecursionFrames frames;
        frames.emplace(_context, _object, nullptr, StringData{});

        // We special case the _id field in top-level objects and move it to the front.
        // This matches other drivers behavior and makes finding the _id field quicker in BSON.
        if (hasOwnField(InternedString::_id)) {
            _writeField(&b, InternedString::_id, &frames, frames.top().originalBSON);
        }

        while (frames.size()) {
            auto& frame = frames.top();

            // If the index is the same as length, we've seen all the keys at this
            // level and should go up a level
            if (frame.idx == frame.ids.length()) {
                frames.pop();
                continue;
            }

            if (frame.idx == 0 && frame.originalBSON && !frame.altered) {
                // If this is our first look at the object and it has an unaltered
                // bson behind it, move idx to the end so we'll roll up on the next
                // pass through the loop.
                frame.subbob_or(&b)->appendElements(*frame.originalBSON);
                frame.idx = frame.ids.length();
                continue;
            }

            id.set(frame.ids[frame.idx++]);

            if (frames.size() == 1) {
                IdWrapper idw(_context, id);

                // TODO: check if it's cheaper to just compare with an interned
                // string of "_id" rather than with ascii
                if (idw.isString() && idw.equalsAscii("_id")) {
                    continue;
                }
            }

            // writeField invokes ValueWriter with the frame stack, which will push
            // onto frames for subobjects, which will effectively recurse the loop.
            _writeField(frame.subbob_or(&b), JS::HandleId(id), &frames, frame.originalBSON);
        }
    }

    const int sizeWithEOO = b.len() + 1 /*EOO*/ - 4 /*BSONObj::Holder ref count*/;
    uassert(17260,
            str::stream() << "Converting from JavaScript to BSON failed: "
                          << "Object size "
                          << sizeWithEOO
                          << " exceeds limit of "
                          << BSONObjMaxInternalSize
                          << " bytes.",
            sizeWithEOO <= BSONObjMaxInternalSize);

    return b.obj();
}