diff --git a/js/src/builtin/Intl.cpp b/js/src/builtin/Intl.cpp index 3c63d9bb02cb..080c9ba73e22 100644 --- a/js/src/builtin/Intl.cpp +++ b/js/src/builtin/Intl.cpp @@ -615,7 +615,7 @@ Collator(JSContext *cx, CallArgs args, bool construct) // 10.1.2.1 step 5 bool extensible; - if (!JSObject::isExtensible(cx, obj, &extensible)) + if (!IsExtensible(cx, obj, &extensible)) return false; if (!extensible) return Throw(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE); @@ -1101,7 +1101,7 @@ NumberFormat(JSContext *cx, CallArgs args, bool construct) // 11.1.2.1 step 5 bool extensible; - if (!JSObject::isExtensible(cx, obj, &extensible)) + if (!IsExtensible(cx, obj, &extensible)) return false; if (!extensible) return Throw(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE); @@ -1558,7 +1558,7 @@ DateTimeFormat(JSContext *cx, CallArgs args, bool construct) // 12.1.2.1 step 5 bool extensible; - if (!JSObject::isExtensible(cx, obj, &extensible)) + if (!IsExtensible(cx, obj, &extensible)) return false; if (!extensible) return Throw(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE); diff --git a/js/src/builtin/Object.cpp b/js/src/builtin/Object.cpp index e7bc0075a35b..c9c0c923a93c 100644 --- a/js/src/builtin/Object.cpp +++ b/js/src/builtin/Object.cpp @@ -421,7 +421,7 @@ js::obj_getPrototypeOf(JSContext *cx, unsigned argc, Value *vp) /* Step 3. */ RootedObject proto(cx); - if (!JSObject::getProto(cx, obj, &proto)) + if (!GetPrototype(cx, obj, &proto)) return false; args.rval().setObjectOrNull(proto); return true; @@ -467,7 +467,7 @@ obj_setPrototypeOf(JSContext *cx, unsigned argc, Value *vp) RootedObject newProto(cx, args[1].toObjectOrNull()); bool success; - if (!JSObject::setProto(cx, obj, newProto, &success)) + if (!SetPrototype(cx, obj, newProto, &success)) return false; /* Step 7. */ @@ -859,7 +859,7 @@ obj_isExtensible(JSContext *cx, unsigned argc, Value *vp) // Step 2. if (args.get(0).isObject()) { RootedObject obj(cx, &args.get(0).toObject()); - if (!JSObject::isExtensible(cx, obj, &extensible)) + if (!IsExtensible(cx, obj, &extensible)) return false; } args.rval().setBoolean(extensible); @@ -881,7 +881,7 @@ obj_preventExtensions(JSContext *cx, unsigned argc, Value *vp) RootedObject obj(cx, &args.get(0).toObject()); bool status; - if (!JSObject::preventExtensions(cx, obj, &status)) + if (!PreventExtensions(cx, obj, &status)) return false; // Step 4. @@ -978,7 +978,7 @@ ProtoGetter(JSContext *cx, unsigned argc, Value *vp) RootedObject obj(cx, &args.thisv().toObject()); RootedObject proto(cx); - if (!JSObject::getProto(cx, obj, &proto)) + if (!GetPrototype(cx, obj, &proto)) return false; args.rval().setObjectOrNull(proto); return true; @@ -1025,7 +1025,7 @@ ProtoSetter(JSContext *cx, unsigned argc, Value *vp) Rooted newProto(cx, args[0].toObjectOrNull()); bool success; - if (!JSObject::setProto(cx, obj, newProto, &success)) + if (!SetPrototype(cx, obj, newProto, &success)) return false; if (!success) { diff --git a/js/src/builtin/TestingFunctions.cpp b/js/src/builtin/TestingFunctions.cpp index 77a1086c4f02..85204ead42cf 100644 --- a/js/src/builtin/TestingFunctions.cpp +++ b/js/src/builtin/TestingFunctions.cpp @@ -2211,7 +2211,7 @@ SetImmutablePrototype(JSContext *cx, unsigned argc, Value *vp) RootedObject obj(cx, &args[0].toObject()); bool succeeded; - if (!JSObject::setImmutablePrototype(cx, obj, &succeeded)) + if (!js::SetImmutablePrototype(cx, obj, &succeeded)) return false; args.rval().setBoolean(succeeded); diff --git a/js/src/jit/VMFunctions.cpp b/js/src/jit/VMFunctions.cpp index 52321fe9dca6..f92240a233f1 100644 --- a/js/src/jit/VMFunctions.cpp +++ b/js/src/jit/VMFunctions.cpp @@ -203,7 +203,7 @@ MutatePrototype(JSContext *cx, HandlePlainObject obj, HandleValue value) RootedObject newProto(cx, value.toObjectOrNull()); bool succeeded; - if (!JSObject::setProto(cx, obj, newProto, &succeeded)) + if (!SetPrototype(cx, obj, newProto, &succeeded)) return false; MOZ_ASSERT(succeeded); return true; diff --git a/js/src/jsapi.cpp b/js/src/jsapi.cpp index 2fc4065c380b..deced0cedb5a 100644 --- a/js/src/jsapi.cpp +++ b/js/src/jsapi.cpp @@ -2018,7 +2018,7 @@ JS_GetInstancePrivate(JSContext *cx, HandleObject obj, const JSClass *clasp, Cal JS_PUBLIC_API(bool) JS_GetPrototype(JSContext *cx, JS::Handle obj, JS::MutableHandle protop) { - return JSObject::getProto(cx, obj, protop); + return GetPrototype(cx, obj, protop); } JS_PUBLIC_API(bool) @@ -2029,7 +2029,7 @@ JS_SetPrototype(JSContext *cx, JS::Handle obj, JS::Handle assertSameCompartment(cx, obj, proto); bool succeeded; - if (!JSObject::setProto(cx, obj, proto, &succeeded)) + if (!SetPrototype(cx, obj, proto, &succeeded)) return false; if (!succeeded) { @@ -2041,6 +2041,18 @@ JS_SetPrototype(JSContext *cx, JS::Handle obj, JS::Handle return true; } +JS_PUBLIC_API(bool) +JS_IsExtensible(JSContext *cx, HandleObject obj, bool *extensible) +{ + return IsExtensible(cx, obj, extensible); +} + +JS_PUBLIC_API(bool) +JS_PreventExtensions(JSContext *cx, JS::HandleObject obj, bool *succeeded) +{ + return PreventExtensions(cx, obj, succeeded); +} + JS_PUBLIC_API(JSObject *) JS_GetParent(JSObject *obj) { @@ -2218,12 +2230,6 @@ JS_NewObjectForConstructor(JSContext *cx, const JSClass *clasp, const CallArgs& return CreateThis(cx, Valueify(clasp), obj); } -JS_PUBLIC_API(bool) -JS_IsExtensible(JSContext *cx, HandleObject obj, bool *extensible) -{ - return JSObject::isExtensible(cx, obj, extensible); -} - JS_PUBLIC_API(bool) JS_IsNative(JSObject *obj) { @@ -2254,7 +2260,7 @@ JS_DeepFreezeObject(JSContext *cx, HandleObject obj) /* Assume that non-extensible objects are already deep-frozen, to avoid divergence. */ bool extensible; - if (!JSObject::isExtensible(cx, obj, &extensible)) + if (!IsExtensible(cx, obj, &extensible)) return false; if (!extensible) return true; @@ -6119,12 +6125,6 @@ JS_DecodeInterpretedFunction(JSContext *cx, const void *data, uint32_t length) return funobj; } -JS_PUBLIC_API(bool) -JS_PreventExtensions(JSContext *cx, JS::HandleObject obj, bool *succeeded) -{ - return JSObject::preventExtensions(cx, obj, succeeded); -} - JS_PUBLIC_API(void) JS::SetAsmJSCacheOps(JSRuntime *rt, const JS::AsmJSCacheOps *ops) { diff --git a/js/src/jsarray.cpp b/js/src/jsarray.cpp index 8413df991565..e4a6a7bf886f 100644 --- a/js/src/jsarray.cpp +++ b/js/src/jsarray.cpp @@ -453,7 +453,7 @@ array_length_getter(JSContext *cx, HandleObject obj_, HandleId id, MutableHandle vp.setNumber(obj->as().length()); return true; } - if (!JSObject::getProto(cx, obj, &obj)) + if (!GetPrototype(cx, obj, &obj)) return false; } while (obj); return true; diff --git a/js/src/jsfun.cpp b/js/src/jsfun.cpp index 16dc2d8a273b..0ac403c42449 100644 --- a/js/src/jsfun.cpp +++ b/js/src/jsfun.cpp @@ -894,7 +894,7 @@ CreateFunctionPrototype(JSContext *cx, JSProtoKey key) bool succeeded; RootedFunction throwTypeError(cx, NewFunction(cx, tte, ThrowTypeError, 0, JSFunction::NATIVE_FUN, self, js::NullPtr())); - if (!throwTypeError || !JSObject::preventExtensions(cx, throwTypeError, &succeeded)) + if (!throwTypeError || !PreventExtensions(cx, throwTypeError, &succeeded)) return nullptr; if (!succeeded) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CHANGE_EXTENSIBILITY); diff --git a/js/src/jsiter.cpp b/js/src/jsiter.cpp index f21a5a59cb23..b3efd0da27d6 100644 --- a/js/src/jsiter.cpp +++ b/js/src/jsiter.cpp @@ -352,7 +352,7 @@ Snapshot(JSContext *cx, HandleObject pobj_, unsigned flags, AutoIdVector *props) if (flags & JSITER_OWNONLY) break; - if (!JSObject::getProto(cx, pobj, &pobj)) + if (!GetPrototype(cx, pobj, &pobj)) return false; } while (pobj != nullptr); @@ -1163,7 +1163,7 @@ SuppressDeletedPropertyHelper(JSContext *cx, HandleObject obj, StringPredicate p * became visible as a result of this deletion. */ RootedObject proto(cx); - if (!JSObject::getProto(cx, obj, &proto)) + if (!GetPrototype(cx, obj, &proto)) return false; if (proto) { RootedObject obj2(cx); diff --git a/js/src/jsobj.cpp b/js/src/jsobj.cpp index 32afc8ab8346..90d239fb3b11 100644 --- a/js/src/jsobj.cpp +++ b/js/src/jsobj.cpp @@ -606,7 +606,7 @@ DefinePropertyOnObject(JSContext *cx, HandleNativeObject obj, HandleId id, const /* 8.12.9 steps 2-4. */ if (!shape) { bool extensible; - if (!JSObject::isExtensible(cx, obj, &extensible)) + if (!IsExtensible(cx, obj, &extensible)) return false; if (!extensible) return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval); @@ -1103,7 +1103,7 @@ JSObject::sealOrFreeze(JSContext *cx, HandleObject obj, ImmutabilityType it) MOZ_ASSERT(it == SEAL || it == FREEZE); bool succeeded; - if (!JSObject::preventExtensions(cx, obj, &succeeded)) + if (!PreventExtensions(cx, obj, &succeeded)) return false; if (!succeeded) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_CHANGE_EXTENSIBILITY); @@ -1202,7 +1202,7 @@ JSObject::sealOrFreeze(JSContext *cx, HandleObject obj, ImmutabilityType it) JSObject::isSealedOrFrozen(JSContext *cx, HandleObject obj, ImmutabilityType it, bool *resultp) { bool extensible; - if (!JSObject::isExtensible(cx, obj, &extensible)) + if (!IsExtensible(cx, obj, &extensible)) return false; if (extensible) { *resultp = false; @@ -2153,7 +2153,7 @@ js::XDRObjectLiteral(XDRState *xdr, MutableHandleNativeObject obj) uint32_t frozen; bool extensible; if (mode == XDR_ENCODE) { - if (!JSObject::isExtensible(cx, obj, &extensible)) + if (!IsExtensible(cx, obj, &extensible)) return false; frozen = extensible ? 0 : 1; } @@ -3229,6 +3229,135 @@ JSObject::reportNotExtensible(JSContext *cx, unsigned report) nullptr, nullptr); } + +/*** ES6 standard internal methods ***************************************************************/ + +bool +js::SetPrototype(JSContext *cx, HandleObject obj, HandleObject proto, bool *succeeded) +{ + /* + * If |obj| has a "lazy" [[Prototype]], it is 1) a proxy 2) whose handler's + * {get,set}PrototypeOf and setImmutablePrototype methods mediate access to + * |obj.[[Prototype]]|. The Proxy subsystem is responsible for responding + * to such attempts. + */ + if (obj->hasLazyPrototype()) { + MOZ_ASSERT(obj->is()); + return Proxy::setPrototypeOf(cx, obj, proto, succeeded); + } + + /* Disallow mutation of immutable [[Prototype]]s. */ + if (obj->nonLazyPrototypeIsImmutable()) { + *succeeded = false; + return true; + } + + /* + * Disallow mutating the [[Prototype]] on ArrayBuffer objects, which + * due to their complicated delegate-object shenanigans can't easily + * have a mutable [[Prototype]]. + */ + if (obj->is()) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SETPROTOTYPEOF_FAIL, + "incompatible ArrayBuffer"); + return false; + } + + /* + * Disallow mutating the [[Prototype]] on Typed Objects, per the spec. + */ + if (obj->is()) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SETPROTOTYPEOF_FAIL, + "incompatible TypedObject"); + return false; + } + + /* + * Explicitly disallow mutating the [[Prototype]] of Location objects + * for flash-related security reasons. + */ + if (!strcmp(obj->getClass()->name, "Location")) { + JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SETPROTOTYPEOF_FAIL, + "incompatible Location object"); + return false; + } + + /* ES6 9.1.2 step 5 forbids changing [[Prototype]] if not [[Extensible]]. */ + bool extensible; + if (!IsExtensible(cx, obj, &extensible)) + return false; + if (!extensible) { + *succeeded = false; + return true; + } + + /* ES6 9.1.2 step 6 forbids generating cyclical prototype chains. */ + RootedObject obj2(cx); + for (obj2 = proto; obj2; ) { + if (obj2 == obj) { + *succeeded = false; + return true; + } + + if (!GetPrototype(cx, obj2, &obj2)) + return false; + } + + Rooted taggedProto(cx, TaggedProto(proto)); + *succeeded = SetClassAndProto(cx, obj, obj->getClass(), taggedProto); + return *succeeded; +} + +bool +js::PreventExtensions(JSContext *cx, HandleObject obj, bool *succeeded) +{ + if (obj->is()) + return js::Proxy::preventExtensions(cx, obj, succeeded); + + if (!obj->nonProxyIsExtensible()) { + *succeeded = true; + return true; + } + + /* + * Force lazy properties to be resolved by iterating over the objects' own + * properties. + */ + AutoIdVector props(cx); + if (!js::GetPropertyKeys(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY, &props)) + return false; + + /* + * Convert all dense elements to sparse properties. This will shrink the + * initialized length and capacity of the object to zero and ensure that no + * new dense elements can be added without calling growElements(), which + * checks isExtensible(). + */ + if (obj->isNative() && !NativeObject::sparsifyDenseElements(cx, obj.as())) + return false; + + *succeeded = true; + return obj->setFlag(cx, BaseShape::NOT_EXTENSIBLE, JSObject::GENERATE_SHAPE); +} + + +/*** SpiderMonkey nonstandard internal methods ***************************************************/ + +bool +js::SetImmutablePrototype(ExclusiveContext *cx, HandleObject obj, bool *succeeded) +{ + if (obj->hasLazyPrototype()) { + if (!cx->shouldBeJSContext()) + return false; + return Proxy::setImmutablePrototype(cx->asJSContext(), obj, succeeded); + } + + if (!obj->setFlag(cx, BaseShape::IMMUTABLE_PROTOTYPE)) + return false; + *succeeded = true; + return true; +} + /* static */ bool JSObject::defaultValue(JSContext *cx, HandleObject obj, JSType hint, MutableHandleValue vp) { @@ -3307,6 +3436,9 @@ js::NativeUnwatch(JSContext *cx, JS::HandleObject obj, JS::HandleId id) return UnwatchGuts(cx, obj, id); } + +/* * */ + bool js::HasDataProperty(JSContext *cx, NativeObject *obj, jsid id, Value *vp) { @@ -3443,7 +3575,7 @@ js::IsDelegateOfObject(JSContext *cx, HandleObject protoObj, JSObject* obj, bool { RootedObject obj2(cx, obj); for (;;) { - if (!JSObject::getProto(cx, obj2, &obj2)) + if (!GetPrototype(cx, obj2, &obj2)) return false; if (!obj2) { *result = false; @@ -3627,6 +3759,9 @@ js_ReportGetterOnlyAssignment(JSContext *cx, bool strict) JSMSG_GETTER_ONLY); } + +/*** Debugging routines **************************************************************************/ + #ifdef DEBUG /* @@ -3951,6 +4086,9 @@ js_DumpBacktrace(JSContext *cx) #endif } + +/* * */ + void JSObject::addSizeOfExcludingThis(mozilla::MallocSizeOf mallocSizeOf, JS::ClassInfo *info) { diff --git a/js/src/jsobj.h b/js/src/jsobj.h index f5453df5ee7a..fe5aec208013 100644 --- a/js/src/jsobj.h +++ b/js/src/jsobj.h @@ -78,6 +78,10 @@ class NormalArgumentsObject; class SetObject; class StrictArgumentsObject; +// Forward declarations, required for later friend declarations. +bool PreventExtensions(JSContext *cx, JS::HandleObject obj, bool *succeeded); +bool SetImmutablePrototype(js::ExclusiveContext *cx, JS::HandleObject obj, bool *succeeded); + } /* namespace js */ /* @@ -113,6 +117,9 @@ class JSObject : public js::gc::Cell friend class js::GCMarker; friend class js::NewObjectCache; friend class js::Nursery; + friend bool js::PreventExtensions(JSContext *cx, JS::HandleObject obj, bool *succeeded); + friend bool js::SetImmutablePrototype(js::ExclusiveContext *cx, JS::HandleObject obj, + bool *succeeded); /* Make the type object to use for LAZY_TYPE objects. */ static js::types::TypeObject *makeLazyType(JSContext *cx, js::HandleObject obj); @@ -337,7 +344,7 @@ class JSObject : public js::gc::Cell * 1. obj->getProto() returns the prototype, but asserts if obj is a proxy. * 2. obj->getTaggedProto() returns a TaggedProto, which can be tested to * check if the proto is an object, nullptr, or lazily computed. - * 3. JSObject::getProto(cx, obj, &proto) computes the proto of an object. + * 3. js::GetPrototype(cx, obj, &proto) computes the proto of an object. * If obj is a proxy and the proto is lazy, this code may allocate or * GC in order to compute the proto. Currently, it will not run JS code. */ @@ -385,19 +392,6 @@ class JSObject : public js::gc::Cell return lastProperty()->hasObjectFlag(js::BaseShape::IMMUTABLE_PROTOTYPE); } - // Attempt to make |obj|'s [[Prototype]] immutable, such that subsequently - // trying to change it will not work. If an internal error occurred, - // returns false. Otherwise, |*succeeded| is set to true iff |obj|'s - // [[Prototype]] is now immutable. - static bool - setImmutablePrototype(js::ExclusiveContext *cx, JS::HandleObject obj, bool *succeeded); - - static inline bool getProto(JSContext *cx, js::HandleObject obj, - js::MutableHandleObject protop); - // Returns false on error, success of operation in outparam. - static inline bool setProto(JSContext *cx, JS::HandleObject obj, - JS::HandleObject proto, bool *succeeded); - // uninlinedSetType() is the same as setType(), but not inlined. inline void setType(js::types::TypeObject *newType); void uninlinedSetType(js::types::TypeObject *newType); @@ -493,9 +487,6 @@ class JSObject : public js::gc::Cell */ public: - static inline bool - isExtensible(js::ExclusiveContext *cx, js::HandleObject obj, bool *extensible); - // Indicates whether a non-proxy is extensible. Don't call on proxies! // This method really shouldn't exist -- but there are a few internal // places that want it (JITs and the like), and it'd be a pain to mark them @@ -507,12 +498,6 @@ class JSObject : public js::gc::Cell return !lastProperty()->hasObjectFlag(js::BaseShape::NOT_EXTENSIBLE); } - // Attempt to change the [[Extensible]] bit on |obj| to false. Indicate - // success or failure through the |*succeeded| outparam, or actual error - // through the return value. - static bool - preventExtensions(JSContext *cx, js::HandleObject obj, bool *succeeded); - private: enum ImmutabilityType { SEAL, FREEZE }; @@ -891,8 +876,71 @@ class ValueArray { namespace js { +/*** Standard internal methods ******************************************************************** + * + * The functions below are the fundamental operations on objects. + * + * ES6 specifies 14 internal methods that define how objects behave. The spec + * is actually quite good on this topic, though you may have to read it a few + * times. See ES6 draft rev 29 (6 Dec 2014) 6.1.7.2 and 6.1.7.3. + * + * When 'obj' is an ordinary object, these functions have boring standard + * behavior as specified by ES6 draft rev 29 section 9.1; see the section about + * internal methods in vm/NativeObject.h. + * + * Proxies override the behavior of internal methods. So when 'obj' is a proxy, + * any one of the functions below could do just about anything. See jsproxy.h. + */ + +/* + * ES6 [[GetPrototypeOf]]. Get obj's prototype, storing it in protop. + * + * If obj is definitely not a proxy, the infallible obj->getProto() can be used + * instead. See the comment on JSObject::getTaggedProto(). + */ +inline bool +GetPrototype(JSContext *cx, HandleObject obj, MutableHandleObject protop); + +/* + * ES6 [[SetPrototypeOf]]. Change obj's prototype to proto. + * + * Returns false on error, success of operation in outparam. For example, if + * obj is not extensible, its prototype is fixed. js::SetPrototype will return + * true, because no exception is thrown for this; but *succeeded will be false. + */ +extern bool +SetPrototype(JSContext *cx, HandleObject obj, HandleObject proto, bool *succeeded); + +/* + * ES6 [[IsExtensible]]. Extensible objects can have new properties defined on + * them. Inextensible objects can't, and their [[Prototype]] slot is fixed as + * well. + */ +inline bool +IsExtensible(ExclusiveContext *cx, HandleObject obj, bool *extensible); + +/* + * ES6 [[PreventExtensions]]. Attempt to change the [[Extensible]] bit on |obj| + * to false. Indicate success or failure through the |*succeeded| outparam, or + * actual error through the return value. + */ +extern bool +PreventExtensions(JSContext *cx, HandleObject obj, bool *succeeded); + + +/*** SpiderMonkey nonstandard internal methods ***************************************************/ + +/* + * Attempt to make |obj|'s [[Prototype]] immutable, such that subsequently + * trying to change it will not work. If an internal error occurred, + * returns false. Otherwise, |*succeeded| is set to true iff |obj|'s + * [[Prototype]] is now immutable. + */ +extern bool +SetImmutablePrototype(js::ExclusiveContext *cx, JS::HandleObject obj, bool *succeeded); + /* Set *resultp to tell whether obj has an own property with the given id. */ -bool +extern bool HasOwnProperty(JSContext *cx, HandleObject obj, HandleId id, bool *resultp); template diff --git a/js/src/jsobjinlines.h b/js/src/jsobjinlines.h index ed3b2676024d..e376962e20ee 100644 --- a/js/src/jsobjinlines.h +++ b/js/src/jsobjinlines.h @@ -164,8 +164,8 @@ JSObject::setType(js::types::TypeObject *newType) type_ = newType; } -/* static */ inline bool -JSObject::getProto(JSContext *cx, js::HandleObject obj, js::MutableHandleObject protop) +inline bool +js::GetPrototype(JSContext *cx, js::HandleObject obj, js::MutableHandleObject protop) { if (obj->getTaggedProto().isLazy()) { MOZ_ASSERT(obj->is()); @@ -176,89 +176,13 @@ JSObject::getProto(JSContext *cx, js::HandleObject obj, js::MutableHandleObject } } -/* static */ inline bool -JSObject::setProto(JSContext *cx, JS::HandleObject obj, JS::HandleObject proto, bool *succeeded) +inline bool +js::IsExtensible(ExclusiveContext *cx, HandleObject obj, bool *extensible) { - /* - * If |obj| has a "lazy" [[Prototype]], it is 1) a proxy 2) whose handler's - * {get,set}PrototypeOf and setImmutablePrototype methods mediate access to - * |obj.[[Prototype]]|. The Proxy subsystem is responsible for responding - * to such attempts. - */ - if (obj->hasLazyPrototype()) { - MOZ_ASSERT(obj->is()); - return js::Proxy::setPrototypeOf(cx, obj, proto, succeeded); - } - - /* Disallow mutation of immutable [[Prototype]]s. */ - if (obj->nonLazyPrototypeIsImmutable()) { - *succeeded = false; - return true; - } - - /* - * Disallow mutating the [[Prototype]] on ArrayBuffer objects, which - * due to their complicated delegate-object shenanigans can't easily - * have a mutable [[Prototype]]. - */ - if (obj->is()) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SETPROTOTYPEOF_FAIL, - "incompatible ArrayBuffer"); - return false; - } - - /* - * Disallow mutating the [[Prototype]] on Typed Objects, per the spec. - */ - if (obj->is()) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SETPROTOTYPEOF_FAIL, - "incompatible TypedObject"); - return false; - } - - /* - * Explicitly disallow mutating the [[Prototype]] of Location objects - * for flash-related security reasons. - */ - if (!strcmp(obj->getClass()->name, "Location")) { - JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_SETPROTOTYPEOF_FAIL, - "incompatible Location object"); - return false; - } - - /* ES6 9.1.2 step 5 forbids changing [[Prototype]] if not [[Extensible]]. */ - bool extensible; - if (!JSObject::isExtensible(cx, obj, &extensible)) - return false; - if (!extensible) { - *succeeded = false; - return true; - } - - /* ES6 9.1.2 step 6 forbids generating cyclical prototype chains. */ - js::RootedObject obj2(cx); - for (obj2 = proto; obj2; ) { - if (obj2 == obj) { - *succeeded = false; - return true; - } - - if (!JSObject::getProto(cx, obj2, &obj2)) - return false; - } - - JS::Rooted taggedProto(cx, js::TaggedProto(proto)); - *succeeded = SetClassAndProto(cx, obj, obj->getClass(), taggedProto); - return *succeeded; -} - -/* static */ inline bool -JSObject::isExtensible(js::ExclusiveContext *cx, js::HandleObject obj, bool *extensible) -{ - if (obj->is()) { + if (obj->is()) { if (!cx->shouldBeJSContext()) return false; - return js::Proxy::isExtensible(cx->asJSContext(), obj, extensible); + return Proxy::isExtensible(cx->asJSContext(), obj, extensible); } *extensible = obj->nonProxyIsExtensible(); diff --git a/js/src/proxy/BaseProxyHandler.cpp b/js/src/proxy/BaseProxyHandler.cpp index 1db34d369bb2..f18946fbe86a 100644 --- a/js/src/proxy/BaseProxyHandler.cpp +++ b/js/src/proxy/BaseProxyHandler.cpp @@ -92,7 +92,7 @@ BaseProxyHandler::set(JSContext *cx, HandleObject proxy, HandleObject receiver, // The spec calls this variable "parent", but that word has weird // connotations in SpiderMonkey, so let's go with "proto". RootedObject proto(cx); - if (!JSObject::getProto(cx, proxy, &proto)) + if (!GetPrototype(cx, proxy, &proto)) return false; if (proto) return JSObject::setGeneric(cx, proto, receiver, id, vp, strict); diff --git a/js/src/proxy/CrossCompartmentWrapper.cpp b/js/src/proxy/CrossCompartmentWrapper.cpp index 21cd71c7bd34..803656269b9d 100644 --- a/js/src/proxy/CrossCompartmentWrapper.cpp +++ b/js/src/proxy/CrossCompartmentWrapper.cpp @@ -84,7 +84,7 @@ CrossCompartmentWrapper::getPrototypeOf(JSContext *cx, HandleObject wrapper, { RootedObject wrapped(cx, wrappedObject(wrapper)); AutoCompartment call(cx, wrapped); - if (!JSObject::getProto(cx, wrapped, protop)) + if (!GetPrototype(cx, wrapped, protop)) return false; if (protop) protop->setDelegate(cx); diff --git a/js/src/proxy/DirectProxyHandler.cpp b/js/src/proxy/DirectProxyHandler.cpp index e3ceed5efee5..a0f419507903 100644 --- a/js/src/proxy/DirectProxyHandler.cpp +++ b/js/src/proxy/DirectProxyHandler.cpp @@ -117,35 +117,35 @@ bool DirectProxyHandler::getPrototypeOf(JSContext *cx, HandleObject proxy, MutableHandleObject protop) const { RootedObject target(cx, proxy->as().target()); - return JSObject::getProto(cx, target, protop); + return GetPrototype(cx, target, protop); } bool DirectProxyHandler::setPrototypeOf(JSContext *cx, HandleObject proxy, HandleObject proto, bool *bp) const { RootedObject target(cx, proxy->as().target()); - return JSObject::setProto(cx, target, proto, bp); + return SetPrototype(cx, target, proto, bp); } bool DirectProxyHandler::setImmutablePrototype(JSContext *cx, HandleObject proxy, bool *succeeded) const { RootedObject target(cx, proxy->as().target()); - return JSObject::setImmutablePrototype(cx, target, succeeded); + return SetImmutablePrototype(cx, target, succeeded); } bool DirectProxyHandler::preventExtensions(JSContext *cx, HandleObject proxy, bool *succeeded) const { RootedObject target(cx, proxy->as().target()); - return JSObject::preventExtensions(cx, target, succeeded); + return PreventExtensions(cx, target, succeeded); } bool DirectProxyHandler::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) const { RootedObject target(cx, proxy->as().target()); - return JSObject::isExtensible(cx, target, extensible); + return IsExtensible(cx, target, extensible); } bool diff --git a/js/src/proxy/Proxy.cpp b/js/src/proxy/Proxy.cpp index d79f9c54a097..9c8128851cd1 100644 --- a/js/src/proxy/Proxy.cpp +++ b/js/src/proxy/Proxy.cpp @@ -87,7 +87,7 @@ js::assertEnteredPolicy(JSContext *cx, JSObject *proxy, jsid id, #define INVOKE_ON_PROTOTYPE(cx, handler, proxy, protoCall) \ JS_BEGIN_MACRO \ RootedObject proto(cx); \ - if (!JSObject::getProto(cx, proxy, &proto)) \ + if (!GetPrototype(cx, proxy, &proto)) \ return false; \ if (!proto) \ return true; \ @@ -370,7 +370,7 @@ Proxy::enumerate(JSContext *cx, HandleObject proxy, MutableHandleObject objp) return false; RootedObject proto(cx); - if (!JSObject::getProto(cx, proxy, &proto)) + if (!GetPrototype(cx, proxy, &proto)) return false; if (!proto) return EnumeratedIdVectorToIterator(cx, proxy, 0, props, objp); diff --git a/js/src/proxy/ScriptedDirectProxyHandler.cpp b/js/src/proxy/ScriptedDirectProxyHandler.cpp index 8da5b2b74ca4..cfe9e809ec80 100644 --- a/js/src/proxy/ScriptedDirectProxyHandler.cpp +++ b/js/src/proxy/ScriptedDirectProxyHandler.cpp @@ -204,7 +204,7 @@ ArrayToIdVector(JSContext *cx, HandleObject proxy, HandleObject target, HandleVa // step v bool extensible; - if (!JSObject::isExtensible(cx, target, &extensible)) + if (!IsExtensible(cx, target, &extensible)) return false; if (!extensible && !isFixed) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_NEW); @@ -251,7 +251,7 @@ ArrayToIdVector(JSContext *cx, HandleObject proxy, HandleObject target, HandleVa // step iii bool extensible; - if (!JSObject::isExtensible(cx, target, &extensible)) + if (!IsExtensible(cx, target, &extensible)) return false; if (!extensible && isFixed) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE); @@ -345,7 +345,7 @@ ScriptedDirectProxyHandler::preventExtensions(JSContext *cx, HandleObject proxy, // Step 11. if (booleanTrapResult) { bool extensible; - if (!JSObject::isExtensible(cx, target, &extensible)) + if (!IsExtensible(cx, target, &extensible)) return false; if (extensible) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_AS_NON_EXTENSIBLE); @@ -396,7 +396,7 @@ ScriptedDirectProxyHandler::isExtensible(JSContext *cx, HandleObject proxy, bool // step 10-11 bool targetResult; - if (!JSObject::isExtensible(cx, target, &targetResult)) + if (!IsExtensible(cx, target, &targetResult)) return false; // step 12 @@ -423,7 +423,7 @@ ScriptedDirectProxyHandler::getPropertyDescriptor(JSContext *cx, HandleObject pr if (desc.object()) return true; RootedObject proto(cx); - if (!JSObject::getProto(cx, proxy, &proto)) + if (!GetPrototype(cx, proxy, &proto)) return false; if (!proto) { MOZ_ASSERT(!desc.object()); @@ -498,7 +498,7 @@ ScriptedDirectProxyHandler::getOwnPropertyDescriptor(JSContext *cx, HandleObject // substep c-e bool extensibleTarget; - if (!JSObject::isExtensible(cx, target, &extensibleTarget)) + if (!IsExtensible(cx, target, &extensibleTarget)) return false; if (!extensibleTarget) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE); @@ -512,7 +512,7 @@ ScriptedDirectProxyHandler::getOwnPropertyDescriptor(JSContext *cx, HandleObject // step 14-15 bool extensibleTarget; - if (!JSObject::isExtensible(cx, target, &extensibleTarget)) + if (!IsExtensible(cx, target, &extensibleTarget)) return false; // step 16-17 @@ -606,7 +606,7 @@ ScriptedDirectProxyHandler::defineProperty(JSContext *cx, HandleObject proxy, Ha // step 16-17 bool extensibleTarget; - if (!JSObject::isExtensible(cx, target, &extensibleTarget)) + if (!IsExtensible(cx, target, &extensibleTarget)) return false; // step 18-19 @@ -853,7 +853,7 @@ ScriptedDirectProxyHandler::has(JSContext *cx, HandleObject proxy, HandleId id, } bool extensible; - if (!JSObject::isExtensible(cx, target, &extensible)) + if (!IsExtensible(cx, target, &extensible)) return false; if (!extensible) { JS_ReportErrorNumber(cx, js_GetErrorMessage, nullptr, JSMSG_CANT_REPORT_E_AS_NE); diff --git a/js/src/vm/Debugger.cpp b/js/src/vm/Debugger.cpp index add34fdd6d50..7e89fa301a1f 100644 --- a/js/src/vm/Debugger.cpp +++ b/js/src/vm/Debugger.cpp @@ -6234,7 +6234,7 @@ DebuggerObject_getProto(JSContext *cx, unsigned argc, Value *vp) RootedObject proto(cx); { AutoCompartment ac(cx, refobj); - if (!JSObject::getProto(cx, refobj, &proto)) + if (!GetPrototype(cx, refobj, &proto)) return false; } RootedValue protov(cx, ObjectOrNullValue(proto)); @@ -6708,7 +6708,7 @@ DebuggerObject_deleteProperty(JSContext *cx, unsigned argc, Value *vp) return true; } -enum SealHelperOp { Seal, Freeze, PreventExtensions }; +enum SealHelperOp { OpSeal, OpFreeze, OpPreventExtensions }; static bool DebuggerObject_sealHelper(JSContext *cx, unsigned argc, Value *vp, SealHelperOp op, const char *name) @@ -6719,14 +6719,14 @@ DebuggerObject_sealHelper(JSContext *cx, unsigned argc, Value *vp, SealHelperOp ac.emplace(cx, obj); ErrorCopier ec(ac); bool ok; - if (op == Seal) { + if (op == OpSeal) { ok = JSObject::seal(cx, obj); - } else if (op == Freeze) { + } else if (op == OpFreeze) { ok = JSObject::freeze(cx, obj); } else { - MOZ_ASSERT(op == PreventExtensions); + MOZ_ASSERT(op == OpPreventExtensions); bool succeeded; - ok = JSObject::preventExtensions(cx, obj, &succeeded); + ok = PreventExtensions(cx, obj, &succeeded); if (!ok) return false; if (!succeeded) { @@ -6743,19 +6743,19 @@ DebuggerObject_sealHelper(JSContext *cx, unsigned argc, Value *vp, SealHelperOp static bool DebuggerObject_seal(JSContext *cx, unsigned argc, Value *vp) { - return DebuggerObject_sealHelper(cx, argc, vp, Seal, "seal"); + return DebuggerObject_sealHelper(cx, argc, vp, OpSeal, "seal"); } static bool DebuggerObject_freeze(JSContext *cx, unsigned argc, Value *vp) { - return DebuggerObject_sealHelper(cx, argc, vp, Freeze, "freeze"); + return DebuggerObject_sealHelper(cx, argc, vp, OpFreeze, "freeze"); } static bool DebuggerObject_preventExtensions(JSContext *cx, unsigned argc, Value *vp) { - return DebuggerObject_sealHelper(cx, argc, vp, PreventExtensions, "preventExtensions"); + return DebuggerObject_sealHelper(cx, argc, vp, OpPreventExtensions, "preventExtensions"); } static bool @@ -6768,14 +6768,14 @@ DebuggerObject_isSealedHelper(JSContext *cx, unsigned argc, Value *vp, SealHelpe ac.emplace(cx, obj); ErrorCopier ec(ac); bool r; - if (op == Seal) { + if (op == OpSeal) { if (!JSObject::isSealed(cx, obj, &r)) return false; - } else if (op == Freeze) { + } else if (op == OpFreeze) { if (!JSObject::isFrozen(cx, obj, &r)) return false; } else { - if (!JSObject::isExtensible(cx, obj, &r)) + if (!IsExtensible(cx, obj, &r)) return false; } args.rval().setBoolean(r); @@ -6785,19 +6785,19 @@ DebuggerObject_isSealedHelper(JSContext *cx, unsigned argc, Value *vp, SealHelpe static bool DebuggerObject_isSealed(JSContext *cx, unsigned argc, Value *vp) { - return DebuggerObject_isSealedHelper(cx, argc, vp, Seal, "isSealed"); + return DebuggerObject_isSealedHelper(cx, argc, vp, OpSeal, "isSealed"); } static bool DebuggerObject_isFrozen(JSContext *cx, unsigned argc, Value *vp) { - return DebuggerObject_isSealedHelper(cx, argc, vp, Freeze, "isFrozen"); + return DebuggerObject_isSealedHelper(cx, argc, vp, OpFreeze, "isFrozen"); } static bool DebuggerObject_isExtensible(JSContext *cx, unsigned argc, Value *vp) { - return DebuggerObject_isSealedHelper(cx, argc, vp, PreventExtensions, "isExtensible"); + return DebuggerObject_isSealedHelper(cx, argc, vp, OpPreventExtensions, "isExtensible"); } enum ApplyOrCallMode { ApplyMode, CallMode }; diff --git a/js/src/vm/Interpreter-inl.h b/js/src/vm/Interpreter-inl.h index 2b6722e2c6b9..787e1b479783 100644 --- a/js/src/vm/Interpreter-inl.h +++ b/js/src/vm/Interpreter-inl.h @@ -661,7 +661,7 @@ ProcessCallSiteObjOperation(JSContext *cx, RootedObject &cso, RootedObject &raw, RootedValue &rawValue) { bool extensible; - if (!JSObject::isExtensible(cx, cso, &extensible)) + if (!IsExtensible(cx, cso, &extensible)) return false; if (extensible) { JSAtom *name = cx->names().raw; diff --git a/js/src/vm/Interpreter.cpp b/js/src/vm/Interpreter.cpp index 38e5c203031d..c4458c83ee05 100644 --- a/js/src/vm/Interpreter.cpp +++ b/js/src/vm/Interpreter.cpp @@ -3157,7 +3157,7 @@ CASE(JSOP_MUTATEPROTO) MOZ_ASSERT(obj->is()); bool succeeded; - if (!JSObject::setProto(cx, obj, newProto, &succeeded)) + if (!SetPrototype(cx, obj, newProto, &succeeded)) goto error; MOZ_ASSERT(succeeded); } diff --git a/js/src/vm/NativeObject.cpp b/js/src/vm/NativeObject.cpp index e14557c21ead..8f1537625b46 100644 --- a/js/src/vm/NativeObject.cpp +++ b/js/src/vm/NativeObject.cpp @@ -136,21 +136,6 @@ ObjectElements::MakeElementsCopyOnWrite(ExclusiveContext *cx, NativeObject *obj) return true; } -/* static */ bool -JSObject::setImmutablePrototype(ExclusiveContext *cx, HandleObject obj, bool *succeeded) -{ - if (obj->hasLazyPrototype()) { - if (!cx->shouldBeJSContext()) - return false; - return Proxy::setImmutablePrototype(cx->asJSContext(), obj, succeeded); - } - - if (!obj->setFlag(cx, BaseShape::IMMUTABLE_PROTOTYPE)) - return false; - *succeeded = true; - return true; -} - #ifdef DEBUG void js::NativeObject::checkShapeConsistency() @@ -1852,7 +1837,7 @@ SetPropertyByDefining(JSContext *cx, HandleNativeObject obj, HandleObject receiv // enforced by [[DefineOwnProperty]], but we haven't implemented that yet.) if (!existing) { bool extensible; - if (!JSObject::isExtensible(cx, receiver, &extensible)) + if (!IsExtensible(cx, receiver, &extensible)) return false; if (!extensible) { // Error in strict mode code, warn with extra warnings option, diff --git a/js/src/vm/Shape.cpp b/js/src/vm/Shape.cpp index c64a1cb07c28..70b86fbb4505 100644 --- a/js/src/vm/Shape.cpp +++ b/js/src/vm/Shape.cpp @@ -496,7 +496,7 @@ NativeObject::addProperty(ExclusiveContext *cx, HandleNativeObject obj, HandleId MOZ_ASSERT(setter != JS_StrictPropertyStub); bool extensible; - if (!JSObject::isExtensible(cx, obj, &extensible)) + if (!IsExtensible(cx, obj, &extensible)) return nullptr; if (!extensible) { if (cx->isJSContext()) @@ -735,7 +735,7 @@ NativeObject::putProperty(ExclusiveContext *cx, HandleNativeObject obj, HandleId */ bool extensible; - if (!JSObject::isExtensible(cx, obj, &extensible)) + if (!IsExtensible(cx, obj, &extensible)) return nullptr; if (!extensible) { @@ -1230,38 +1230,6 @@ Shape::setObjectMetadata(JSContext *cx, JSObject *metadata, TaggedProto proto, S return replaceLastProperty(cx, base, proto, lastRoot); } -/* static */ bool -JSObject::preventExtensions(JSContext *cx, HandleObject obj, bool *succeeded) -{ - if (obj->is()) - return js::Proxy::preventExtensions(cx, obj, succeeded); - - if (!obj->nonProxyIsExtensible()) { - *succeeded = true; - return true; - } - - /* - * Force lazy properties to be resolved by iterating over the objects' own - * properties. - */ - AutoIdVector props(cx); - if (!js::GetPropertyKeys(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY, &props)) - return false; - - /* - * Convert all dense elements to sparse properties. This will shrink the - * initialized length and capacity of the object to zero and ensure that no - * new dense elements can be added without calling growElements(), which - * checks isExtensible(). - */ - if (obj->isNative() && !NativeObject::sparsifyDenseElements(cx, obj.as())) - return false; - - *succeeded = true; - return obj->setFlag(cx, BaseShape::NOT_EXTENSIBLE, GENERATE_SHAPE); -} - bool JSObject::setFlag(ExclusiveContext *cx, /*BaseShape::Flag*/ uint32_t flag_, GenerateShape generateShape)