Bug 750307 - "Assertion failure: isBoolean()" in RegExpObject::ignoreCase after redefining nonconfigurable data property. r=Waldo.

This commit is contained in:
Jason Orendorff 2012-06-11 16:31:52 -05:00
parent a3e36d07fe
commit 7a383385cd
4 changed files with 78 additions and 8 deletions

View File

@ -0,0 +1,6 @@
load(libdir + "asserts.js");
var g = newGlobal('new-compartment');
var a = g.RegExp("x");
assertThrowsInstanceOf(function () { Object.defineProperty(a, "ignoreCase", {value: undefined}); },
g.TypeError);
a.toString();

View File

@ -1881,6 +1881,50 @@ Reject(JSContext *cx, JSObject *obj, unsigned errorNumber, bool throwError, bool
return JS_TRUE;
}
// See comments on CheckDefineProperty in jsobj.h.
//
// DefinePropertyOnObject has its own implementation of these checks.
//
bool
js::CheckDefineProperty(JSContext *cx, HandleObject obj, HandleId id, HandleValue value,
PropertyOp getter, StrictPropertyOp setter, unsigned attrs)
{
if (!obj->isNative())
return true;
// ES5 8.12.9 Step 1. Even though we know obj is native, we use generic
// APIs for shorter, more readable code.
AutoPropertyDescriptorRooter desc(cx);
if (!GetOwnPropertyDescriptor(cx, obj, id, &desc))
return false;
// This does not have to check obj->isExtensible() 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)) {
// Steps 6-11, skipping step 10.a.ii. Prohibit redefining a permanent
// property with different metadata, except to make a writable property
// non-writable.
if (getter != desc.getter ||
setter != desc.setter ||
(attrs != desc.attrs && attrs != (desc.attrs | JSPROP_READONLY)))
{
return Throw(cx, id, JSMSG_CANT_REDEFINE_PROP);
}
// Step 10.a.ii. Prohibit changing the value of a non-configurable,
// non-writable data property.
if ((desc.attrs & (JSPROP_GETTER | JSPROP_SETTER | JSPROP_READONLY)) == JSPROP_READONLY) {
bool same;
if (!SameValue(cx, value, desc.value, &same))
return false;
if (!same)
return JSObject::reportReadOnly(cx, id);
}
}
return true;
}
static JSBool
DefinePropertyOnObject(JSContext *cx, HandleObject obj, HandleId id, const PropDesc &desc,
bool throwError, bool *rval)
@ -5162,8 +5206,8 @@ js::CheckUndeclaredVarAssignment(JSContext *cx, JSString *propname)
JSMSG_UNDECLARED_VAR, bytes.ptr());
}
static bool
ReportReadOnly(JSContext *cx, jsid id, unsigned report)
bool
JSObject::reportReadOnly(JSContext *cx, jsid id, unsigned report)
{
return js_ReportValueErrorFlags(cx, report, JSMSG_READ_ONLY,
JSDVG_IGNORE_STACK, IdToValue(id), NULL,
@ -5232,9 +5276,9 @@ baseops::SetPropertyHelper(JSContext *cx, HandleObject obj, HandleId id, unsigne
if (pd.attrs & JSPROP_READONLY) {
if (strict)
return ReportReadOnly(cx, id, JSREPORT_ERROR);
return JSObject::reportReadOnly(cx, id, JSREPORT_ERROR);
if (cx->hasStrictOption())
return ReportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
return JSObject::reportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
return true;
}
}
@ -5276,9 +5320,9 @@ baseops::SetPropertyHelper(JSContext *cx, HandleObject obj, HandleId id, unsigne
if (!shape->writable()) {
/* Error in strict mode code, warn with strict option, otherwise do nothing. */
if (strict)
return ReportReadOnly(cx, id, JSREPORT_ERROR);
return JSObject::reportReadOnly(cx, id, JSREPORT_ERROR);
if (cx->hasStrictOption())
return ReportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
return JSObject::reportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
return JS_TRUE;
}
}

View File

@ -699,6 +699,7 @@ struct JSObject : public js::ObjectImpl
void freeSlot(JSContext *cx, uint32_t slot);
public:
static bool reportReadOnly(JSContext *cx, jsid id, unsigned report = JSREPORT_ERROR);
bool reportNotConfigurable(JSContext* cx, jsid id, unsigned report = JSREPORT_ERROR);
bool reportNotExtensible(JSContext *cx, unsigned report = JSREPORT_ERROR);
@ -1400,6 +1401,23 @@ Throw(JSContext *cx, jsid id, unsigned errorNumber);
extern bool
Throw(JSContext *cx, JSObject *obj, unsigned errorNumber);
/*
* Helper function. To approximate a call to the [[DefineOwnProperty]] internal
* method described in ES5, first call this, then call JS_DefinePropertyById.
*
* JS_DefinePropertyById by itself does not enforce the invariants on
* non-configurable properties when obj->isNative(). This function performs the
* relevant checks (specified in ES5 8.12.9 [[DefineOwnProperty]] steps 1-11),
* but only if obj is native.
*
* The reason for the messiness here is that ES5 uses [[DefineOwnProperty]] as
* a sort of extension point, but there is no hook in js::Class,
* js::ProxyHandler, or the JSAPI with precisely the right semantics for it.
*/
extern bool
CheckDefineProperty(JSContext *cx, HandleObject obj, HandleId id, HandleValue value,
PropertyOp getter, StrictPropertyOp setter, unsigned attrs);
} /* namespace js */
#endif /* jsobj_h___ */

View File

@ -400,8 +400,10 @@ bool
IndirectProxyHandler::defineProperty(JSContext *cx, JSObject *proxy, jsid id,
PropertyDescriptor *desc)
{
return JS_DefinePropertyById(cx, GetProxyTargetObject(proxy), id,
desc->value, desc->getter, desc->setter,
RootedObject obj(cx, GetProxyTargetObject(proxy));
return CheckDefineProperty(cx, obj, RootedId(cx, id), RootedValue(cx, desc->value),
desc->getter, desc->setter, desc->attrs) &&
JS_DefinePropertyById(cx, obj, id, desc->value, desc->getter, desc->setter,
desc->attrs);
}