Bug 888106 - Add too-much-recursion detection to isExtensible tests, and make the isExtensible hook capable of failing. r=bholley, r=ejpbruel

--HG--
extra : rebase_source : fe7345322f87dd214aa5122ea8704750e8b2375a
This commit is contained in:
Jeff Walden 2013-06-28 14:01:09 -07:00
parent 453bb904cf
commit 63dbd9617f
25 changed files with 189 additions and 79 deletions

View File

@ -569,7 +569,8 @@ public:
virtual void finalize(JSFreeOp *fop, JSObject *proxy) MOZ_OVERRIDE;
// Fundamental traps
virtual bool isExtensible(JSObject *proxy) MOZ_OVERRIDE;
virtual bool isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible)
MOZ_OVERRIDE;
virtual bool preventExtensions(JSContext *cx,
JS::Handle<JSObject*> proxy) MOZ_OVERRIDE;
virtual bool getPropertyDescriptor(JSContext* cx,
@ -641,11 +642,13 @@ protected:
};
bool
nsOuterWindowProxy::isExtensible(JSObject *proxy)
nsOuterWindowProxy::isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy,
bool *extensible)
{
// If [[Extensible]] could be false, then navigating a window could navigate
// to a window that's [[Extensible]] after being at one that wasn't: an
// invariant violation. So always report true for this.
*extensible = true;
return true;
}

View File

@ -144,9 +144,11 @@ DOMProxyHandler::EnsureExpandoObject(JSContext* cx, JS::Handle<JSObject*> obj)
}
bool
DOMProxyHandler::isExtensible(JSObject *proxy)
DOMProxyHandler::isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible)
{
return true; // always extensible per WebIDL
// always extensible per WebIDL
*extensible = true;
return true;
}
bool

View File

@ -51,7 +51,7 @@ public:
JS::Handle<jsid> id, bool* bp) MOZ_OVERRIDE;
bool enumerate(JSContext* cx, JS::Handle<JSObject*> proxy, JS::AutoIdVector& props) MOZ_OVERRIDE;
bool has(JSContext* cx, JS::Handle<JSObject*> proxy, JS::Handle<jsid> id, bool* bp) MOZ_OVERRIDE;
bool isExtensible(JSObject *proxy) MOZ_OVERRIDE;
bool isExtensible(JSContext *cx, JS::Handle<JSObject*> proxy, bool *extensible) MOZ_OVERRIDE;
static JSObject* GetExpandoObject(JSObject* obj)
{

View File

@ -603,7 +603,10 @@ Collator(JSContext *cx, CallArgs args, bool construct)
return false;
// 10.1.2.1 step 5
if (!obj->isExtensible())
bool extensible;
if (!JSObject::isExtensible(cx, obj, &extensible))
return false;
if (!extensible)
return Throw(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE);
} else {
// 10.1.2.1 step 3.a
@ -1081,7 +1084,10 @@ NumberFormat(JSContext *cx, CallArgs args, bool construct)
return false;
// 11.1.2.1 step 5
if (!obj->isExtensible())
bool extensible;
if (!JSObject::isExtensible(cx, obj, &extensible))
return false;
if (!extensible)
return Throw(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE);
} else {
// 11.1.2.1 step 3.a
@ -1531,7 +1537,10 @@ DateTimeFormat(JSContext *cx, CallArgs args, bool construct)
return false;
// 12.1.2.1 step 5
if (!obj->isExtensible())
bool extensible;
if (!JSObject::isExtensible(cx, obj, &extensible))
return false;
if (!extensible)
return Throw(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE);
} else {
// 12.1.2.1 step 3.a

View File

@ -885,7 +885,10 @@ obj_isExtensible(JSContext *cx, unsigned argc, Value *vp)
if (!GetFirstArgumentAsObject(cx, args, "Object.isExtensible", &obj))
return false;
args.rval().setBoolean(obj->isExtensible());
bool extensible;
if (!JSObject::isExtensible(cx, obj, &extensible))
return false;
args.rval().setBoolean(extensible);
return true;
}
@ -898,7 +901,11 @@ obj_preventExtensions(JSContext *cx, unsigned argc, Value *vp)
return false;
args.rval().setObject(*obj);
if (!obj->isExtensible())
bool extensible;
if (!JSObject::isExtensible(cx, obj, &extensible))
return false;
if (!extensible)
return true;
return JSObject::preventExtensions(cx, obj);

View File

@ -3336,7 +3336,7 @@ IsCacheableSetPropAddSlot(JSContext *cx, HandleObject obj, HandleShape oldShape,
return false;
// Object must be extensible, oldShape must be immediate parent of curShape.
if (!obj->isExtensible() || obj->lastProperty()->previous() != oldShape)
if (!obj->nonProxyIsExtensible() || obj->lastProperty()->previous() != oldShape)
return false;
// Basic shape checks.

View File

@ -2065,7 +2065,7 @@ IsPropertyAddInlineable(JSContext *cx, HandleObject obj, HandleId id, uint32_t o
if (obj->getClass()->addProperty != JS_PropertyStub)
return false;
if (!obj->isExtensible() || !shape->writable())
if (!obj->nonProxyIsExtensible() || !shape->writable())
return false;
// walk up the object prototype chain and ensure that all prototypes

View File

@ -3363,9 +3363,13 @@ JS_NewObjectForConstructor(JSContext *cx, JSClass *clasp, const jsval *vp)
}
JS_PUBLIC_API(JSBool)
JS_IsExtensible(JSObject *obj)
JS_IsExtensible(JSContext *cx, HandleObject obj, JSBool *extensible)
{
return obj->isExtensible();
bool isExtensible;
if (!JSObject::isExtensible(cx, obj, &isExtensible))
return false;
*extensible = isExtensible;
return true;
}
JS_PUBLIC_API(JSBool)
@ -3400,7 +3404,10 @@ JS_DeepFreezeObject(JSContext *cx, JSObject *objArg)
assertSameCompartment(cx, obj);
/* Assume that non-extensible objects are already deep-frozen, to avoid divergence. */
if (!obj->isExtensible())
bool extensible;
if (!JSObject::isExtensible(cx, obj, &extensible))
return false;
if (!extensible)
return true;
if (!JSObject::freeze(cx, obj))

View File

@ -3185,7 +3185,7 @@ JS_NewObject(JSContext *cx, JSClass *clasp, JSObject *proto, JSObject *parent);
/* Queries the [[Extensible]] property of the object. */
extern JS_PUBLIC_API(JSBool)
JS_IsExtensible(JSObject *obj);
JS_IsExtensible(JSContext *cx, JS::HandleObject obj, JSBool *extensible);
extern JS_PUBLIC_API(JSBool)
JS_IsNative(JSObject *obj);

View File

@ -566,7 +566,7 @@ js::CheckDefineProperty(JSContext *cx, HandleObject obj, HandleId id, HandleValu
if (!GetOwnPropertyDescriptor(cx, obj, id, &desc))
return false;
// This does not have to check obj->isExtensible() when !desc.obj (steps
// This does not have to check obj's extensibility when !desc.obj (steps
// 2-3) because the low-level methods JSObject::{add,put}Property check
// for that.
if (desc.obj && (desc.attrs & JSPROP_PERMANENT)) {
@ -608,7 +608,10 @@ DefinePropertyOnObject(JSContext *cx, HandleObject obj, HandleId id, const PropD
/* 8.12.9 steps 2-4. */
if (!shape) {
if (!obj->isExtensible())
bool extensible;
if (!JSObject::isExtensible(cx, obj, &extensible))
return false;
if (!extensible)
return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
*rval = true;
@ -1131,7 +1134,10 @@ JSObject::sealOrFreeze(JSContext *cx, HandleObject obj, ImmutabilityType it)
assertSameCompartment(cx, obj);
JS_ASSERT(it == SEAL || it == FREEZE);
if (obj->isExtensible() && !JSObject::preventExtensions(cx, obj))
bool extensible;
if (!JSObject::isExtensible(cx, obj, &extensible))
return false;
if (extensible && !JSObject::preventExtensions(cx, obj))
return false;
AutoIdVector props(cx);
@ -1220,7 +1226,10 @@ JSObject::sealOrFreeze(JSContext *cx, HandleObject obj, ImmutabilityType it)
/* static */ bool
JSObject::isSealedOrFrozen(JSContext *cx, HandleObject obj, ImmutabilityType it, bool *resultp)
{
if (obj->isExtensible()) {
bool extensible;
if (!JSObject::isExtensible(cx, obj, &extensible))
return false;
if (extensible) {
*resultp = false;
return true;
}
@ -2730,7 +2739,7 @@ JSObject::maybeDensifySparseElements(JSContext *cx, HandleObject obj)
return ED_SPARSE;
/* Watch for conditions under which an object's elements cannot be dense. */
if (!obj->isExtensible() || obj->watched())
if (!obj->nonProxyIsExtensible() || obj->watched())
return ED_SPARSE;
/*
@ -2853,7 +2862,7 @@ ReallocateElements(ThreadSafeContext *tcx, JSObject *obj, ObjectElements *oldHea
bool
JSObject::growElements(ThreadSafeContext *tcx, uint32_t newcap)
{
JS_ASSERT(isExtensible());
JS_ASSERT(nonProxyIsExtensible());
/*
* When an object with CAPACITY_DOUBLING_MAX or fewer elements needs to
@ -4466,7 +4475,10 @@ baseops::SetPropertyHelper(JSContext *cx, HandleObject obj, HandleObject receive
}
if (!shape) {
if (!obj->isExtensible()) {
bool extensible;
if (!JSObject::isExtensible(cx, obj, &extensible))
return false;
if (!extensible) {
/* Error in strict mode code, warn with extra warnings option, otherwise do nothing. */
if (strict)
return obj->reportNotExtensible(cx);
@ -5192,7 +5204,7 @@ JSObject::dump()
fprintf(stderr, "flags:");
if (obj->isDelegate()) fprintf(stderr, " delegate");
if (!obj->isExtensible()) fprintf(stderr, " not_extensible");
if (!obj->isProxy() && !obj->nonProxyIsExtensible()) fprintf(stderr, " not_extensible");
if (obj->isIndexed()) fprintf(stderr, " indexed");
if (obj->isNative()) {

View File

@ -687,7 +687,7 @@ class JSObject : public js::ObjectImpl
*
* Notes:
* 1. getter and setter must be normalized based on flags (see jsscope.cpp).
* 2. !isExtensible() checking must be done by callers.
* 2. Checks for non-extensibility must be done by callers.
*/
static js::Shape *addPropertyInternal(JSContext *cx,
JS::HandleObject obj, JS::HandleId id,

View File

@ -384,7 +384,7 @@ JSObject::extendDenseElements(js::ThreadSafeContext *tcx,
* elements can be added/written with no extensible or watchpoint checks as
* long as there is capacity for them.
*/
if (!isExtensible() || watched()) {
if (!nonProxyIsExtensible() || watched()) {
JS_ASSERT(getDenseCapacity() == 0);
return ED_SPARSE;
}

View File

@ -625,9 +625,10 @@ DirectProxyHandler::iterate(JSContext *cx, HandleObject proxy, unsigned flags,
}
bool
DirectProxyHandler::isExtensible(JSObject *proxy)
DirectProxyHandler::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible)
{
return GetProxyTargetObject(proxy)->isExtensible();
RootedObject target(cx, GetProxyTargetObject(proxy));
return JSObject::isExtensible(cx, target, extensible);
}
bool
@ -787,7 +788,7 @@ class ScriptedIndirectProxyHandler : public BaseProxyHandler
MutableHandleValue vp) MOZ_OVERRIDE;
/* Spidermonkey extensions. */
virtual bool isExtensible(JSObject *proxy) MOZ_OVERRIDE;
virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) MOZ_OVERRIDE;
virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE;
virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE;
virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
@ -811,9 +812,10 @@ ScriptedIndirectProxyHandler::~ScriptedIndirectProxyHandler()
}
bool
ScriptedIndirectProxyHandler::isExtensible(JSObject *proxy)
ScriptedIndirectProxyHandler::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible)
{
// Scripted indirect proxies don't support extensibility changes.
*extensible = true;
return true;
}
@ -1394,7 +1396,10 @@ TrapGetOwnProperty(JSContext *cx, HandleObject proxy, HandleId id, MutableHandle
return false;
}
if (!target->isExtensible()) {
bool extensible;
if (!JSObject::isExtensible(cx, target, &extensible))
return false;
if (!extensible) {
bool found;
if (!HasOwn(cx, target, id, &found))
return false;
@ -1414,7 +1419,10 @@ TrapGetOwnProperty(JSContext *cx, HandleObject proxy, HandleId id, MutableHandle
return false;
// step 9
if (target->isExtensible() && !isFixed) {
bool extensible;
if (!JSObject::isExtensible(cx, target, &extensible))
return false;
if (extensible && !isFixed) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_REPORT_NEW);
return false;
}
@ -1495,7 +1503,10 @@ TrapDefineOwnProperty(JSContext *cx, HandleObject proxy, HandleId id, MutableHan
if (!HasOwn(cx, target, id, &isFixed))
return false;
if (!target->isExtensible() && !isFixed) {
bool extensible;
if (!JSObject::isExtensible(cx, target, &extensible))
return false;
if (!extensible && !isFixed) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_DEFINE_NEW);
return false;
}
@ -1581,7 +1592,10 @@ ArrayToIdVector(JSContext *cx, HandleObject proxy, HandleObject target, HandleVa
return false;
// step v
if (!target->isExtensible() && !isFixed) {
bool extensible;
if (!JSObject::isExtensible(cx, target, &extensible))
return false;
if (!extensible && !isFixed) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_REPORT_NEW);
return false;
}
@ -1625,7 +1639,10 @@ ArrayToIdVector(JSContext *cx, HandleObject proxy, HandleObject target, HandleVa
return false;
// step iii
if (!target->isExtensible() && isFixed) {
bool extensible;
if (!JSObject::isExtensible(cx, target, &extensible))
return false;
if (!extensible && isFixed) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_REPORT_E_AS_NE);
return false;
}
@ -1674,7 +1691,10 @@ ScriptedDirectProxyHandler::preventExtensions(JSContext *cx, HandleObject proxy)
bool success = ToBoolean(trapResult);
if (success) {
// step g
if (target->isExtensible()) {
bool extensible;
if (!JSObject::isExtensible(cx, target, &extensible))
return false;
if (extensible) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_REPORT_AS_NON_EXTENSIBLE);
return false;
}
@ -1919,7 +1939,10 @@ ScriptedDirectProxyHandler::has(JSContext *cx, HandleObject proxy, HandleId id,
return false;
}
if (!target->isExtensible()) {
bool extensible;
if (!JSObject::isExtensible(cx, target, &extensible))
return false;
if (!extensible) {
bool isFixed;
if (!HasOwn(cx, target, id, &isFixed))
return false;
@ -1979,7 +2002,10 @@ ScriptedDirectProxyHandler::hasOwn(JSContext *cx, HandleObject proxy, HandleId i
return false;
}
if (!target->isExtensible()) {
bool extensible;
if (!JSObject::isExtensible(cx, target, &extensible))
return false;
if (!extensible) {
bool isFixed;
if (!HasOwn(cx, target, id, &isFixed))
return false;
@ -1988,13 +2014,18 @@ ScriptedDirectProxyHandler::hasOwn(JSContext *cx, HandleObject proxy, HandleId i
return false;
}
}
} else if (!target->isExtensible()) {
bool isFixed;
if (!HasOwn(cx, target, id, &isFixed))
return false;
if (!isFixed) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_REPORT_NEW);
} else {
bool extensible;
if (!JSObject::isExtensible(cx, target, &extensible))
return false;
if (!extensible) {
bool isFixed;
if (!HasOwn(cx, target, id, &isFixed))
return false;
if (!isFixed) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_REPORT_NEW);
return false;
}
}
}
@ -2578,9 +2609,10 @@ Proxy::iterate(JSContext *cx, HandleObject proxy, unsigned flags, MutableHandleV
}
bool
Proxy::isExtensible(JSObject *proxy)
Proxy::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible)
{
return GetProxyHandler(proxy)->isExtensible(proxy);
JS_CHECK_RECURSION(cx, return false);
return GetProxyHandler(proxy)->isExtensible(cx, proxy, extensible);
}
bool

View File

@ -129,7 +129,7 @@ class JS_FRIEND_API(BaseProxyHandler)
MutableHandleValue vp);
/* Spidermonkey extensions. */
virtual bool isExtensible(JSObject *proxy) = 0;
virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) = 0;
virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args);
virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args);
virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args);
@ -190,7 +190,7 @@ class JS_PUBLIC_API(DirectProxyHandler) : public BaseProxyHandler
MutableHandleValue vp) MOZ_OVERRIDE;
/* Spidermonkey extensions. */
virtual bool isExtensible(JSObject *proxy) MOZ_OVERRIDE;
virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) MOZ_OVERRIDE;
virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE;
virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE;
virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
@ -242,7 +242,7 @@ class Proxy
static bool iterate(JSContext *cx, HandleObject proxy, unsigned flags, MutableHandleValue vp);
/* Spidermonkey extensions. */
static bool isExtensible(JSObject *proxy);
static bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible);
static bool call(JSContext *cx, HandleObject proxy, const CallArgs &args);
static bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args);
static bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl, CallArgs args);

View File

@ -191,18 +191,12 @@ bool CrossCompartmentWrapper::finalizeInBackground(Value priv)
#define NOTHING (true)
bool
CrossCompartmentWrapper::isExtensible(JSObject *wrapper)
CrossCompartmentWrapper::isExtensible(JSContext *cx, HandleObject wrapper, bool *extensible)
{
// The lack of a context to enter a compartment here is troublesome. We
// don't know anything about the wrapped object (it might even be a
// proxy!), and embeddings' proxy handlers could theoretically trigger
// compartment mismatches here (because isExtensible wouldn't be called in
// the wrapped object's compartment. But that's probably not very likely.
// (Famous last words.)
//
// Given that we're likely going to make this method take a context and
// maybe be fallible at some point, punt on the issue for now.
return wrappedObject(wrapper)->isExtensible();
PIERCE(cx, wrapper,
NOTHING,
Wrapper::isExtensible(cx, wrapper, extensible),
NOTHING);
}
bool
@ -605,11 +599,12 @@ SecurityWrapper<Base>::SecurityWrapper(unsigned flags)
template <class Base>
bool
SecurityWrapper<Base>::isExtensible(JSObject *wrapper)
SecurityWrapper<Base>::isExtensible(JSContext *cx, HandleObject wrapper, bool *extensible)
{
// Just like BaseProxyHandler, SecurityWrappers claim by default to always
// be extensible, so as not to leak information about the state of the
// underlying wrapped thing.
*extensible = true;
return true;
}
@ -691,10 +686,11 @@ DeadObjectProxy::DeadObjectProxy()
}
bool
DeadObjectProxy::isExtensible(JSObject *proxy)
DeadObjectProxy::isExtensible(JSContext *cx, HandleObject proxy, bool *extensible)
{
// This is kind of meaningless, but dead-object semantics aside,
// [[Extensible]] always being true is consistent with other proxy types.
*extensible = true;
return true;
}

View File

@ -105,7 +105,7 @@ class JS_FRIEND_API(CrossCompartmentWrapper) : public Wrapper
MutableHandleValue vp) MOZ_OVERRIDE;
/* Spidermonkey extensions. */
virtual bool isExtensible(JSObject *proxy) MOZ_OVERRIDE;
virtual bool isExtensible(JSContext *cx, HandleObject wrapper, bool *extensible) MOZ_OVERRIDE;
virtual bool call(JSContext *cx, HandleObject wrapper, const CallArgs &args) MOZ_OVERRIDE;
virtual bool construct(JSContext *cx, HandleObject wrapper, const CallArgs &args) MOZ_OVERRIDE;
virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,
@ -139,7 +139,7 @@ class JS_FRIEND_API(SecurityWrapper) : public Base
public:
SecurityWrapper(unsigned flags);
virtual bool isExtensible(JSObject *wrapper) MOZ_OVERRIDE;
virtual bool isExtensible(JSContext *cx, HandleObject wrapper, bool *extensible) MOZ_OVERRIDE;
virtual bool preventExtensions(JSContext *cx, HandleObject wrapper) MOZ_OVERRIDE;
virtual bool enter(JSContext *cx, HandleObject wrapper, HandleId id, Wrapper::Action act,
bool *bp) MOZ_OVERRIDE;
@ -185,7 +185,7 @@ class JS_FRIEND_API(DeadObjectProxy) : public BaseProxyHandler
virtual bool enumerate(JSContext *cx, HandleObject wrapper, AutoIdVector &props) MOZ_OVERRIDE;
/* Spidermonkey extensions. */
virtual bool isExtensible(JSObject *proxy) MOZ_OVERRIDE;
virtual bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) MOZ_OVERRIDE;
virtual bool call(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE;
virtual bool construct(JSContext *cx, HandleObject proxy, const CallArgs &args) MOZ_OVERRIDE;
virtual bool nativeCall(JSContext *cx, IsAcceptableThis test, NativeImpl impl,

View File

@ -4776,7 +4776,10 @@ DebuggerObject_sealHelper(JSContext *cx, unsigned argc, Value *vp, SealHelperOp
ok = JSObject::freeze(cx, obj);
} else {
JS_ASSERT(op == PreventExtensions);
if (!obj->isExtensible()) {
bool extensible;
if (!JSObject::isExtensible(cx, obj, &extensible))
return false;
if (!extensible) {
args.rval().setUndefined();
return true;
}
@ -4823,7 +4826,8 @@ DebuggerObject_isSealedHelper(JSContext *cx, unsigned argc, Value *vp, SealHelpe
if (!JSObject::isFrozen(cx, obj, &r))
return false;
} else {
r = obj->isExtensible();
if (!JSObject::isExtensible(cx, obj, &r))
return false;
}
args.rval().setBoolean(r);
return true;

View File

@ -124,7 +124,10 @@ ProtoSetterImpl(JSContext *cx, CallArgs args)
Rooted<JSObject*> obj(cx, &args.thisv().toObject());
/* ES5 8.6.2 forbids changing [[Prototype]] if not [[Extensible]]. */
if (!obj->isExtensible()) {
bool extensible;
if (!JSObject::isExtensible(cx, obj, &extensible))
return false;
if (!extensible) {
obj->reportNotExtensible(cx);
return false;
}

View File

@ -39,15 +39,27 @@ js::ObjectImpl::nativeContainsPure(Shape *shape)
}
inline bool
js::ObjectImpl::isExtensible() const
js::ObjectImpl::nonProxyIsExtensible() const
{
if (this->isProxy())
return Proxy::isExtensible(const_cast<JSObject*>(this->asObjectPtr()));
MOZ_ASSERT(!isProxy());
// [[Extensible]] for ordinary non-proxy objects is an object flag.
return !lastProperty()->hasObjectFlag(BaseShape::NOT_EXTENSIBLE);
}
/* static */ inline bool
js::ObjectImpl::isExtensible(JSContext *cx, js::Handle<ObjectImpl*> obj, bool *extensible)
{
if (obj->isProxy()) {
HandleObject h =
HandleObject::fromMarkedLocation(reinterpret_cast<JSObject* const*>(obj.address()));
return Proxy::isExtensible(cx, h, extensible);
}
*extensible = obj->nonProxyIsExtensible();
return true;
}
inline bool
js::ObjectImpl::isNative() const
{

View File

@ -481,7 +481,10 @@ DenseElementsHeader::defineElement(JSContext *cx, Handle<ObjectImpl*> obj, uint3
* If the element doesn't exist, we can only add it if the object is
* extensible.
*/
if (!obj->isExtensible()) {
bool extensible;
if (!JSObject::isExtensible(cx, obj, &extensible))
return false;
if (!extensible) {
*succeeded = false;
if (!shouldThrow)
return true;

View File

@ -1239,7 +1239,14 @@ class ObjectImpl : public gc::Cell
return type_->clasp;
}
inline bool isExtensible() const;
static inline bool
isExtensible(JSContext *cx, Handle<ObjectImpl*> 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
// all as friends.
inline bool nonProxyIsExtensible() const;
// Attempt to change the [[Extensible]] bit on |obj| to false. Callers
// must ensure that |obj| is currently extensible before calling this!

View File

@ -1358,10 +1358,11 @@ class DebugScopeProxy : public BaseProxyHandler
DebugScopeProxy() : BaseProxyHandler(&family) {}
bool isExtensible(JSObject *proxy) MOZ_OVERRIDE
bool isExtensible(JSContext *cx, HandleObject proxy, bool *extensible) MOZ_OVERRIDE
{
// always [[Extensible]], can't be made non-[[Extensible]], like most
// proxies
*extensible = true;
return true;
}

View File

@ -446,7 +446,10 @@ JSObject::addProperty(JSContext *cx, HandleObject obj, HandleId id,
{
JS_ASSERT(!JSID_IS_VOID(id));
if (!obj->isExtensible()) {
bool extensible;
if (!JSObject::isExtensible(cx, obj, &extensible))
return NULL;
if (!extensible) {
obj->reportNotExtensible(cx);
return NULL;
}
@ -610,7 +613,10 @@ JSObject::putProperty(JSContext *cx, HandleObject obj, HandleId id,
* You can't add properties to a non-extensible object, but you can change
* attributes of properties in such objects.
*/
if (!obj->isExtensible()) {
bool extensible;
if (!JSObject::isExtensible(cx, obj, &extensible))
return NULL;
if (!extensible) {
obj->reportNotExtensible(cx);
return NULL;
}
@ -1077,9 +1083,14 @@ Shape::setObjectMetadata(JSContext *cx, JSObject *metadata, TaggedProto proto, S
/* static */ bool
js::ObjectImpl::preventExtensions(JSContext *cx, Handle<ObjectImpl*> obj)
{
MOZ_ASSERT(obj->isExtensible(),
#ifdef DEBUG
bool extensible;
if (!JSObject::isExtensible(cx, obj, &extensible))
return false;
MOZ_ASSERT(extensible,
"Callers must ensure |obj| is extensible before calling "
"preventExtensions");
#endif
if (obj->isProxy()) {
RootedObject object(cx, obj->asObjectPtr());

View File

@ -1306,13 +1306,14 @@ DEBUG_CheckXBLLookup(JSContext *cx, JSPropertyDescriptor *desc)
template <typename Base, typename Traits>
bool
XrayWrapper<Base, Traits>::isExtensible(JSObject *wrapper)
XrayWrapper<Base, Traits>::isExtensible(JSContext *cx, JS::Handle<JSObject*> wrapper, bool *extensible)
{
// Xray wrappers are supposed to provide a clean view of the target
// reflector, hiding any modifications by script in the target scope. So
// even if that script freezes the reflector, we don't want to make that
// visible to the caller. DOM reflectors are always extensible by default,
// so we can just return true here.
*extensible = true;
return true;
}

View File

@ -70,7 +70,7 @@ class XrayWrapper : public Base {
virtual ~XrayWrapper();
/* Fundamental proxy traps. */
virtual bool isExtensible(JSObject *wrapper) MOZ_OVERRIDE;
virtual bool isExtensible(JSContext *cx, JS::Handle<JSObject*> wrapper, bool *extensible) MOZ_OVERRIDE;
virtual bool preventExtensions(JSContext *cx, JS::Handle<JSObject*> wrapper) MOZ_OVERRIDE;
virtual bool getPropertyDescriptor(JSContext *cx, JS::Handle<JSObject*> wrapper, JS::Handle<jsid> id,
js::PropertyDescriptor *desc, unsigned flags);