Bug 537873: Throw errors when strict mode code assigns to an array's length and the truncation would delete non-configurable elements. r=brendan

This is the patch that actually fixes the bug.
This commit is contained in:
Jim Blandy 2011-02-09 11:31:40 -08:00
Родитель 6babbcb6b2
Коммит 2d670dcc54
1 изменённых файлов: 32 добавлений и 16 удалений

Просмотреть файл

@ -499,8 +499,21 @@ JS_DEFINE_CALLINFO_3(extern, BOOL, js_EnsureDenseArrayCapacity, CONTEXT, OBJECT,
0, nanojit::ACCSET_STORE_ANY & ~tjit::ACCSET_OBJ_CLASP) 0, nanojit::ACCSET_STORE_ANY & ~tjit::ACCSET_OBJ_CLASP)
#endif #endif
static JSBool /*
DeleteArrayElement(JSContext *cx, JSObject *obj, jsdouble index, JSBool strict) * Delete the element |index| from |obj|. If |strict|, do a strict
* deletion: throw if the property is not configurable.
*
* - Return 1 if the deletion succeeds (that is, ES5's [[Delete]] would
* return true)
*
* - Return 0 if the deletion fails because the property is not
* configurable (that is, [[Delete]] would return false). Note that if
* |strict| is true we will throw, not return zero.
*
* - Return -1 if an exception occurs (that is, [[Delete]] would throw).
*/
static int
DeleteArrayElement(JSContext *cx, JSObject *obj, jsdouble index, bool strict)
{ {
JS_ASSERT(index >= 0); JS_ASSERT(index >= 0);
if (obj->isDenseArray()) { if (obj->isDenseArray()) {
@ -508,21 +521,24 @@ DeleteArrayElement(JSContext *cx, JSObject *obj, jsdouble index, JSBool strict)
jsuint idx = jsuint(index); jsuint idx = jsuint(index);
if (idx < obj->getDenseArrayCapacity()) { if (idx < obj->getDenseArrayCapacity()) {
obj->setDenseArrayElement(idx, MagicValue(JS_ARRAY_HOLE)); obj->setDenseArrayElement(idx, MagicValue(JS_ARRAY_HOLE));
return js_SuppressDeletedIndexProperties(cx, obj, idx, idx+1); if (!js_SuppressDeletedIndexProperties(cx, obj, idx, idx+1))
return -1;
} }
} }
return JS_TRUE; return 1;
} }
AutoIdRooter idr(cx); AutoIdRooter idr(cx);
if (!IndexToId(cx, obj, index, NULL, idr.addr())) if (!IndexToId(cx, obj, index, NULL, idr.addr()))
return JS_FALSE; return -1;
if (JSID_IS_VOID(idr.id())) if (JSID_IS_VOID(idr.id()))
return JS_TRUE; return 1;
Value junk; Value v;
return obj->deleteProperty(cx, idr.id(), &junk, strict); if (!obj->deleteProperty(cx, idr.id(), &v, strict))
return -1;
return v.isTrue() ? 1 : 0;
} }
/* /*
@ -535,7 +551,7 @@ SetOrDeleteArrayElement(JSContext *cx, JSObject *obj, jsdouble index,
{ {
if (hole) { if (hole) {
JS_ASSERT(v.isUndefined()); JS_ASSERT(v.isUndefined());
return DeleteArrayElement(cx, obj, index, true); return DeleteArrayElement(cx, obj, index, true) >= 0;
} }
return SetArrayElement(cx, obj, index, v); return SetArrayElement(cx, obj, index, v);
} }
@ -632,10 +648,10 @@ array_length_setter(JSContext *cx, JSObject *obj, jsid id, JSBool strict, Value
obj->setArrayLength(oldlen + 1); obj->setArrayLength(oldlen + 1);
return false; return false;
} }
if (!DeleteArrayElement(cx, obj, oldlen, true)) { int deletion = DeleteArrayElement(cx, obj, oldlen, strict);
if (deletion <= 0) {
obj->setArrayLength(oldlen + 1); obj->setArrayLength(oldlen + 1);
JS_ClearPendingException(cx); return deletion >= 0;
return true;
} }
} while (oldlen != newlen); } while (oldlen != newlen);
obj->setArrayLength(newlen); obj->setArrayLength(newlen);
@ -1987,7 +2003,7 @@ js::array_sort(JSContext *cx, uintN argc, Value *vp)
/* Re-create any holes that sorted to the end of the array. */ /* Re-create any holes that sorted to the end of the array. */
while (len > newlen) { while (len > newlen) {
if (!JS_CHECK_OPERATION_LIMIT(cx) || !DeleteArrayElement(cx, obj, --len, true)) if (!JS_CHECK_OPERATION_LIMIT(cx) || DeleteArrayElement(cx, obj, --len, true) < 0)
return false; return false;
} }
vp->setObject(*obj); vp->setObject(*obj);
@ -2120,7 +2136,7 @@ array_pop_slowly(JSContext *cx, JSObject* obj, Value *vp)
/* Get the to-be-deleted property's value into vp. */ /* Get the to-be-deleted property's value into vp. */
if (!GetElement(cx, obj, index, &hole, vp)) if (!GetElement(cx, obj, index, &hole, vp))
return JS_FALSE; return JS_FALSE;
if (!hole && !DeleteArrayElement(cx, obj, index, true)) if (!hole && DeleteArrayElement(cx, obj, index, true) < 0)
return JS_FALSE; return JS_FALSE;
} }
return js_SetLengthProperty(cx, obj, index); return js_SetLengthProperty(cx, obj, index);
@ -2140,7 +2156,7 @@ array_pop_dense(JSContext *cx, JSObject* obj, Value *vp)
index--; index--;
if (!GetElement(cx, obj, index, &hole, vp)) if (!GetElement(cx, obj, index, &hole, vp))
return JS_FALSE; return JS_FALSE;
if (!hole && !DeleteArrayElement(cx, obj, index, true)) if (!hole && DeleteArrayElement(cx, obj, index, true) < 0)
return JS_FALSE; return JS_FALSE;
obj->setArrayLength(index); obj->setArrayLength(index);
return JS_TRUE; return JS_TRUE;
@ -2203,7 +2219,7 @@ array_shift(JSContext *cx, uintN argc, Value *vp)
} }
/* Delete the only or last element when it exists. */ /* Delete the only or last element when it exists. */
if (!hole && !DeleteArrayElement(cx, obj, length, true)) if (!hole && DeleteArrayElement(cx, obj, length, true) < 0)
return JS_FALSE; return JS_FALSE;
} }
return js_SetLengthProperty(cx, obj, length); return js_SetLengthProperty(cx, obj, length);