Fix shape vs. slot management under putProperty, plus related layering and error reporting fixes (596805, r=jorendorff).

This commit is contained in:
Brendan Eich 2010-09-16 11:56:54 -07:00
parent 6cd6f4d73e
commit d66fd134da
10 changed files with 293 additions and 207 deletions

View File

@ -100,7 +100,7 @@ MSG_DEF(JSMSG_NEED_DIET, 17, 1, JSEXN_INTERNALERR, "{0} too large"
MSG_DEF(JSMSG_TOO_MANY_LOCAL_ROOTS, 18, 0, JSEXN_ERR, "out of local root space")
MSG_DEF(JSMSG_READ_ONLY, 19, 1, JSEXN_TYPEERR, "{0} is read-only")
MSG_DEF(JSMSG_BAD_FORMAL, 20, 0, JSEXN_SYNTAXERR, "malformed formal parameter")
MSG_DEF(JSMSG_CANT_DELETE, 21, 1, JSEXN_TYPEERR, "property '{0}' is non-configurable and cannot be deleted")
MSG_DEF(JSMSG_CANT_DELETE, 21, 1, JSEXN_TYPEERR, "property {0} is non-configurable and can't be deleted")
MSG_DEF(JSMSG_NOT_FUNCTION, 22, 1, JSEXN_TYPEERR, "{0} is not a function")
MSG_DEF(JSMSG_NOT_CONSTRUCTOR, 23, 1, JSEXN_TYPEERR, "{0} is not a constructor")
MSG_DEF(JSMSG_SCRIPT_STACK_QUOTA, 24, 0, JSEXN_INTERNALERR, "script stack space quota is exhausted")
@ -116,7 +116,7 @@ MSG_DEF(JSMSG_BAD_RADIX, 33, 1, JSEXN_ERR, "illegal radix {0}")
MSG_DEF(JSMSG_PAREN_BEFORE_LET, 34, 0, JSEXN_SYNTAXERR, "missing ( before let head")
MSG_DEF(JSMSG_CANT_CONVERT, 35, 1, JSEXN_ERR, "can't convert {0} to an integer")
MSG_DEF(JSMSG_CYCLIC_VALUE, 36, 1, JSEXN_TYPEERR, "cyclic {0} value")
MSG_DEF(JSMSG_COMPILE_EXECED_SCRIPT, 37, 0, JSEXN_TYPEERR, "cannot compile over a script that is currently executing")
MSG_DEF(JSMSG_COMPILE_EXECED_SCRIPT, 37, 0, JSEXN_TYPEERR, "can't compile over a script that is currently executing")
MSG_DEF(JSMSG_CANT_CONVERT_TO, 38, 2, JSEXN_TYPEERR, "can't convert {0} to {1}")
MSG_DEF(JSMSG_NO_PROPERTIES, 39, 1, JSEXN_TYPEERR, "{0} has no properties")
MSG_DEF(JSMSG_CANT_FIND_CLASS, 40, 1, JSEXN_TYPEERR, "can't find class id {0}")
@ -158,7 +158,7 @@ MSG_DEF(JSMSG_PAREN_AFTER_COND, 75, 0, JSEXN_SYNTAXERR, "missing ) after
MSG_DEF(JSMSG_DESTRUCT_DUP_ARG, 76, 0, JSEXN_SYNTAXERR, "duplicate argument is mixed with destructuring pattern")
MSG_DEF(JSMSG_NAME_AFTER_DOT, 77, 0, JSEXN_SYNTAXERR, "missing name after . operator")
MSG_DEF(JSMSG_BRACKET_IN_INDEX, 78, 0, JSEXN_SYNTAXERR, "missing ] in index expression")
MSG_DEF(JSMSG_XML_WHOLE_PROGRAM, 79, 0, JSEXN_SYNTAXERR, "XML cannot be the whole program")
MSG_DEF(JSMSG_XML_WHOLE_PROGRAM, 79, 0, JSEXN_SYNTAXERR, "XML can't be the whole program")
MSG_DEF(JSMSG_PAREN_BEFORE_SWITCH, 80, 0, JSEXN_SYNTAXERR, "missing ( before switch expression")
MSG_DEF(JSMSG_PAREN_AFTER_SWITCH, 81, 0, JSEXN_SYNTAXERR, "missing ) after switch expression")
MSG_DEF(JSMSG_CURLY_BEFORE_SWITCH, 82, 0, JSEXN_SYNTAXERR, "missing { before switch body")
@ -192,7 +192,7 @@ MSG_DEF(JSMSG_SEMI_BEFORE_STMNT, 109, 0, JSEXN_SYNTAXERR, "missing ; before
MSG_DEF(JSMSG_NO_RETURN_VALUE, 110, 1, JSEXN_TYPEERR, "function {0} does not always return a value")
MSG_DEF(JSMSG_DUPLICATE_FORMAL, 111, 1, JSEXN_SYNTAXERR, "duplicate formal argument {0}")
MSG_DEF(JSMSG_EQUAL_AS_ASSIGN, 112, 1, JSEXN_SYNTAXERR, "test for equality (==) mistyped as assignment (=)?{0}")
MSG_DEF(JSMSG_OPTIMIZED_CLOSURE_LEAK, 113, 0, JSEXN_INTERNALERR, "cannot access optimized closure")
MSG_DEF(JSMSG_OPTIMIZED_CLOSURE_LEAK, 113, 0, JSEXN_INTERNALERR, "can't access optimized closure")
MSG_DEF(JSMSG_TOO_MANY_DEFAULTS, 114, 0, JSEXN_SYNTAXERR, "more than one switch default")
MSG_DEF(JSMSG_TOO_MANY_CASES, 115, 0, JSEXN_INTERNALERR, "too many switch cases")
MSG_DEF(JSMSG_BAD_SWITCH, 116, 0, JSEXN_SYNTAXERR, "invalid switch statement")
@ -298,7 +298,7 @@ MSG_DEF(JSMSG_BAD_GENERATOR_SYNTAX, 215, 1, JSEXN_SYNTAXERR, "{0} expression m
MSG_DEF(JSMSG_ARRAY_COMP_LEFTSIDE, 216, 0, JSEXN_SYNTAXERR, "invalid array comprehension left-hand side")
MSG_DEF(JSMSG_NON_XML_FILTER, 217, 1, JSEXN_TYPEERR, "XML filter is applied to non-XML value {0}")
MSG_DEF(JSMSG_EMPTY_ARRAY_REDUCE, 218, 0, JSEXN_TYPEERR, "reduce of empty array with no initial value")
MSG_DEF(JSMSG_NON_LIST_XML_METHOD, 219, 2, JSEXN_TYPEERR, "cannot call {0} method on an XML list with {1} elements")
MSG_DEF(JSMSG_NON_LIST_XML_METHOD, 219, 2, JSEXN_TYPEERR, "can't call {0} method on an XML list with {1} elements")
MSG_DEF(JSMSG_BAD_DELETE_OPERAND, 220, 0, JSEXN_SYNTAXERR, "invalid delete operand")
MSG_DEF(JSMSG_BAD_INCOP_OPERAND, 221, 0, JSEXN_SYNTAXERR, "invalid increment/decrement operand")
MSG_DEF(JSMSG_UNEXPECTED_TYPE, 222, 2, JSEXN_TYPEERR, "{0} is {1}")
@ -318,10 +318,10 @@ MSG_DEF(JSMSG_DEPRECATED_DELETE_OPERAND, 235, 0, JSEXN_SYNTAXERR, "Applying the
MSG_DEF(JSMSG_DEPRECATED_ASSIGN, 236, 1, JSEXN_SYNTAXERR, "assignment to {0} is deprecated")
MSG_DEF(JSMSG_BAD_BINDING, 237, 1, JSEXN_SYNTAXERR, "redefining {0} is deprecated")
MSG_DEF(JSMSG_INVALID_DESCRIPTOR, 238, 0, JSEXN_TYPEERR, "property descriptors must not specify a value or be writable when a getter or setter has been specified")
MSG_DEF(JSMSG_OBJECT_NOT_EXTENSIBLE, 239, 0, JSEXN_TYPEERR, "object is not extensible")
MSG_DEF(JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP, 240, 1, JSEXN_TYPEERR, "can't redefine non-configurable property '{0}'")
MSG_DEF(JSMSG_CANT_APPEND_PROPERTIES_TO_UNWRITABLE_LENGTH_ARRAY, 241, 0, JSEXN_TYPEERR, "Can't add elements past the end of an array if its length property is unwritable")
MSG_DEF(JSMSG_DEFINE_ARRAY_LENGTH_UNSUPPORTED, 242, 0, JSEXN_INTERNALERR, "defining the length property on an array is not currently supported")
MSG_DEF(JSMSG_OBJECT_NOT_EXTENSIBLE, 239, 1, JSEXN_TYPEERR, "{0} is not extensible")
MSG_DEF(JSMSG_CANT_REDEFINE_PROP, 240, 1, JSEXN_TYPEERR, "can't redefine non-configurable property '{0}'")
MSG_DEF(JSMSG_CANT_APPEND_TO_ARRAY, 241, 0, JSEXN_TYPEERR, "can't add elements past the end of an array if its length property is unwritable")
MSG_DEF(JSMSG_CANT_DEFINE_ARRAY_LENGTH,242, 0, JSEXN_INTERNALERR, "defining the length property on an array is not currently supported")
MSG_DEF(JSMSG_CANT_DEFINE_ARRAY_INDEX,243, 0, JSEXN_TYPEERR, "can't define array index property")
MSG_DEF(JSMSG_TYPED_ARRAY_BAD_INDEX, 244, 0, JSEXN_ERR, "invalid or out-of-range index")
MSG_DEF(JSMSG_TYPED_ARRAY_NEGATIVE_ARG, 245, 1, JSEXN_ERR, "argument {0} must be >= 0")

View File

@ -152,7 +152,7 @@ obj_setProto(JSContext *cx, JSObject *obj, jsid id, Value *vp)
{
/* ECMAScript 5 8.6.2 forbids changing [[Prototype]] if not [[Extensible]]. */
if (!obj->isExtensible()) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_OBJECT_NOT_EXTENSIBLE);
obj->reportNotExtensible(cx);
return false;
}
@ -1971,10 +1971,17 @@ Reject(JSContext *cx, uintN errorNumber, bool throwError, jsid id, bool *rval)
}
static JSBool
Reject(JSContext *cx, uintN errorNumber, bool throwError, bool *rval)
Reject(JSContext *cx, JSObject *obj, uintN errorNumber, bool throwError, bool *rval)
{
if (throwError) {
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, errorNumber);
if (js_ErrorFormatString[errorNumber].argCount == 1) {
js_ReportValueErrorFlags(cx, JSREPORT_ERROR, errorNumber,
JSDVG_IGNORE_STACK, ObjectValue(*obj),
NULL, NULL, NULL);
} else {
JS_ASSERT(js_ErrorFormatString[errorNumber].argCount == 0);
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, errorNumber);
}
return JS_FALSE;
}
@ -2006,7 +2013,7 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc,
/* 8.12.9 steps 2-4. */
if (!current) {
if (!obj->isExtensible())
return Reject(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
*rval = true;
@ -2094,8 +2101,8 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc,
*/
if (!shape->configurable() &&
(!shape->hasDefaultGetter() || !shape->hasDefaultSetter())) {
return Reject(cx, obj2, current, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP,
throwError, desc.id, rval);
return Reject(cx, obj2, current, JSMSG_CANT_REDEFINE_PROP, throwError,
desc.id, rval);
}
if (!js_NativeGet(cx, obj, obj2, shape, JSGET_NO_METHOD_BARRIER, &v)) {
@ -2141,8 +2148,7 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc,
JS_ASSERT_IF(!desc.hasConfigurable, !desc.configurable());
if (desc.configurable() ||
(desc.hasEnumerable && desc.enumerable() != shape->enumerable())) {
return Reject(cx, obj2, current, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP, throwError,
desc.id, rval);
return Reject(cx, obj2, current, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id, rval);
}
}
@ -2150,18 +2156,16 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc,
/* 8.12.9 step 8, no validation required */
} else if (desc.isDataDescriptor() != shape->isDataDescriptor()) {
/* 8.12.9 step 9. */
if (!shape->configurable()) {
return Reject(cx, obj2, current, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP,
throwError, desc.id, rval);
}
if (!shape->configurable())
return Reject(cx, obj2, current, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id, rval);
} else if (desc.isDataDescriptor()) {
/* 8.12.9 step 10. */
JS_ASSERT(shape->isDataDescriptor());
if (!shape->configurable() && !shape->writable()) {
if ((desc.hasWritable && desc.writable()) ||
(desc.hasValue && !SameValue(desc.value, v, cx))) {
return Reject(cx, obj2, current, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP,
throwError, desc.id, rval);
return Reject(cx, obj2, current, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id,
rval);
}
}
} else {
@ -2172,8 +2176,8 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc,
!SameValue(desc.setterValue(), shape->setterOrUndefined(), cx)) ||
(desc.hasGet &&
!SameValue(desc.getterValue(), shape->getterOrUndefined(), cx))) {
return Reject(cx, obj2, current, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP,
throwError, desc.id, rval);
return Reject(cx, obj2, current, JSMSG_CANT_REDEFINE_PROP, throwError, desc.id,
rval);
}
}
}
@ -2281,7 +2285,7 @@ DefinePropertyOnArray(JSContext *cx, JSObject *obj, const PropDesc &desc,
* to define the "length" property, rather than attempting to implement
* some difficult-for-authors-to-grasp subset of that functionality.
*/
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_DEFINE_ARRAY_LENGTH_UNSUPPORTED);
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_DEFINE_ARRAY_LENGTH);
return JS_FALSE;
}
@ -2290,12 +2294,12 @@ DefinePropertyOnArray(JSContext *cx, JSObject *obj, const PropDesc &desc,
/*
// Disabled until we support defining "length":
if (index >= oldLen && lengthPropertyNotWritable())
return ThrowTypeError(cx, JSMSG_CANT_APPEND_PROPERTIES_TO_UNWRITABLE_LENGTH_ARRAY);
return ThrowTypeError(cx, JSMSG_CANT_APPEND_TO_ARRAY);
*/
if (!DefinePropertyOnObject(cx, obj, desc, false, rval))
return JS_FALSE;
if (!*rval)
return Reject(cx, JSMSG_CANT_DEFINE_ARRAY_INDEX, throwError, rval);
return Reject(cx, obj, JSMSG_CANT_DEFINE_ARRAY_INDEX, throwError, rval);
if (index >= oldLen) {
JS_ASSERT(index != UINT32_MAX);
@ -2319,7 +2323,7 @@ DefineProperty(JSContext *cx, JSObject *obj, const PropDesc &desc, bool throwErr
if (obj->getOps()->lookupProperty) {
if (obj->isProxy())
return JSProxy::defineProperty(cx, obj, desc.id, desc.pd);
return Reject(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
return Reject(cx, obj, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
}
return DefinePropertyOnObject(cx, obj, desc, throwError, rval);
@ -5101,31 +5105,28 @@ js_CheckUndeclaredVarAssignment(JSContext *cx, JSString *propname)
JSMSG_UNDECLARED_VAR, bytes);
}
namespace js {
JSBool
ReportReadOnly(JSContext* cx, jsid id, uintN flags)
bool
JSObject::reportReadOnly(JSContext* cx, jsid id, uintN report)
{
return js_ReportValueErrorFlags(cx, flags, JSMSG_READ_ONLY,
return js_ReportValueErrorFlags(cx, report, JSMSG_READ_ONLY,
JSDVG_IGNORE_STACK, IdToValue(id), NULL,
NULL, NULL);
}
JSBool
ReportNotConfigurable(JSContext* cx, jsid id, uintN flags)
bool
JSObject::reportNotConfigurable(JSContext* cx, jsid id, uintN report)
{
return js_ReportValueErrorFlags(cx, flags, JSMSG_CANT_DELETE,
return js_ReportValueErrorFlags(cx, report, JSMSG_CANT_DELETE,
JSDVG_IGNORE_STACK, IdToValue(id), NULL,
NULL, NULL);
}
JSBool
ReportNotExtensible(JSContext* cx, uintN flags)
bool
JSObject::reportNotExtensible(JSContext *cx, uintN report)
{
return JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage, NULL,
JSMSG_OBJECT_NOT_EXTENSIBLE);
}
return js_ReportValueErrorFlags(cx, report, JSMSG_OBJECT_NOT_EXTENSIBLE,
JSDVG_IGNORE_STACK, ObjectValue(*this),
NULL, NULL, NULL);
}
/*
@ -5212,9 +5213,9 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
/* Error in strict mode code, warn with strict option, otherwise do nothing. */
if (strict)
return ReportReadOnly(cx, id, 0);
return obj->reportReadOnly(cx, id);
if (JS_HAS_STRICT_OPTION(cx))
return ReportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
return obj->reportReadOnly(cx, id, JSREPORT_STRICT | JSREPORT_WARNING);
return JS_TRUE;
#ifdef JS_TRACER
@ -5300,11 +5301,10 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
if (!obj->isExtensible()) {
/* Error in strict mode code, warn with strict option, otherwise do nothing. */
if (strict)
return ReportNotExtensible(cx, 0);
return obj->reportNotExtensible(cx);
if (JS_HAS_STRICT_OPTION(cx))
return ReportNotExtensible(cx, JSREPORT_STRICT | JSREPORT_WARNING);
else
return JS_TRUE;
return obj->reportNotExtensible(cx, JSREPORT_STRICT | JSREPORT_WARNING);
return JS_TRUE;
}
/*
@ -5451,7 +5451,7 @@ js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool str
if (shape->isSharedPermanent()) {
JS_UNLOCK_OBJ(cx, proto);
if (strict)
return ReportNotConfigurable(cx, id, 0);
return obj->reportNotConfigurable(cx, id);
rval->setBoolean(false);
return true;
}
@ -5470,7 +5470,7 @@ js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, Value *rval, JSBool str
if (!shape->configurable()) {
JS_UNLOCK_OBJ(cx, obj);
if (strict)
return ReportNotConfigurable(cx, id, 0);
return obj->reportNotConfigurable(cx, id);
rval->setBoolean(false);
return true;
}

View File

@ -1005,16 +1005,25 @@ struct JSObject {
bool allocSlot(JSContext *cx, uint32 *slotp);
void freeSlot(JSContext *cx, uint32 slot);
private:
void reportReadOnlyScope(JSContext *cx);
bool reportReadOnly(JSContext* cx, jsid id, uintN report = JSREPORT_ERROR);
bool reportNotConfigurable(JSContext* cx, jsid id, uintN report = JSREPORT_ERROR);
bool reportNotExtensible(JSContext *cx, uintN report = JSREPORT_ERROR);
private:
js::Shape *getChildProperty(JSContext *cx, js::Shape *parent, js::Shape &child);
const js::Shape *addPropertyCommon(JSContext *cx, jsid id,
js::PropertyOp getter, js::PropertyOp setter,
uint32 slot, uintN attrs,
uintN flags, intN shortid,
js::Shape **spp);
/*
* Internal helper that adds a shape not yet mapped by this object.
*
* Notes:
* 1. getter and setter must be normalized based on flags (see jsscope.cpp).
* 2. !isExtensible() checking must be done by callers.
*/
const js::Shape *addPropertyInternal(JSContext *cx, jsid id,
js::PropertyOp getter, js::PropertyOp setter,
uint32 slot, uintN attrs,
uintN flags, intN shortid,
js::Shape **spp);
bool toDictionaryMode(JSContext *cx);
@ -1041,7 +1050,7 @@ struct JSObject {
const js::Shape *changeProperty(JSContext *cx, const js::Shape *shape, uintN attrs, uintN mask,
js::PropertyOp getter, js::PropertyOp setter);
/* Remove id from this object. */
/* Remove the property named by id from this object. */
bool removeProperty(JSContext *cx, jsid id);
/* Clear the scope, making it empty. */

View File

@ -513,21 +513,6 @@ JSObject::getChildProperty(JSContext *cx, Shape *parent, Shape &child)
return shape;
}
void
JSObject::reportReadOnlyScope(JSContext *cx)
{
JSString *str;
const char *bytes;
str = js_ValueToString(cx, ObjectValue(*this));
if (!str)
return;
bytes = js_GetStringBytes(cx, str);
if (!bytes)
return;
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_READ_ONLY, bytes);
}
Shape *
Shape::newDictionaryShape(JSContext *cx, const Shape &child, Shape **listp)
{
@ -720,29 +705,27 @@ JSObject::addProperty(JSContext *cx, jsid id,
{
JS_ASSERT(!JSID_IS_VOID(id));
if (!isExtensible()) {
reportNotExtensible(cx);
return NULL;
}
NormalizeGetterAndSetter(cx, this, id, attrs, flags, getter, setter);
/* Search for id with adding = true in order to claim its entry. */
Shape **spp = nativeSearch(id, true);
JS_ASSERT(!SHAPE_FETCH(spp));
return addPropertyCommon(cx, id, getter, setter, slot, attrs, flags, shortid, spp);
return addPropertyInternal(cx, id, getter, setter, slot, attrs, flags, shortid, spp);
}
const Shape *
JSObject::addPropertyCommon(JSContext *cx, jsid id,
PropertyOp getter, PropertyOp setter,
uint32 slot, uintN attrs,
uintN flags, intN shortid,
Shape **spp)
JSObject::addPropertyInternal(JSContext *cx, jsid id,
PropertyOp getter, PropertyOp setter,
uint32 slot, uintN attrs,
uintN flags, intN shortid,
Shape **spp)
{
/*
* You can't add properties to a non-extensible object, but you can change
* attributes of properties in such objects.
*/
if (!isExtensible()) {
reportReadOnlyScope(cx);
return NULL;
}
JS_ASSERT_IF(inDictionaryMode(), !lastProp->frozen());
PropertyTable *table = NULL;
if (!inDictionaryMode()) {
@ -822,137 +805,182 @@ JSObject::putProperty(JSContext *cx, jsid id,
uint32 slot, uintN attrs,
uintN flags, intN shortid)
{
Shape **spp, *shape, *overwriting;
JS_ASSERT(!JSID_IS_VOID(id));
/*
* Horrid non-strict eval, debuggers, and |default xml namespace ...| may
* extend Call objects.
*/
if (lastProp->frozen()) {
if (!Shape::newDictionaryList(cx, &lastProp))
return NULL;
JS_ASSERT(!lastProp->frozen());
}
NormalizeGetterAndSetter(cx, this, id, attrs, flags, getter, setter);
/* Search for id in order to claim its entry if table has been allocated. */
spp = nativeSearch(id, true);
shape = SHAPE_FETCH(spp);
if (!shape)
return addPropertyCommon(cx, id, getter, setter, slot, attrs, flags, shortid, spp);
Shape **spp = nativeSearch(id, true);
Shape *shape = SHAPE_FETCH(spp);
if (!shape) {
/*
* You can't add properties to a non-extensible object, but you can change
* attributes of properties in such objects.
*/
if (!isExtensible()) {
reportNotExtensible(cx);
return NULL;
}
return addPropertyInternal(cx, id, getter, setter, slot, attrs, flags, shortid, spp);
}
/* Property exists: search must have returned a valid *spp. */
JS_ASSERT(!SHAPE_IS_REMOVED(*spp));
overwriting = shape;
/*
* If all property members match, this is a redundant add and we can
* return early. If the caller wants to allocate a slot, but doesn't
* care which slot, copy shape->slot into slot so we can match shape,
* if all other members match.
* If the caller wants to allocate a slot, but doesn't care which slot,
* copy the existing shape's slot into slot so we can match shape, if all
* other members match.
*/
bool hadSlot = !shape->isAlias() && containsSlot(shape->slot);
bool hadSlot = !shape->isAlias() && shape->hasSlot();
uint32 oldSlot = shape->slot;
if (!(attrs & JSPROP_SHARED) && slot == SHAPE_INVALID_SLOT && hadSlot)
slot = oldSlot;
/*
* Now that we've possibly preserved slot, check whether all members match.
* If so, this is a redundant "put" and we can return without more work.
*/
if (shape->matchesParamsAfterId(getter, setter, slot, attrs, flags, shortid)) {
METER(redundantPuts);
return shape;
}
PropertyTable *table = inDictionaryMode() ? lastProp->table : NULL;
/*
* Overwriting a non-last property requires switching to dictionary mode.
* The shape tree is shared immutable, and we can't removeProperty and then
* addPropertyInternal because a failure under add would lose data.
*/
if (shape != lastProp && !inDictionaryMode()) {
if (!toDictionaryMode(cx))
return false;
spp = nativeSearch(shape->id);
shape = SHAPE_FETCH(spp);
}
/*
* If we are clearing shape to force the existing property that it
* describes to be overwritten, then we have to unlink shape from the
* ancestor line at lastProp->lastProp.
* Now that we have passed the lastProp->frozen() check at the top of this
* method, and the non-last-property conditioning just above, we are ready
* to overwrite.
*
* If shape is not lastProp and this scope is not in dictionary mode,
* we must switch to dictionary mode so we can unlink the non-terminal
* shape without breaking anyone sharing the property lineage via our
* prototype's property tree.
* Optimize the case of a non-frozen dictionary-mode object based on the
* property that dictionaries exclusively own their mutable shape structs,
* each of which has a unique shape number (not shared via a shape tree).
*/
Shape *oldLastProp = lastProp;
if (shape == lastProp && !inDictionaryMode()) {
removeLastProperty();
} else {
if (!inDictionaryMode()) {
if (!toDictionaryMode(cx))
if (inDictionaryMode()) {
/* FIXME bug 593129 -- slot allocation and JSObject *this must move out of here! */
if (slot == SHAPE_INVALID_SLOT && !(attrs & JSPROP_SHARED) && !(flags & Shape::ALIAS)) {
if (!allocSlot(cx, &slot))
return NULL;
spp = nativeSearch(id);
shape = SHAPE_FETCH(spp);
table = lastProp->table;
oldLastProp = lastProp;
}
shape->removeFromDictionary(this);
}
#ifdef DEBUG
if (shape == oldLastProp) {
JS_ASSERT(lastProp->slotSpan <= shape->slotSpan);
if (shape->hasSlot())
JS_ASSERT(shape->slot < shape->slotSpan);
if (lastProp->slotSpan < numSlots())
getSlotRef(lastProp->slotSpan).setUndefined();
}
#endif
/*
* If we fail later on trying to find or create a new shape, we will
* restore *spp from |overwriting|. Note that we don't bother to keep
* table->removedCount in sync, because we will fix up both *spp and
* table->entryCount shortly.
*/
if (table)
SHAPE_STORE_PRESERVING_COLLISION(spp, NULL);
{
/* Find or create a property tree node labeled by our arguments. */
Shape child(id, getter, setter, slot, attrs, flags, shortid);
shape = getChildProperty(cx, lastProp, child);
}
if (shape) {
JS_ASSERT(shape == lastProp);
if (table) {
/* Store the tree node pointer in the table entry for id. */
SHAPE_STORE_PRESERVING_COLLISION(spp, shape);
/* Move table from oldLastProp to the new lastProp, aka shape. */
JS_ASSERT(oldLastProp->table == table);
oldLastProp->setTable(NULL);
shape->setTable(table);
}
if (!lastProp->table) {
/* See comment in JSObject::addPropertyCommon about ignoring OOM here. */
lastProp->maybeHash(cx);
}
/*
* Can't fail now, so free the previous incarnation's slot if the new
* shape has no slot. But we do not need to free oldSlot (and must not,
* as trying to will botch an assertion in JSObject::freeSlot) if the
* new lastProp (shape here) has a slotSpan that does not cover it.
* We are going to mutate shape and move it to be lastProp if it isn't
* already, so we must regenerate its shape.
*/
if (hadSlot && !shape->hasSlot() && oldSlot < shape->slotSpan) {
freeSlot(cx, oldSlot);
JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals);
shape->shape = js_GenerateShape(cx, false);
/*
* Set shape->slot before calling shape->insertIntoDictionary, which
* uses it under shape->setParent.
*/
shape->slot = slot;
if (shape != lastProp) {
if (PropertyTable *table = lastProp->table) {
shape->table = table;
lastProp->table = NULL;
}
shape->removeFromDictionary(this);
shape->insertIntoDictionary(&lastProp);
} else {
if (slot != SHAPE_INVALID_SLOT && slot >= shape->slotSpan)
shape->slotSpan = slot + 1;
}
CHECK_SHAPE_CONSISTENCY(this);
METER(puts);
return shape;
shape->rawGetter = getter;
shape->rawSetter = setter;
shape->attrs = attrs;
shape->flags = flags | Shape::IN_DICTIONARY;
shape->shortid = shortid;
/*
* We are done updating shape and lastProp. Now we may need to update
* flags and objShape. In the last non-dictionary property case in the
* else clause just below, getChildProperty handles this for us.
*/
updateFlags(shape);
updateShape(cx);
} else {
/*
* Updating lastProp in a non-dictionary-mode object. Such objects
* share their shapes via a tree rooted at a prototype emptyShape, or
* perhaps a well-known compartment-wide singleton emptyShape.
*
* If any shape in the tree has a property hashtable, it is shared and
* immutable too, therefore we must not update *spp.
*/
JS_ASSERT(shape == lastProp);
removeLastProperty();
/* Find or create a property tree node labeled by our arguments. */
Shape child(id, getter, setter, slot, attrs, flags, shortid);
Shape *newShape = getChildProperty(cx, lastProp, child);
if (!newShape) {
setLastProperty(shape);
CHECK_SHAPE_CONSISTENCY(this);
METER(putFails);
return NULL;
}
shape = newShape;
}
JS_ASSERT(shape == lastProp);
if (!shape->table) {
/* See JSObject::addPropertyInternal comment about ignoring OOM. */
shape->maybeHash(cx);
}
/*
* Can't fail now, so free the previous incarnation's slot if the new shape
* has no slot. But we do not need to free oldSlot (and must not, as trying
* to will botch an assertion in JSObject::freeSlot) if the new lastProp
* (shape here) has a slotSpan that does not cover it.
*/
if (hadSlot && !shape->hasSlot()) {
if (oldSlot < shape->slotSpan)
freeSlot(cx, oldSlot);
#ifdef DEBUG
else
getSlotRef(oldSlot).setUndefined();
#endif
JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals);
}
if (table)
SHAPE_STORE_PRESERVING_COLLISION(spp, overwriting);
CHECK_SHAPE_CONSISTENCY(this);
METER(putFails);
return NULL;
METER(puts);
return shape;
}
const Shape *
JSObject::changeProperty(JSContext *cx, const Shape *shape, uintN attrs, uintN mask,
PropertyOp getter, PropertyOp setter)
{
const Shape *newShape;
JS_ASSERT_IF(inDictionaryMode(), !lastProp->frozen());
JS_ASSERT(!JSID_IS_VOID(shape->id));
JS_ASSERT(nativeContains(*shape));
@ -974,6 +1002,8 @@ JSObject::changeProperty(JSContext *cx, const Shape *shape, uintN attrs, uintN m
Shape child(shape->id, getter, setter, shape->slot, attrs, shape->flags, shape->shortid);
const Shape *newShape;
if (inDictionaryMode()) {
shape->removeFromDictionary(this);
newShape = Shape::newDictionaryShape(cx, child, &lastProp);
@ -1014,11 +1044,15 @@ JSObject::changeProperty(JSContext *cx, const Shape *shape, uintN attrs, uintN m
/*
* Let JSObject::putProperty handle this |overwriting| case, including
* the conservation of shape->slot (if it's valid). We must not call
* JSObject::removeProperty because it will free a valid shape->slot and
* JSObject::putProperty won't re-allocate it.
* removeProperty because it will free an allocated shape->slot, and
* putProperty won't re-allocate it.
*/
newShape = putProperty(cx, child.id, child.rawGetter, child.rawSetter, child.slot,
child.attrs, child.flags, child.shortid);
#ifdef DEBUG
if (newShape)
METER(changePuts);
#endif
}
#ifdef DEBUG
@ -1041,27 +1075,26 @@ JSObject::removeProperty(JSContext *cx, jsid id)
return true;
}
/* If shape is not the last property added, switch to dictionary mode. */
if (shape != lastProp) {
if (!inDictionaryMode()) {
if (!toDictionaryMode(cx))
return false;
spp = nativeSearch(shape->id);
shape = SHAPE_FETCH(spp);
}
JS_ASSERT(SHAPE_FETCH(spp) == shape);
}
/* First, if shape is unshared and not cleared, free its slot number. */
bool hadSlot = !shape->isAlias() && containsSlot(shape->slot);
/* First, if shape is unshared and not has a slot, free its slot number. */
bool hadSlot = !shape->isAlias() && shape->hasSlot();
if (hadSlot) {
freeSlot(cx, shape->slot);
JS_ATOMIC_INCREMENT(&cx->runtime->propertyRemovals);
}
/* If shape is not the last property added, switch to dictionary mode. */
if (shape != lastProp && !inDictionaryMode()) {
if (!toDictionaryMode(cx))
return false;
spp = nativeSearch(shape->id);
shape = SHAPE_FETCH(spp);
}
/*
* Next, consider removing id from lastProp->table if in dictionary mode,
* by setting its entry to a removed or free sentinel.
* A dictionary-mode object owns mutable, unique shapes on a non-circular
* doubly linked list, optionally hashed by lastProp->table. So we can edit
* the list and hash in place.
*/
if (inDictionaryMode()) {
PropertyTable *table = lastProp->table;
@ -1092,12 +1125,13 @@ JSObject::removeProperty(JSContext *cx, jsid id)
/*
* Remove shape from its non-circular doubly linked list, setting this
* object's shape first so the updateShape(cx) after this if-else will
* generate a fresh shape for this scope. We need a fresh shape for all
* deletions, even of lastProp. Otherwise, a shape number can replay
* and caches may return get deleted DictionaryShapes! See bug 595365.
* object's OWN_SHAPE flag so the updateShape(cx) further below will
* generate a fresh shape id for this object, distinct from the id of
* any shape in the list. We need a fresh shape for all deletions, even
* of lastProp. Otherwise, a shape number could replay and caches might
* return get deleted DictionaryShapes! See bug 595365.
*/
setOwnShape(lastProp->shape);
flags |= OWN_SHAPE;
Shape *oldLastProp = lastProp;
shape->removeFromDictionary(this);
@ -1132,7 +1166,7 @@ JSObject::removeProperty(JSContext *cx, jsid id)
}
updateShape(cx);
/* Last, consider shrinking table if its load factor is <= .25. */
/* On the way out, consider shrinking table if its load factor is <= .25. */
if (PropertyTable *table = lastProp->table) {
uint32 size = table->capacity();
if (size > PropertyTable::MIN_SIZE && table->entryCount <= size >> 2) {

View File

@ -808,6 +808,7 @@ struct JSScopeStats {
jsrefcount redundantPuts;
jsrefcount putFails;
jsrefcount changes;
jsrefcount changePuts;
jsrefcount changeFails;
jsrefcount compresses;
jsrefcount grows;

View File

@ -47,8 +47,7 @@ START(summary);
try
{
expect = 'TypeError: cannot call namespace method on an XML list with ' +
'0 elements';
expect = "TypeError: can't call namespace method on an XML list with 0 elements";
XML.prototype.function::namespace.call(new XMLList());
}
catch(ex)

View File

@ -101,7 +101,7 @@ reportCompare('abc', value, summary + ': push');
// pop
value = 'abc';
expect = "TypeError: property 'Array.prototype.pop.call(value)' is non-configurable and cannot be deleted";
expect = "TypeError: property Array.prototype.pop.call(value) is non-configurable and can't be deleted";
try
{
actual = Array.prototype.pop.call(value);

View File

@ -23,6 +23,7 @@ script regress-566549.js
script regress-566914.js
script regress-567152.js
script regress-569306.js
script regress-569464.js
script regress-571014.js
script regress-577648-1.js
script regress-577648-2.js
@ -37,8 +38,9 @@ fails-if(!xulRuntime.shell) script regress-595230-1.js
fails-if(!xulRuntime.shell) script regress-595230-2.js
script regress-595365-1.js
fails-if(!xulRuntime.shell) script regress-595365-2.js
script regress-569464.js
script regress-596103.js
script regress-596805-1.js
script regress-596805-2.js
script regress-597870.js
fails-if(!xulRuntime.shell) script regress-597945-1.js
script regress-597945-2.js

View File

@ -0,0 +1,14 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
var f = function(){};
for (var y in f);
f.j = 0;
Object.defineProperty(f, "k", ({configurable: true}));
delete f.j;
Object.defineProperty(f, "k", ({get: function() {}}));
reportCompare(0, 0, "ok");

View File

@ -0,0 +1,27 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/licenses/publicdomain/
*/
for each(let c in [0, 0, 0, 0, 0]) {
try { (function() {
this.c = this;
this.e = arguments;
Object.defineProperty(this, "d", {
get: Math.pow,
configurable: true
});
delete this.e;
delete this.c;
Object.defineProperty(this, "d", {
writable: true
});
if (Math.tan( - 1)) {
Object.defineProperty(this, {});
}
} (c));
} catch(e) {}
}
reportCompare(0, 0, "ok");