Example #1
0
static bool
EnumerateExtraProperties(JSContext* cx, HandleObject obj, unsigned flags, Maybe<IdSet>& ht,
                         AutoIdVector* props)
{
    MOZ_ASSERT(obj->getClass()->getNewEnumerate());

    AutoIdVector properties(cx);
    bool enumerableOnly = !(flags & JSITER_HIDDEN);
    if (!obj->getClass()->getNewEnumerate()(cx, obj, properties, enumerableOnly))
        return false;

    RootedId id(cx);
    for (size_t n = 0; n < properties.length(); n++) {
        id = properties[n];

        // The enumerate hook does not indicate whether the properties
        // it returns are enumerable or not. Since we already passed
        // `enumerableOnly` to the hook to filter out non-enumerable
        // properties, it doesn't really matter what we pass here.
        bool enumerable = true;
        if (!Enumerate<CheckForDuplicates>(cx, obj, id, enumerable, flags, ht, props))
            return false;
    }

    return true;
}
Example #2
0
static bool
TryPreserveReflector(JSContext *cx, HandleObject obj)
{
    if (obj->getClass()->ext.isWrappedNative ||
        (obj->getClass()->flags & JSCLASS_IS_DOMJSCLASS) ||
        (obj->isProxy() && GetProxyHandler(obj)->family() == GetDOMProxyHandlerFamily()))
    {
        JS_ASSERT(cx->runtime()->preserveWrapperCallback);
        if (!cx->runtime()->preserveWrapperCallback(cx, obj)) {
            JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_BAD_WEAKMAP_KEY);
            return false;
        }
    }
    return true;
}
Example #3
0
bool
BaseProxyHandler::watch(JSContext *cx, HandleObject proxy, HandleId id, HandleObject callable) const
{
    JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_WATCH,
                         proxy->getClass()->name);
    return false;
}
Example #4
0
bool
GetPropIRGenerator::tryAttachTypedElement(HandleObject obj, ObjOperandId objId,
                                          ValOperandId indexId)
{
    MOZ_ASSERT(idVal_.isNumber());

    if (!obj->is<TypedArrayObject>() && !IsPrimitiveArrayTypedObject(obj))
        return false;

    if (!cx_->runtime()->jitSupportsFloatingPoint &&
        (TypedThingRequiresFloatingPoint(obj) || idVal_.isDouble()))
    {
        return false;
    }

    // Don't attach typed object stubs if the underlying storage could be
    // detached, as the stub will always bail out.
    if (IsPrimitiveArrayTypedObject(obj) && cx_->compartment()->detachedTypedObjects)
        return false;

    TypedThingLayout layout = GetTypedThingLayout(obj->getClass());
    if (layout != Layout_TypedArray)
        writer.guardNoDetachedTypedObjects();

    writer.guardShape(objId, obj->as<ShapedObject>().shape());

    Int32OperandId int32IndexId = writer.guardIsInt32(indexId);
    writer.loadTypedElementResult(objId, int32IndexId, layout, TypedThingElementType(obj));
    writer.returnFromIC();
    return true;
}
Example #5
0
static MOZ_ALWAYS_INLINE bool
SetWeakMapEntryInternal(JSContext* cx, Handle<WeakMapObject*> mapObj,
                        HandleObject key, HandleValue value)
{
    ObjectValueMap* map = mapObj->getMap();
    if (!map) {
        auto newMap = cx->make_unique<ObjectValueMap>(cx, mapObj.get());
        if (!newMap)
            return false;
        if (!newMap->init()) {
            JS_ReportOutOfMemory(cx);
            return false;
        }
        map = newMap.release();
        mapObj->setPrivate(map);
    }

    // Preserve wrapped native keys to prevent wrapper optimization.
    if (!TryPreserveReflector(cx, key))
        return false;

    if (JSWeakmapKeyDelegateOp op = key->getClass()->extWeakmapKeyDelegateOp()) {
        RootedObject delegate(cx, op(key));
        if (delegate && !TryPreserveReflector(cx, delegate))
            return false;
    }

    MOZ_ASSERT(key->compartment() == mapObj->compartment());
    MOZ_ASSERT_IF(value.isObject(), value.toObject().compartment() == mapObj->compartment());
    if (!map->put(key, value)) {
        JS_ReportOutOfMemory(cx);
        return false;
    }
    return true;
}
Example #6
0
static bool
TryPreserveReflector(JSContext* cx, HandleObject obj)
{
    if (obj->getClass()->isWrappedNative() ||
        obj->getClass()->isDOMClass() ||
        (obj->is<ProxyObject>() &&
         obj->as<ProxyObject>().handler()->family() == GetDOMProxyHandlerFamily()))
    {
        MOZ_ASSERT(cx->runtime()->preserveWrapperCallback);
        if (!cx->runtime()->preserveWrapperCallback(cx, obj)) {
            JS_ReportErrorNumber(cx, GetErrorMessage, nullptr, JSMSG_BAD_WEAKMAP_KEY);
            return false;
        }
    }
    return true;
}
Example #7
0
static bool
CloneProperties(JSContext *cx, HandleObject obj, HandleObject clone, CloneMemory &clonedObjects)
{
    RootedId id(cx);
    RootedValue val(cx);
    AutoIdVector ids(cx);
    {
        AutoCompartment ac(cx, obj);
        if (!GetPropertyNames(cx, obj, JSITER_OWNONLY, &ids))
            return false;
    }
    for (uint32_t i = 0; i < ids.length(); i++) {
        id = ids[i];
        if (!GetUnclonedValue(cx, obj, id, &val) ||
            !CloneValue(cx, &val, clonedObjects) ||
            !JS_DefinePropertyById(cx, clone, id, val.get(), NULL, NULL, 0))
        {
            return false;
        }
    }

    if (SelfHostedClass::is(cx, obj->getClass())) {
        for (uint32_t i = 0; i < JSCLASS_RESERVED_SLOTS(obj->getClass()); i++) {
            val = obj->getReservedSlot(i);
            if (!CloneValue(cx, &val, clonedObjects))
                return false;
            clone->setReservedSlot(i, val);
        }

        /* Privates are not cloned, so be careful! */
        if (obj->hasPrivate())
            clone->setPrivate(obj->getPrivate());
    }

    return true;
}
static RawObject
CloneObject(JSContext *cx, HandleObject srcObj, CloneMemory &clonedObjects)
{
    CloneMemory::AddPtr p = clonedObjects.lookupForAdd(srcObj.get());
    if (p)
        return p->value;
    RootedObject clone(cx);
    if (srcObj->isFunction()) {
        RootedFunction fun(cx, srcObj->toFunction());
        clone = CloneFunctionObject(cx, fun, cx->global(), fun->getAllocKind());
    } else if (srcObj->isRegExp()) {
        RegExpObject &reobj = srcObj->asRegExp();
        RootedAtom source(cx, reobj.getSource());
        clone = RegExpObject::createNoStatics(cx, source, reobj.getFlags(), NULL);
    } else if (srcObj->isDate()) {
        clone = JS_NewDateObjectMsec(cx, srcObj->getDateUTCTime().toNumber());
    } else if (srcObj->isBoolean()) {
        clone = BooleanObject::create(cx, srcObj->asBoolean().unbox());
    } else if (srcObj->isNumber()) {
        clone = NumberObject::create(cx, srcObj->asNumber().unbox());
    } else if (srcObj->isString()) {
        Rooted<JSStableString*> str(cx, srcObj->asString().unbox()->ensureStable(cx));
        if (!str)
            return NULL;
        str = js_NewStringCopyN(cx, str->chars().get(), str->length())->ensureStable(cx);
        if (!str)
            return NULL;
        clone = StringObject::create(cx, str);
    } else if (srcObj->isDenseArray()) {
        return CloneDenseArray(cx, srcObj, clonedObjects);
    } else {
        if (srcObj->isArray()) {
            clone = NewDenseEmptyArray(cx);
        } else {
            JS_ASSERT(srcObj->isNative());
            clone = NewObjectWithClassProto(cx, srcObj->getClass(), NULL, cx->global(),
                                            srcObj->getAllocKind());
        }
    }
    if (!clone || !clonedObjects.relookupOrAdd(p, srcObj.get(), clone.get()) ||
        !CloneProperties(cx, srcObj, clone, clonedObjects))
    {
        return NULL;
    }
    return clone;
}
Example #9
0
bool
GetPropIRGenerator::tryAttachWindowProxy(HandleObject obj, ObjOperandId objId, HandleId id)
{
    // Attach a stub when the receiver is a WindowProxy and we are calling some
    // kinds of JSNative getters on the Window object (the global object).

    if (!IsWindowProxy(obj))
        return false;

    // This must be a WindowProxy for the current Window/global. Else it would
    // be a cross-compartment wrapper and IsWindowProxy returns false for
    // those.
    MOZ_ASSERT(obj->getClass() == cx_->maybeWindowProxyClass());
    MOZ_ASSERT(ToWindowIfWindowProxy(obj) == cx_->global());

    // Now try to do the lookup on the Window (the current global) and see if
    // it's a native getter.
    HandleObject windowObj = cx_->global();
    RootedShape shape(cx_);
    RootedNativeObject holder(cx_);
    NativeGetPropCacheability type = CanAttachNativeGetProp(cx_, windowObj, id, &holder, &shape, pc_,
                                                            engine_, isTemporarilyUnoptimizable_);
    if (type != CanAttachCallGetter ||
        !IsCacheableGetPropCallNative(windowObj, holder, shape))
    {
        return false;
    }

    // Make sure the native getter is okay with the IC passing the Window
    // instead of the WindowProxy as |this| value.
    JSFunction* callee = &shape->getterObject()->as<JSFunction>();
    MOZ_ASSERT(callee->isNative());
    if (!callee->jitInfo() || callee->jitInfo()->needsOuterizedThisObject())
        return false;

    // Guard the incoming object is a WindowProxy and inline a getter call based
    // on the Window object.
    maybeEmitIdGuard(id);
    writer.guardClass(objId, GuardClassKind::WindowProxy);
    ObjOperandId windowObjId = writer.loadObject(windowObj);
    EmitCallGetterResult(writer, windowObj, holder, shape, windowObjId);
    return true;
}
Example #10
0
JS_SplicePrototype(JSContext *cx, HandleObject obj, HandleObject proto)
{
    /*
     * Change the prototype of an object which hasn't been used anywhere
     * and does not share its type with another object. Unlike JS_SetPrototype,
     * does not nuke type information for the object.
     */
    CHECK_REQUEST(cx);

    if (!obj->hasSingletonType()) {
        /*
         * We can see non-singleton objects when trying to splice prototypes
         * due to mutable __proto__ (ugh).
         */
        return JS_SetPrototype(cx, obj, proto);
    }

    Rooted<TaggedProto> tagged(cx, TaggedProto(proto));
    return obj->splicePrototype(cx, obj->getClass(), tagged);
}
Example #11
0
static bool
FindErrorInstanceOrPrototype(JSContext* cx, HandleObject obj, MutableHandleObject result)
{
    // Walk up the prototype chain until we find an error object instance or
    // prototype object. This allows code like:
    //  Object.create(Error.prototype).stack
    // or
    //   function NYI() { }
    //   NYI.prototype = new Error;
    //   (new NYI).stack
    // to continue returning stacks that are useless, but at least don't throw.

    RootedObject target(cx, CheckedUnwrap(obj));
    if (!target) {
        ReportAccessDenied(cx);
        return false;
    }

    RootedObject proto(cx);
    while (!IsErrorProtoKey(StandardProtoKeyOrNull(target))) {
        if (!GetPrototype(cx, target, &proto))
            return false;

        if (!proto) {
            // We walked the whole prototype chain and did not find an Error
            // object.
            JS_ReportErrorNumberASCII(cx, GetErrorMessage, nullptr, JSMSG_INCOMPATIBLE_PROTO,
                                      js_Error_str, "(get stack)", obj->getClass()->name);
            return false;
        }

        target = CheckedUnwrap(proto);
        if (!target) {
            ReportAccessDenied(cx);
            return false;
        }
    }

    result.set(target);
    return true;
}
Example #12
0
static inline bool
Enumerate(JSContext* cx, HandleObject pobj, jsid id,
          bool enumerable, unsigned flags, Maybe<IdSet>& ht, AutoIdVector* props)
{
    if (CheckForDuplicates) {
        if (!ht) {
            ht.emplace(cx);
            // Most of the time there are only a handful of entries.
            if (!ht->init(5))
                return false;
        }

        // If we've already seen this, we definitely won't add it.
        IdSet::AddPtr p = ht->lookupForAdd(id);
        if (MOZ_UNLIKELY(!!p))
            return true;

        // It's not necessary to add properties to the hash table at the end of
        // the prototype chain, but custom enumeration behaviors might return
        // duplicated properties, so always add in such cases.
        if (pobj->is<ProxyObject>() ||
            pobj->staticPrototype() ||
            pobj->getClass()->getNewEnumerate())
        {
            if (!ht->add(p, id))
                return false;
        }
    }

    if (!enumerable && !(flags & JSITER_HIDDEN))
        return true;

    // Symbol-keyed properties and nonenumerable properties are skipped unless
    // the caller specifically asks for them. A caller can also filter out
    // non-symbols by asking for JSITER_SYMBOLSONLY.
    if (JSID_IS_SYMBOL(id) ? !(flags & JSITER_SYMBOLS) : (flags & JSITER_SYMBOLSONLY))
        return true;

    return props->append(id);
}
Example #13
0
MOZ_ALWAYS_INLINE bool
SetWeakMapEntryInternal(JSContext *cx, Handle<WeakMapObject*> mapObj,
                        HandleObject key, HandleValue value)
{
    ObjectValueMap *map = mapObj->getMap();
    if (!map) {
        map = cx->new_<ObjectValueMap>(cx, mapObj.get());
        if (!map)
            return false;
        if (!map->init()) {
            js_delete(map);
            JS_ReportOutOfMemory(cx);
            return false;
        }
        mapObj->setPrivate(map);
    }

    // Preserve wrapped native keys to prevent wrapper optimization.
    if (!TryPreserveReflector(cx, key))
        return false;

    if (JSWeakmapKeyDelegateOp op = key->getClass()->ext.weakmapKeyDelegateOp) {
        RootedObject delegate(cx, op(key));
        if (delegate && !TryPreserveReflector(cx, delegate))
            return false;
    }

    JS_ASSERT(key->compartment() == mapObj->compartment());
    JS_ASSERT_IF(value.isObject(), value.toObject().compartment() == mapObj->compartment());
    if (!map->put(key, value)) {
        JS_ReportOutOfMemory(cx);
        return false;
    }
    WeakMapPostWriteBarrier(cx->runtime(), map, key.get());
    return true;
}
Example #14
0
bool
BaseProxyHandler::set(JSContext* cx, HandleObject proxy, HandleObject receiver,
                      HandleId id, bool strict, MutableHandleValue vp) const
{
    assertEnteredPolicy(cx, proxy, id, SET);

    // This method is not covered by any spec, but we follow ES6 draft rev 28
    // (2014 Oct 14) 9.1.9 fairly closely, adapting it slightly for
    // SpiderMonkey's particular foibles.

    // Steps 2-3.  (Step 1 is a superfluous assertion.)
    Rooted<PropertyDescriptor> ownDesc(cx);
    if (!getOwnPropertyDescriptor(cx, proxy, id, &ownDesc))
        return false;

    // Step 4.
    if (!ownDesc.object()) {
        // The spec calls this variable "parent", but that word has weird
        // connotations in SpiderMonkey, so let's go with "proto".
        RootedObject proto(cx);
        if (!GetPrototype(cx, proxy, &proto))
            return false;
        if (proto)
            return SetProperty(cx, proto, receiver, id, vp, strict);

        // Change ownDesc to be a complete descriptor for a configurable,
        // writable, enumerable data property. Then fall through to step 5.
        ownDesc.clear();
        ownDesc.setAttributes(JSPROP_ENUMERATE);
    }

    // Step 5.
    if (ownDesc.isDataDescriptor()) {
        // Steps 5.a-b, adapted to our nonstandard implementation of ES6
        // [[Set]] return values.
        if (!ownDesc.isWritable()) {
            if (strict)
                return JSObject::reportReadOnly(cx, id, JSREPORT_ERROR);
            if (cx->compartment()->options().extraWarnings(cx))
                return JSObject::reportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
            return true;
        }

        // Nonstandard SpiderMonkey special case: setter ops.
        StrictPropertyOp setter = ownDesc.setter();
        MOZ_ASSERT(setter != JS_StrictPropertyStub);
        if (setter && setter != JS_StrictPropertyStub)
            return CallSetter(cx, receiver, id, setter, ownDesc.attributes(), strict, vp);

        // Steps 5.c-d. Adapt for SpiderMonkey by using HasOwnProperty instead
        // of the standard [[GetOwnProperty]].
        bool existingDescriptor;
        if (!HasOwnProperty(cx, receiver, id, &existingDescriptor))
            return false;

        // Steps 5.e-f.
        unsigned attrs =
            existingDescriptor
            ? JSPROP_IGNORE_ENUMERATE | JSPROP_IGNORE_READONLY | JSPROP_IGNORE_PERMANENT
            : JSPROP_ENUMERATE;

        // A very old nonstandard SpiderMonkey extension: default to the Class
        // getter and setter ops.
        const Class* clasp = receiver->getClass();
        MOZ_ASSERT(clasp->getProperty != JS_PropertyStub);
        MOZ_ASSERT(clasp->setProperty != JS_StrictPropertyStub);
        return DefineProperty(cx, receiver, id, vp,
                              clasp->getProperty, clasp->setProperty, attrs);
    }

    // Step 6.
    MOZ_ASSERT(ownDesc.isAccessorDescriptor());
    RootedObject setter(cx);
    if (ownDesc.hasSetterObject())
        setter = ownDesc.setterObject();
    if (!setter)
        return js_ReportGetterOnlyAssignment(cx, strict);
    RootedValue setterValue(cx, ObjectValue(*setter));
    return InvokeGetterOrSetter(cx, receiver, setterValue, 1, vp.address(), vp);
}