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; }
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; }
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; }
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; }
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; }
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; }
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; }
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; }
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); }
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; }
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); }
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; }
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); }