зеркало из https://github.com/mozilla/gecko-dev.git
Bug 492849 - Implement Object.preventExtensions, Object.isExtensible. a=waldo,jimb, r=brendan
--HG-- rename : js/src/jsapi-tests/testSeal.cpp => js/src/jsapi-tests/testDeepFreeze.cpp
This commit is contained in:
Родитель
ff464b7c97
Коммит
1623508e26
|
@ -561,8 +561,7 @@ InitCTypeClass(JSContext* cx, JSObject* parent)
|
|||
!JS_DefineFunctions(cx, prototype, sCTypeFunctions))
|
||||
return NULL;
|
||||
|
||||
if (!JS_SealObject(cx, ctor, JS_FALSE) ||
|
||||
!JS_SealObject(cx, prototype, JS_FALSE))
|
||||
if (!JS_FreezeObject(cx, ctor) || !JS_FreezeObject(cx, prototype))
|
||||
return NULL;
|
||||
|
||||
return prototype;
|
||||
|
@ -603,8 +602,8 @@ InitCDataClass(JSContext* cx, JSObject* parent, JSObject* CTypeProto)
|
|||
!JS_DefineFunctions(cx, prototype, sCDataFunctions))
|
||||
return NULL;
|
||||
|
||||
if (//!JS_SealObject(cx, prototype, JS_FALSE) || // XXX fixme - see bug 541212!
|
||||
!JS_SealObject(cx, ctor, JS_FALSE))
|
||||
if (//!JS_FreezeObject(cx, prototype) || // XXX fixme - see bug 541212!
|
||||
!JS_FreezeObject(cx, ctor))
|
||||
return NULL;
|
||||
|
||||
return prototype;
|
||||
|
@ -622,7 +621,7 @@ DefineABIConstant(JSContext* cx,
|
|||
return false;
|
||||
if (!JS_SetReservedSlot(cx, obj, SLOT_ABICODE, INT_TO_JSVAL(code)))
|
||||
return false;
|
||||
return JS_SealObject(cx, obj, JS_FALSE);
|
||||
return JS_FreezeObject(cx, obj);
|
||||
}
|
||||
|
||||
// Set up a single type constructor for
|
||||
|
@ -692,9 +691,9 @@ InitTypeConstructor(JSContext* cx,
|
|||
if (instanceProps && !JS_DefineProperties(cx, dataProto, instanceProps))
|
||||
return false;
|
||||
|
||||
if (!JS_SealObject(cx, obj, JS_FALSE) ||
|
||||
//!JS_SealObject(cx, dataProto, JS_FALSE) || // XXX fixme - see bug 541212!
|
||||
!JS_SealObject(cx, typeProto, JS_FALSE))
|
||||
if (!JS_FreezeObject(cx, obj) ||
|
||||
//!JS_FreezeObject(cx, dataProto) || // XXX fixme - see bug 541212!
|
||||
!JS_FreezeObject(cx, typeProto))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
@ -717,7 +716,7 @@ InitInt64Class(JSContext* cx,
|
|||
JSObject* ctor = JS_GetConstructor(cx, prototype);
|
||||
if (!ctor)
|
||||
return NULL;
|
||||
if (!JS_SealObject(cx, ctor, JS_FALSE))
|
||||
if (!JS_FreezeObject(cx, ctor))
|
||||
return NULL;
|
||||
|
||||
// Stash ctypes.{Int64,UInt64}.prototype on a reserved slot of the 'join'
|
||||
|
@ -728,7 +727,7 @@ InitInt64Class(JSContext* cx,
|
|||
OBJECT_TO_JSVAL(prototype)))
|
||||
return NULL;
|
||||
|
||||
if (!JS_SealObject(cx, prototype, JS_FALSE))
|
||||
if (!JS_FreezeObject(cx, prototype))
|
||||
return NULL;
|
||||
|
||||
return prototype;
|
||||
|
@ -958,7 +957,7 @@ JS_InitCTypesClass(JSContext* cx, JSObject* global)
|
|||
return false;
|
||||
|
||||
// Seal the ctypes object, to prevent modification.
|
||||
return JS_SealObject(cx, ctypes, JS_FALSE);
|
||||
return JS_FreezeObject(cx, ctypes);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
|
@ -2638,12 +2637,12 @@ CType::Create(JSContext* cx,
|
|||
return NULL;
|
||||
|
||||
// Set the 'prototype' object.
|
||||
if (//!JS_SealObject(cx, prototype, JS_FALSE) || // XXX fixme - see bug 541212!
|
||||
if (//!JS_FreezeObject(cx, prototype) || // XXX fixme - see bug 541212!
|
||||
!JS_SetReservedSlot(cx, typeObj, SLOT_PROTO, OBJECT_TO_JSVAL(prototype)))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!JS_SealObject(cx, typeObj, JS_FALSE))
|
||||
if (!JS_FreezeObject(cx, typeObj))
|
||||
return NULL;
|
||||
|
||||
// Assert a sanity check on size and alignment: size % alignment should always
|
||||
|
@ -3953,7 +3952,7 @@ AddFieldToArray(JSContext* cx,
|
|||
JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT))
|
||||
return false;
|
||||
|
||||
return JS_SealObject(cx, fieldObj, JS_FALSE);
|
||||
return JS_FreezeObject(cx, fieldObj);
|
||||
}
|
||||
|
||||
JSBool
|
||||
|
@ -4114,7 +4113,7 @@ StructType::DefineInternal(JSContext* cx, JSObject* typeObj, JSObject* fieldsObj
|
|||
|
||||
if (!JS_SetReservedSlot(cx, typeObj, SLOT_SIZE, sizeVal) ||
|
||||
!JS_SetReservedSlot(cx, typeObj, SLOT_ALIGN, INT_TO_JSVAL(structAlign)) ||
|
||||
//!JS_SealObject(cx, prototype, JS_FALSE) || // XXX fixme - see bug 541212!
|
||||
//!JS_FreezeObject(cx, prototype) || // XXX fixme - see bug 541212!
|
||||
!JS_SetReservedSlot(cx, typeObj, SLOT_PROTO, OBJECT_TO_JSVAL(prototype)))
|
||||
return JS_FALSE;
|
||||
|
||||
|
@ -4363,7 +4362,7 @@ StructType::BuildFieldsArray(JSContext* cx, JSObject* obj)
|
|||
return NULL;
|
||||
|
||||
// Seal the fields array.
|
||||
if (!JS_SealObject(cx, fieldsProp, JS_FALSE))
|
||||
if (!JS_FreezeObject(cx, fieldsProp))
|
||||
return NULL;
|
||||
|
||||
return fieldsProp;
|
||||
|
@ -4901,8 +4900,8 @@ FunctionType::ConstructData(JSContext* cx,
|
|||
// having to do things like reset SLOT_REFERENT when someone tries to
|
||||
// change the pointer value.
|
||||
// XXX This will need to change when bug 541212 is fixed -- CData::ValueSetter
|
||||
// could be called on a sealed object.
|
||||
return JS_SealObject(cx, dataObj, JS_FALSE);
|
||||
// could be called on a frozen object.
|
||||
return JS_FreezeObject(cx, dataObj);
|
||||
}
|
||||
|
||||
typedef Array<AutoValue, 16> AutoValueAutoArray;
|
||||
|
@ -5118,7 +5117,7 @@ FunctionType::ArgTypesGetter(JSContext* cx, JSObject* obj, jsid idval, jsval* vp
|
|||
return JS_FALSE;
|
||||
|
||||
// Seal and cache it.
|
||||
if (!JS_SealObject(cx, argTypes, JS_FALSE) ||
|
||||
if (!JS_FreezeObject(cx, argTypes) ||
|
||||
!JS_SetReservedSlot(cx, obj, SLOT_ARGS_T, OBJECT_TO_JSVAL(argTypes)))
|
||||
return JS_FALSE;
|
||||
|
||||
|
@ -5818,7 +5817,7 @@ Int64Base::Construct(JSContext* cx,
|
|||
return NULL;
|
||||
}
|
||||
|
||||
if (!JS_SealObject(cx, result, JS_FALSE))
|
||||
if (!JS_FreezeObject(cx, result))
|
||||
return NULL;
|
||||
|
||||
return result;
|
||||
|
|
|
@ -373,7 +373,7 @@ Library::Declare(JSContext* cx, uintN argc, jsval* vp)
|
|||
// change the pointer value.
|
||||
// XXX This will need to change when bug 541212 is fixed -- CData::ValueSetter
|
||||
// could be called on a sealed object.
|
||||
if (isFunction && !JS_SealObject(cx, result, JS_FALSE))
|
||||
if (isFunction && !JS_FreezeObject(cx, result))
|
||||
return JS_FALSE;
|
||||
|
||||
return JS_TRUE;
|
||||
|
|
|
@ -338,3 +338,4 @@ MSG_DEF(JSMSG_NOT_EXPECTED_TYPE, 255, 3, JSEXN_TYPEERR, "{0}: expected {1},
|
|||
MSG_DEF(JSMSG_CALLER_IS_STRICT, 256, 0, JSEXN_TYPEERR, "access to strict mode caller function is censored")
|
||||
MSG_DEF(JSMSG_NEED_DEBUG_MODE, 257, 0, JSEXN_ERR, "function can be called only in debug mode")
|
||||
MSG_DEF(JSMSG_STRICT_CODE_LET_EXPR_STMT, 258, 0, JSEXN_ERR, "strict mode code may not contain unparenthesized let expression statements")
|
||||
MSG_DEF(JSMSG_CANT_CHANGE_EXTENSIBILITY, 259, 0, JSEXN_TYPEERR, "can't change object's extensibility")
|
||||
|
|
|
@ -53,23 +53,23 @@ CPPSRCS = \
|
|||
testConservativeGC.cpp \
|
||||
testContexts.cpp \
|
||||
testDebugger.cpp \
|
||||
testDeepFreeze.cpp \
|
||||
testDefineGetterSetterNonEnumerable.cpp \
|
||||
testDefineProperty.cpp \
|
||||
testExtendedEq.cpp \
|
||||
testGCChunkAlloc.cpp \
|
||||
testFuncCallback.cpp \
|
||||
testGCChunkAlloc.cpp \
|
||||
testIntString.cpp \
|
||||
testIsAboutToBeFinalized.cpp \
|
||||
testLookup.cpp \
|
||||
testNewObject.cpp \
|
||||
testOps.cpp \
|
||||
testPropCache.cpp \
|
||||
testTrap.cpp \
|
||||
testSameValue.cpp \
|
||||
testSeal.cpp \
|
||||
testXDR.cpp \
|
||||
testSetPropertyWithNativeGetterStubSetter.cpp \
|
||||
testScriptObject.cpp \
|
||||
testSetPropertyWithNativeGetterStubSetter.cpp \
|
||||
testTrap.cpp \
|
||||
testXDR.cpp \
|
||||
$(NULL)
|
||||
|
||||
DEFINES += -DEXPORT_JS_API
|
||||
|
|
|
@ -4,11 +4,11 @@
|
|||
|
||||
#include "tests.h"
|
||||
|
||||
BEGIN_TEST(testSeal_bug535703)
|
||||
BEGIN_TEST(testDeepFreeze_bug535703)
|
||||
{
|
||||
JSObject *obj = JS_NewObject(cx, NULL, NULL, NULL);
|
||||
CHECK(obj);
|
||||
JS_SealObject(cx, obj, JS_TRUE); // don't crash
|
||||
JS_DeepFreezeObject(cx, obj); // don't crash
|
||||
return true;
|
||||
}
|
||||
END_TEST(testSeal_bug535703)
|
||||
END_TEST(testDeepFreeze_bug535703)
|
|
@ -3007,51 +3007,38 @@ JS_NewObjectForConstructor(JSContext *cx, const jsval *vp)
|
|||
}
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_SealObject(JSContext *cx, JSObject *obj, JSBool deep)
|
||||
JS_FreezeObject(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, obj);
|
||||
|
||||
/* Nothing to do if obj is already sealed. */
|
||||
if (obj->sealed())
|
||||
return obj->freeze(cx);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(JSBool)
|
||||
JS_DeepFreezeObject(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
CHECK_REQUEST(cx);
|
||||
assertSameCompartment(cx, obj);
|
||||
|
||||
/* Assume that non-extensible objects are already deep-frozen, to avoid divergence. */
|
||||
if (obj->isExtensible())
|
||||
return true;
|
||||
|
||||
if (obj->isDenseArray() && !obj->makeDenseArraySlow(cx))
|
||||
if (!obj->freeze(cx))
|
||||
return false;
|
||||
|
||||
if (!obj->isNative()) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||
JSMSG_CANT_SEAL_OBJECT,
|
||||
obj->getClass()->name);
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef JS_THREADSAFE
|
||||
/* Insist on scope being used exclusively by cx's thread. */
|
||||
JS_ASSERT(obj->title.ownercx == cx);
|
||||
#endif
|
||||
|
||||
/* XXX Enumerate lazy properties now, as they can't be added later. */
|
||||
JSIdArray *ida = JS_Enumerate(cx, obj);
|
||||
if (!ida)
|
||||
return false;
|
||||
JS_DestroyIdArray(cx, ida);
|
||||
|
||||
/* If not sealing an entire object graph, we're done after sealing obj. */
|
||||
obj->seal(cx);
|
||||
if (!deep)
|
||||
return true;
|
||||
|
||||
/* Walk slots in obj and if any value is a non-null object, seal it. */
|
||||
for (uint32 i = 0, n = obj->slotSpan(); i != n; ++i) {
|
||||
for (uint32 i = 0, n = obj->slotSpan(); i < n; ++i) {
|
||||
const Value &v = obj->getSlot(i);
|
||||
if (i == JSSLOT_PRIVATE && (obj->getClass()->flags & JSCLASS_HAS_PRIVATE))
|
||||
continue;
|
||||
if (v.isPrimitive())
|
||||
continue;
|
||||
if (!JS_SealObject(cx, &v.toObject(), deep))
|
||||
if (!JS_DeepFreezeObject(cx, &v.toObject()))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1928,8 +1928,19 @@ extern JS_PUBLIC_API(JSObject *)
|
|||
JS_NewObjectWithGivenProto(JSContext *cx, JSClass *clasp, JSObject *proto,
|
||||
JSObject *parent);
|
||||
|
||||
/*
|
||||
* Freeze obj, and all objects it refers to, recursively. This will not recurse
|
||||
* through non-extensible objects, on the assumption that those are already
|
||||
* deep-frozen.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSBool)
|
||||
JS_SealObject(JSContext *cx, JSObject *obj, JSBool deep);
|
||||
JS_DeepFreezeObject(JSContext *cx, JSObject *obj);
|
||||
|
||||
/*
|
||||
* Freezes an object; see ES5's Object.freeze(obj) method.
|
||||
*/
|
||||
extern JS_PUBLIC_API(JSBool)
|
||||
JS_FreezeObject(JSContext *cx, JSObject *obj);
|
||||
|
||||
extern JS_PUBLIC_API(JSObject *)
|
||||
JS_ConstructObject(JSContext *cx, JSClass *clasp, JSObject *proto,
|
||||
|
|
|
@ -630,7 +630,7 @@ array_length_setter(JSContext *cx, JSObject *obj, jsid id, Value *vp, JSBool str
|
|||
Value junk;
|
||||
|
||||
/* Check for a sealed object first. */
|
||||
if (obj->sealed()) {
|
||||
if (!obj->isExtensible()) {
|
||||
return js_ReportValueErrorFlags(cx, JSREPORT_ERROR, JSMSG_READ_ONLY,
|
||||
JSDVG_IGNORE_STACK, IdToValue(id), NULL,
|
||||
NULL, NULL);
|
||||
|
@ -1003,6 +1003,27 @@ array_trace(JSTracer *trc, JSObject *obj)
|
|||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
JSBool
|
||||
array_fix(JSContext *cx, JSObject *obj, bool *success, AutoIdVector *props)
|
||||
{
|
||||
JS_ASSERT(obj->isDenseArray());
|
||||
|
||||
/*
|
||||
* We must slowify dense arrays; otherwise, we'd need to detect assignments to holes,
|
||||
* since that is effectively adding a new property to the array.
|
||||
*/
|
||||
if (!obj->makeDenseArraySlow(cx) ||
|
||||
!GetPropertyNames(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY, props))
|
||||
return false;
|
||||
|
||||
*success = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Class js_ArrayClass = {
|
||||
"Array",
|
||||
Class::NON_NATIVE |
|
||||
|
@ -1035,6 +1056,7 @@ Class js_ArrayClass = {
|
|||
NULL, /* enumerate */
|
||||
array_typeOf,
|
||||
array_trace,
|
||||
array_fix,
|
||||
NULL, /* thisObject */
|
||||
NULL, /* clear */
|
||||
}
|
||||
|
|
|
@ -4046,7 +4046,7 @@ BEGIN_CASE(JSOP_SETMETHOD)
|
|||
JSObject *obj2;
|
||||
JSAtom *atom;
|
||||
if (cache->testForSet(cx, regs.pc, obj, &entry, &obj2, &atom)) {
|
||||
JS_ASSERT(!obj->sealed());
|
||||
JS_ASSERT(obj->isExtensible());
|
||||
|
||||
/*
|
||||
* Fast property cache hit, only partially confirmed by
|
||||
|
@ -4155,7 +4155,7 @@ BEGIN_CASE(JSOP_SETMETHOD)
|
|||
if (obj == obj2) {
|
||||
shape = entry->vword.toShape();
|
||||
JS_ASSERT(shape->writable());
|
||||
JS_ASSERT(!obj2->sealed());
|
||||
JS_ASSERT(obj2->isExtensible());
|
||||
NATIVE_SET(cx, obj, shape, entry, &rval);
|
||||
}
|
||||
if (shape)
|
||||
|
|
|
@ -676,13 +676,13 @@ js_GetSlotThreadSafe(JSContext *cx, JSObject *obj, uint32 slot)
|
|||
JS_ASSERT(obj->containsSlot(slot));
|
||||
|
||||
/*
|
||||
* Avoid locking if called from the GC. Also avoid locking a sealed
|
||||
* Avoid locking if called from the GC. Also avoid locking a non-extensible
|
||||
* object. If neither of those special cases applies, try to claim obj's
|
||||
* flyweight lock from whatever context may have had it in an earlier
|
||||
* request.
|
||||
*/
|
||||
if (CX_THREAD_IS_RUNNING_GC(cx) ||
|
||||
obj->sealed() ||
|
||||
!obj->isExtensible() ||
|
||||
(obj->title.ownercx && ClaimTitle(&obj->title, cx))) {
|
||||
return Jsvalify(obj->getSlot(slot));
|
||||
}
|
||||
|
@ -754,13 +754,13 @@ js_SetSlotThreadSafe(JSContext *cx, JSObject *obj, uint32 slot, jsval v)
|
|||
JS_ASSERT(obj->containsSlot(slot));
|
||||
|
||||
/*
|
||||
* Avoid locking if called from the GC. Also avoid locking a sealed
|
||||
* Avoid locking if called from the GC. Also avoid locking a non-extensible
|
||||
* object. If neither of those special cases applies, try to claim obj's
|
||||
* flyweight lock from whatever context may have had it in an earlier
|
||||
* request.
|
||||
*/
|
||||
if (CX_THREAD_IS_RUNNING_GC(cx) ||
|
||||
obj->sealed() ||
|
||||
!obj->isExtensible() ||
|
||||
(obj->title.ownercx && ClaimTitle(&obj->title, cx))) {
|
||||
obj->lockedSetSlot(slot, Valueify(v));
|
||||
return;
|
||||
|
@ -1245,7 +1245,7 @@ js_LockObj(JSContext *cx, JSObject *obj)
|
|||
if (CX_THREAD_IS_RUNNING_GC(cx))
|
||||
return;
|
||||
|
||||
if (obj->sealed() && !cx->thread->lockedSealedTitle) {
|
||||
if (!obj->isExtensible() && !cx->thread->lockedSealedTitle) {
|
||||
cx->thread->lockedSealedTitle = &obj->title;
|
||||
return;
|
||||
}
|
||||
|
@ -1311,7 +1311,7 @@ js_IsTitleLocked(JSContext *cx, JSTitle *title)
|
|||
if (CX_THREAD_IS_RUNNING_GC(cx))
|
||||
return JS_TRUE;
|
||||
|
||||
/* Special case: locked object is sealed (ES5 frozen) -- see js_LockObj. */
|
||||
/* Special case: locked object is not extensible -- see js_LockObj. */
|
||||
if (cx->thread->lockedSealedTitle == title)
|
||||
return JS_TRUE;
|
||||
|
||||
|
|
113
js/src/jsobj.cpp
113
js/src/jsobj.cpp
|
@ -150,6 +150,12 @@ obj_getProto(JSContext *cx, JSObject *obj, jsid id, Value *vp)
|
|||
static JSBool
|
||||
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);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!vp->isObjectOrNull())
|
||||
return JS_TRUE;
|
||||
|
||||
|
@ -1999,7 +2005,7 @@ DefinePropertyOnObject(JSContext *cx, JSObject *obj, const PropDesc &desc,
|
|||
|
||||
/* 8.12.9 steps 2-4. */
|
||||
if (!current) {
|
||||
if (obj->sealed())
|
||||
if (!obj->isExtensible())
|
||||
return Reject(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
|
||||
|
||||
*rval = true;
|
||||
|
@ -2519,6 +2525,72 @@ obj_getOwnPropertyNames(JSContext *cx, uintN argc, Value *vp)
|
|||
return true;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
obj_isExtensible(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
JSObject *obj;
|
||||
if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.isExtensible", &obj))
|
||||
return false;
|
||||
|
||||
vp->setBoolean(obj->isExtensible());
|
||||
return true;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
obj_preventExtensions(JSContext *cx, uintN argc, Value *vp)
|
||||
{
|
||||
JSObject *obj;
|
||||
if (!GetFirstArgumentAsObject(cx, argc, vp, "Object.preventExtensions", &obj))
|
||||
return false;
|
||||
|
||||
vp->setObject(*obj);
|
||||
|
||||
AutoIdVector props(cx);
|
||||
return obj->preventExtensions(cx, &props);
|
||||
}
|
||||
|
||||
bool
|
||||
JSObject::sealOrFreeze(JSContext *cx, bool freeze)
|
||||
{
|
||||
assertSameCompartment(cx, this);
|
||||
|
||||
AutoIdVector props(cx);
|
||||
if (isExtensible()) {
|
||||
if (!preventExtensions(cx, &props))
|
||||
return false;
|
||||
} else {
|
||||
if (!GetPropertyNames(cx, this, JSITER_HIDDEN | JSITER_OWNONLY, &props))
|
||||
return false;
|
||||
}
|
||||
|
||||
/* preventExtensions must slowify dense arrays, so we can assign to holes without checks. */
|
||||
JS_ASSERT(!isDenseArray());
|
||||
|
||||
for (size_t i = 0, len = props.length(); i < len; i++) {
|
||||
jsid id = props[i];
|
||||
|
||||
uintN attrs;
|
||||
if (!getAttributes(cx, id, &attrs))
|
||||
return false;
|
||||
|
||||
/* Make all attributes permanent; if freezing, make data attributes read-only. */
|
||||
uintN new_attrs;
|
||||
if (freeze && !(attrs & (JSPROP_GETTER | JSPROP_SETTER)))
|
||||
new_attrs = JSPROP_PERMANENT | JSPROP_READONLY;
|
||||
else
|
||||
new_attrs = JSPROP_PERMANENT;
|
||||
|
||||
/* If we already have the attributes we need, skip the setAttributes call. */
|
||||
if ((attrs | new_attrs) == attrs)
|
||||
continue;
|
||||
|
||||
attrs |= new_attrs;
|
||||
if (!setAttributes(cx, id, &attrs))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#if JS_HAS_OBJ_WATCHPOINT
|
||||
const char js_watch_str[] = "watch";
|
||||
|
@ -2559,6 +2631,8 @@ static JSFunctionSpec object_static_methods[] = {
|
|||
JS_FN("defineProperties", obj_defineProperties, 2,0),
|
||||
JS_FN("create", obj_create, 2,0),
|
||||
JS_FN("getOwnPropertyNames", obj_getOwnPropertyNames, 1,0),
|
||||
JS_FN("isExtensible", obj_isExtensible, 1,0),
|
||||
JS_FN("preventExtensions", obj_preventExtensions, 1,0),
|
||||
JS_FS_END
|
||||
};
|
||||
|
||||
|
@ -2914,9 +2988,10 @@ Class js_WithClass = {
|
|||
with_DeleteProperty,
|
||||
with_Enumerate,
|
||||
with_TypeOf,
|
||||
NULL, /* trace */
|
||||
NULL, /* trace */
|
||||
NULL, /* fix */
|
||||
with_ThisObject,
|
||||
NULL, /* clear */
|
||||
NULL, /* clear */
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -3631,6 +3706,7 @@ bool
|
|||
SetProto(JSContext *cx, JSObject *obj, JSObject *proto, bool checkForCycles)
|
||||
{
|
||||
JS_ASSERT_IF(!checkForCycles, obj != proto);
|
||||
JS_ASSERT(obj->isExtensible());
|
||||
|
||||
if (obj->isNative()) {
|
||||
JS_LOCK_OBJ(cx, obj);
|
||||
|
@ -4948,6 +5024,13 @@ ReportNotConfigurable(JSContext* cx, jsid id, uintN flags)
|
|||
NULL, NULL);
|
||||
}
|
||||
|
||||
JSBool
|
||||
ReportNotExtensible(JSContext* cx, uintN flags)
|
||||
{
|
||||
return JS_ReportErrorFlagsAndNumber(cx, flags, js_GetErrorMessage, NULL,
|
||||
JSMSG_OBJECT_NOT_EXTENSIBLE);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -4977,10 +5060,6 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
|
|||
/* Convert string indices to integers if appropriate. */
|
||||
id = js_CheckForStringIndex(id);
|
||||
|
||||
/* Check for a sealed object first (now that id has been normalized). */
|
||||
if (obj->sealed())
|
||||
return ReportReadOnly(cx, id, JSREPORT_ERROR);
|
||||
|
||||
protoIndex = js_LookupPropertyWithFlags(cx, obj, id, cx->resolveFlags,
|
||||
&pobj, &prop);
|
||||
if (protoIndex < 0)
|
||||
|
@ -5049,10 +5128,6 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
|
|||
#endif
|
||||
}
|
||||
}
|
||||
if (pobj->sealed() && !shape->hasSlot()) {
|
||||
JS_UNLOCK_OBJ(cx, pobj);
|
||||
return ReportReadOnly(cx, id, JSREPORT_ERROR);
|
||||
}
|
||||
|
||||
attrs = shape->attributes();
|
||||
if (pobj != obj) {
|
||||
|
@ -5127,6 +5202,16 @@ js_SetPropertyHelper(JSContext *cx, JSObject *obj, jsid id, uintN defineHow,
|
|||
|
||||
added = false;
|
||||
if (!shape) {
|
||||
if (!obj->isExtensible()) {
|
||||
/* Error in strict mode code, warn with strict option, otherwise do nothing. */
|
||||
if (strict)
|
||||
return ReportNotExtensible(cx, 0);
|
||||
if (JS_HAS_STRICT_OPTION(cx))
|
||||
return ReportNotExtensible(cx, JSREPORT_STRICT | JSREPORT_WARNING);
|
||||
else
|
||||
return JS_TRUE;
|
||||
}
|
||||
|
||||
/*
|
||||
* Purge the property cache of now-shadowed id in obj's scope chain.
|
||||
* Do this early, before locking obj to avoid nesting locks.
|
||||
|
@ -6315,12 +6400,13 @@ js_DumpObject(JSObject *obj)
|
|||
uint32 flags = obj->flags;
|
||||
if (flags & JSObject::DELEGATE) fprintf(stderr, " delegate");
|
||||
if (flags & JSObject::SYSTEM) fprintf(stderr, " system");
|
||||
if (flags & JSObject::SEALED) fprintf(stderr, " sealed");
|
||||
if (flags & JSObject::NOT_EXTENSIBLE) fprintf(stderr, " not extensible");
|
||||
if (flags & JSObject::BRANDED) fprintf(stderr, " branded");
|
||||
if (flags & JSObject::GENERIC) fprintf(stderr, " generic");
|
||||
if (flags & JSObject::METHOD_BARRIER) fprintf(stderr, " method_barrier");
|
||||
if (flags & JSObject::INDEXED) fprintf(stderr, " indexed");
|
||||
if (flags & JSObject::OWN_SHAPE) fprintf(stderr, " own_shape");
|
||||
|
||||
bool anyFlags = flags != 0;
|
||||
if (obj->isNative()) {
|
||||
if (obj->inDictionaryMode()) {
|
||||
|
@ -6349,9 +6435,6 @@ js_DumpObject(JSObject *obj)
|
|||
}
|
||||
|
||||
if (obj->isNative()) {
|
||||
if (obj->sealed())
|
||||
fprintf(stderr, "sealed\n");
|
||||
|
||||
fprintf(stderr, "properties:\n");
|
||||
for (Shape::Range r = obj->lastProperty()->all(); !r.empty(); r.popFront())
|
||||
DumpShape(r.front());
|
||||
|
|
|
@ -321,12 +321,12 @@ struct JSObject {
|
|||
inline bool nativeContains(const js::Shape &shape);
|
||||
|
||||
enum {
|
||||
DELEGATE = 0x01,
|
||||
SYSTEM = 0x02,
|
||||
SEALED = 0x04,
|
||||
BRANDED = 0x08,
|
||||
GENERIC = 0x10,
|
||||
METHOD_BARRIER = 0x20,
|
||||
DELEGATE = 0x01,
|
||||
SYSTEM = 0x02,
|
||||
NOT_EXTENSIBLE = 0x04,
|
||||
BRANDED = 0x08,
|
||||
GENERIC = 0x10,
|
||||
METHOD_BARRIER = 0x20,
|
||||
INDEXED = 0x40,
|
||||
OWN_SHAPE = 0x80,
|
||||
BOUND_FUNCTION = 0x100
|
||||
|
@ -399,14 +399,6 @@ struct JSObject {
|
|||
bool isSystem() const { return !!(flags & SYSTEM); }
|
||||
void setSystem() { flags |= SYSTEM; }
|
||||
|
||||
/*
|
||||
* Don't define clearSealed, as it can't be done safely because JS_LOCK_OBJ
|
||||
* will avoid taking the lock if the object owns its scope and the scope is
|
||||
* sealed.
|
||||
*/
|
||||
bool sealed() { return !!(flags & SEALED); }
|
||||
void seal(JSContext *cx);
|
||||
|
||||
/*
|
||||
* A branded object contains plain old methods (function-valued properties
|
||||
* without magic getters and setters), and its shape evolves whenever a
|
||||
|
@ -448,6 +440,11 @@ struct JSObject {
|
|||
void shadowingShapeChange(JSContext *cx, const js::Shape &shape);
|
||||
bool globalObjectOwnShapeChange(JSContext *cx);
|
||||
|
||||
void extensibleShapeChange(JSContext *cx) {
|
||||
/* This will do for now. */
|
||||
generateOwnShape(cx);
|
||||
}
|
||||
|
||||
/*
|
||||
* A scope has a method barrier when some compiler-created "null closure"
|
||||
* function objects (functions that do not use lexical bindings above their
|
||||
|
@ -673,6 +670,29 @@ struct JSObject {
|
|||
*(void **)&fslots[JSSLOT_PRIVATE] = data;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* ES5 meta-object properties and operations.
|
||||
*/
|
||||
|
||||
private:
|
||||
/*
|
||||
* The guts of Object.seal (ES5 15.2.3.8) and Object.freeze (ES5 15.2.3.9): mark the
|
||||
* object as non-extensible, and adjust each property's attributes appropriately: each
|
||||
* property becomes non-configurable, and if |freeze|, data properties become
|
||||
* read-only as well.
|
||||
*/
|
||||
bool sealOrFreeze(JSContext *cx, bool freeze = false);
|
||||
|
||||
public:
|
||||
bool isExtensible() const { return !(flags & NOT_EXTENSIBLE); }
|
||||
bool preventExtensions(JSContext *cx, js::AutoIdVector *props);
|
||||
|
||||
/* ES5 15.2.3.8: non-extensible, all props non-configurable */
|
||||
inline bool seal(JSContext *cx) { return sealOrFreeze(cx); }
|
||||
/* ES5 15.2.3.9: non-extensible, all properties non-configurable, all data props read-only */
|
||||
bool freeze(JSContext *cx) { return sealOrFreeze(cx, true); }
|
||||
|
||||
/*
|
||||
* Primitive-specific getters and setters.
|
||||
*/
|
||||
|
|
|
@ -68,13 +68,29 @@ JSObject::dropProperty(JSContext *cx, JSProperty *prop)
|
|||
JS_UNLOCK_OBJ(cx, this);
|
||||
}
|
||||
|
||||
inline void
|
||||
JSObject::seal(JSContext *cx)
|
||||
inline bool
|
||||
JSObject::preventExtensions(JSContext *cx, js::AutoIdVector *props)
|
||||
{
|
||||
JS_ASSERT(!sealed());
|
||||
JS_ASSERT(isExtensible());
|
||||
|
||||
if (js::FixOp fix = getOps()->fix) {
|
||||
bool success;
|
||||
if (!fix(cx, this, &success, props))
|
||||
return false;
|
||||
if (!success) {
|
||||
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_CANT_CHANGE_EXTENSIBILITY);
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!GetPropertyNames(cx, this, JSITER_HIDDEN | JSITER_OWNONLY, props))
|
||||
return false;
|
||||
}
|
||||
|
||||
if (isNative())
|
||||
generateOwnShape(cx);
|
||||
flags |= SEALED;
|
||||
extensibleShapeChange(cx);
|
||||
|
||||
flags |= NOT_EXTENSIBLE;
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool
|
||||
|
|
|
@ -134,7 +134,7 @@ PropertyCache::testForInit(JSRuntime *rt, jsbytecode *pc, JSObject *obj,
|
|||
const js::Shape **shapep, PropertyCacheEntry **entryp)
|
||||
{
|
||||
JS_ASSERT(obj->slotSpan() >= JSSLOT_FREE(obj->getClass()));
|
||||
JS_ASSERT(!obj->sealed());
|
||||
JS_ASSERT(obj->isExtensible());
|
||||
uint32 kshape = obj->shape();
|
||||
PropertyCacheEntry *entry = &table[hash(pc, kshape)];
|
||||
*entryp = entry;
|
||||
|
|
|
@ -945,6 +945,7 @@ JS_FRIEND_API(Class) ObjectProxyClass = {
|
|||
NULL, /* enumerate */
|
||||
NULL, /* typeof */
|
||||
proxy_TraceObject,
|
||||
NULL, /* fix */
|
||||
NULL, /* thisObject */
|
||||
proxy_Finalize, /* clear */
|
||||
}
|
||||
|
@ -985,6 +986,7 @@ JS_FRIEND_API(Class) OuterWindowProxyClass = {
|
|||
NULL, /* enumerate */
|
||||
NULL, /* typeof */
|
||||
proxy_TraceObject,
|
||||
NULL, /* fix */
|
||||
NULL, /* thisObject */
|
||||
proxy_Finalize, /* clear */
|
||||
}
|
||||
|
@ -1047,6 +1049,7 @@ JS_FRIEND_API(Class) FunctionProxyClass = {
|
|||
NULL, /* enumerate */
|
||||
proxy_TypeOf_fun,
|
||||
proxy_TraceObject,
|
||||
NULL, /* fix */
|
||||
NULL, /* thisObject */
|
||||
NULL, /* clear */
|
||||
}
|
||||
|
|
|
@ -720,17 +720,6 @@ JSObject::addProperty(JSContext *cx, jsid id,
|
|||
{
|
||||
JS_ASSERT(!JSID_IS_VOID(id));
|
||||
|
||||
/*
|
||||
* You can't add properties to a sealed object. But note well that you can
|
||||
* change property attributes in a sealed object, even though that replaces
|
||||
* a Shape * in the scope's hash table -- but no id is added, so the object
|
||||
* remains sealed.
|
||||
*/
|
||||
if (sealed()) {
|
||||
reportReadOnlyScope(cx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NormalizeGetterAndSetter(cx, this, id, attrs, flags, getter, setter);
|
||||
|
||||
/* Search for id with adding = true in order to claim its entry. */
|
||||
|
@ -746,6 +735,15 @@ JSObject::addPropertyCommon(JSContext *cx, jsid id,
|
|||
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;
|
||||
}
|
||||
|
||||
PropertyTable *table = NULL;
|
||||
if (!inDictionaryMode()) {
|
||||
if (lastProp->entryCount() >= PropertyTree::MAX_HEIGHT) {
|
||||
|
@ -828,11 +826,6 @@ JSObject::putProperty(JSContext *cx, jsid id,
|
|||
|
||||
JS_ASSERT(!JSID_IS_VOID(id));
|
||||
|
||||
if (sealed()) {
|
||||
reportReadOnlyScope(cx);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
NormalizeGetterAndSetter(cx, this, id, attrs, flags, getter, setter);
|
||||
|
||||
/* Search for id in order to claim its entry if table has been allocated. */
|
||||
|
@ -1041,11 +1034,6 @@ JSObject::changeProperty(JSContext *cx, const Shape *shape, uintN attrs, uintN m
|
|||
bool
|
||||
JSObject::removeProperty(JSContext *cx, jsid id)
|
||||
{
|
||||
if (sealed()) {
|
||||
reportReadOnlyScope(cx);
|
||||
return false;
|
||||
}
|
||||
|
||||
Shape **spp = nativeSearch(id);
|
||||
Shape *shape = SHAPE_FETCH(spp);
|
||||
if (!shape) {
|
||||
|
|
|
@ -786,9 +786,8 @@ class TypedArrayTemplate
|
|||
return false;
|
||||
}
|
||||
|
||||
makeFastWithPrivate(cx, obj, tarray);
|
||||
rval->setObject(*obj);
|
||||
return true;
|
||||
return makeFastWithPrivate(cx, obj, tarray);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -868,10 +867,8 @@ class TypedArrayTemplate
|
|||
return false;
|
||||
}
|
||||
|
||||
makeFastWithPrivate(cx, nobj, ntarray);
|
||||
|
||||
vp->setObject(*nobj);
|
||||
return true;
|
||||
return makeFastWithPrivate(cx, nobj, ntarray);
|
||||
}
|
||||
|
||||
/* set(array[, offset]) */
|
||||
|
@ -962,13 +959,16 @@ class TypedArrayTemplate
|
|||
}
|
||||
|
||||
// helper used by both the constructor and Slice()
|
||||
static void
|
||||
static bool
|
||||
makeFastWithPrivate(JSContext *cx, JSObject *obj, ThisTypeArray *tarray)
|
||||
{
|
||||
JS_ASSERT(obj->getClass() == slowClass());
|
||||
obj->setSharedNonNativeMap();
|
||||
obj->clasp = fastClass();
|
||||
obj->setPrivate(tarray);
|
||||
|
||||
AutoIdVector props(cx);
|
||||
return obj->preventExtensions(cx, &props);
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
|
@ -882,6 +882,18 @@ typedef JSBool
|
|||
typedef JSBool
|
||||
(* CallOp)(JSContext *cx, uintN argc, Value *vp);
|
||||
|
||||
class AutoIdVector;
|
||||
|
||||
/*
|
||||
* Prepare to make |obj| non-extensible; in particular, fully resolve its properties.
|
||||
* On error, return false.
|
||||
* If |obj| is now ready to become non-extensible, set |*fixed| to true and return true.
|
||||
* If |obj| refuses to become non-extensible, set |*fixed| to false and return true; the
|
||||
* caller will throw an appropriate error.
|
||||
*/
|
||||
typedef JSBool
|
||||
(* FixOp)(JSContext *cx, JSObject *obj, bool *fixed, AutoIdVector *props);
|
||||
|
||||
static inline Native Valueify(JSNative f) { return (Native)f; }
|
||||
static inline JSNative Jsvalify(Native f) { return (JSNative)f; }
|
||||
static inline PropertyOp Valueify(JSPropertyOp f) { return (PropertyOp)f; }
|
||||
|
@ -957,6 +969,7 @@ struct ObjectOps {
|
|||
js::NewEnumerateOp enumerate;
|
||||
JSTypeOfOp typeOf;
|
||||
JSTraceOp trace;
|
||||
js::FixOp fix;
|
||||
JSObjectOp thisObject;
|
||||
JSFinalizeOp clear;
|
||||
};
|
||||
|
|
|
@ -4881,6 +4881,14 @@ xml_trace(JSTracer *trc, JSObject *obj)
|
|||
JS_CALL_TRACER(trc, xml, JSTRACE_XML, "private");
|
||||
}
|
||||
|
||||
static JSBool
|
||||
xml_fix(JSContext *cx, JSObject *obj, bool *success, AutoIdVector *props)
|
||||
{
|
||||
JS_ASSERT(obj->isExtensible());
|
||||
*success = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
xml_clear(JSContext *cx, JSObject *obj)
|
||||
{
|
||||
|
@ -5100,6 +5108,7 @@ JS_FRIEND_DATA(Class) js_XMLClass = {
|
|||
xml_enumerate,
|
||||
xml_typeOf,
|
||||
NULL, /* trace */
|
||||
xml_fix,
|
||||
NULL, /* thisObject */
|
||||
xml_clear
|
||||
}
|
||||
|
|
|
@ -559,8 +559,6 @@ class SetPropCompiler : public PICStubCompiler
|
|||
return disable("dense array");
|
||||
if (!obj->isNative())
|
||||
return disable("non-native");
|
||||
if (obj->sealed())
|
||||
return disable("sealed");
|
||||
|
||||
Class *clasp = obj->getClass();
|
||||
|
||||
|
@ -590,8 +588,6 @@ class SetPropCompiler : public PICStubCompiler
|
|||
|
||||
if (!holder->isNative())
|
||||
return disable("non-native holder");
|
||||
if (holder->sealed())
|
||||
return disable("sealed holder");
|
||||
|
||||
if (!shape->writable())
|
||||
return disable("readonly");
|
||||
|
|
|
@ -141,7 +141,7 @@ stubs::SetName(VMFrame &f, JSAtom *origAtom)
|
|||
JSObject *obj2;
|
||||
JSAtom *atom;
|
||||
if (cache->testForSet(cx, f.regs.pc, obj, &entry, &obj2, &atom)) {
|
||||
JS_ASSERT(!obj->sealed());
|
||||
JS_ASSERT(obj->isExtensible());
|
||||
|
||||
/*
|
||||
* Fast property cache hit, only partially confirmed by
|
||||
|
@ -248,7 +248,7 @@ stubs::SetName(VMFrame &f, JSAtom *origAtom)
|
|||
if (obj == obj2) {
|
||||
shape = entry->vword.toShape();
|
||||
JS_ASSERT(shape->writable());
|
||||
JS_ASSERT(!obj2->sealed());
|
||||
JS_ASSERT(obj2->isExtensible());
|
||||
NATIVE_SET(cx, obj, shape, entry, &rval);
|
||||
}
|
||||
if (shape)
|
||||
|
|
|
@ -60,7 +60,7 @@ pm_construct(JSContext* cx, uintN argc, jsval* vp)
|
|||
if (!obj)
|
||||
return JS_FALSE;
|
||||
|
||||
if (!JS_SealObject(cx, obj, JS_FALSE))
|
||||
if (!JS_FreezeObject(cx, obj))
|
||||
return JS_FALSE;
|
||||
|
||||
PerfMeasurement* p = new PerfMeasurement(PerfMeasurement::EventMask(mask));
|
||||
|
@ -269,8 +269,8 @@ RegisterPerfMeasurement(JSContext *cx, JSObject *global)
|
|||
return 0;
|
||||
}
|
||||
|
||||
if (!JS_SealObject(cx, prototype, JS_FALSE) ||
|
||||
!JS_SealObject(cx, ctor, JS_FALSE)) {
|
||||
if (!JS_FreezeObject(cx, prototype) ||
|
||||
!JS_FreezeObject(cx, ctor)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -761,7 +761,7 @@ ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc)
|
|||
if (JS_GET_CLASS(cx, JS_GetPrototype(cx, obj)) != &global_class) {
|
||||
JSObject *gobj;
|
||||
|
||||
if (!JS_SealObject(cx, obj, JS_TRUE))
|
||||
if (!JS_DeepFreezeObject(cx, obj))
|
||||
return JS_FALSE;
|
||||
gobj = JS_NewGlobalObject(cx, &global_class);
|
||||
if (!gobj)
|
||||
|
@ -2564,20 +2564,6 @@ Clone(JSContext *cx, uintN argc, jsval *vp)
|
|||
return JS_TRUE;
|
||||
}
|
||||
|
||||
static JSBool
|
||||
Seal(JSContext *cx, uintN argc, jsval *vp)
|
||||
{
|
||||
JSObject *target;
|
||||
JSBool deep = JS_FALSE;
|
||||
|
||||
if (!JS_ConvertArguments(cx, argc, JS_ARGV(cx, vp), "o/b", &target, &deep))
|
||||
return JS_FALSE;
|
||||
JS_SET_RVAL(cx, vp, JSVAL_VOID);
|
||||
if (!target)
|
||||
return JS_TRUE;
|
||||
return JS_SealObject(cx, target, deep);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
GetPDA(JSContext *cx, uintN argc, jsval *vp)
|
||||
{
|
||||
|
@ -3025,6 +3011,7 @@ static Class split_global_class = {
|
|||
NULL, /* enumerate */
|
||||
NULL, /* typeOf */
|
||||
NULL, /* trace */
|
||||
NULL, /* fix */
|
||||
split_thisObject,
|
||||
NULL, /* clear */
|
||||
},
|
||||
|
@ -4182,7 +4169,6 @@ static JSFunctionSpec shell_functions[] = {
|
|||
JS_FN("clear", Clear, 0,0),
|
||||
JS_FN("intern", Intern, 1,0),
|
||||
JS_FN("clone", Clone, 1,0),
|
||||
JS_FN("seal", Seal, 1,0),
|
||||
JS_FN("getpda", GetPDA, 1,0),
|
||||
JS_FN("getslx", GetSLX, 1,0),
|
||||
JS_FN("toint32", ToInt32, 1,0),
|
||||
|
@ -4300,7 +4286,6 @@ static const char *const shell_help_messages[] = {
|
|||
"clear([obj]) Clear properties of object",
|
||||
"intern(str) Internalize str in the atom table",
|
||||
"clone(fun[, scope]) Clone function object",
|
||||
"seal(obj[, deep]) Seal object, or object graph if deep",
|
||||
"getpda(obj) Get the property descriptors for obj",
|
||||
"getslx(obj) Get script line extent",
|
||||
"toint32(n) Testing hook for JS_ValueToInt32",
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/licenses/publicdomain/
|
||||
* Contributor:
|
||||
* Jeff Walden <jwalden+code@mit.edu>
|
||||
*/
|
||||
|
||||
var gTestfile = 'extensibility.js';
|
||||
//-----------------------------------------------------------------------------
|
||||
var BUGNUMBER = 492849;
|
||||
var summary = 'XML values cannot have their [[Extensible]] property changed';
|
||||
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
/**************
|
||||
* BEGIN TEST *
|
||||
**************/
|
||||
|
||||
var x = <foo/>;
|
||||
|
||||
assertEq(Object.isExtensible(x), true);
|
||||
|
||||
try
|
||||
{
|
||||
Object.preventExtensions(x);
|
||||
throw new Error("didn't throw");
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
assertEq(e instanceof TypeError, true,
|
||||
"xmlValue.[[Extensible]] cannot be changed");
|
||||
}
|
||||
|
||||
assertEq(Object.isExtensible(x), true);
|
||||
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
if (typeof reportCompare === "function")
|
||||
reportCompare(true, true);
|
||||
|
||||
print("All tests passed!");
|
|
@ -20,4 +20,5 @@ script regress-410192.js
|
|||
script regress-450871-01.js
|
||||
script regress-450871-02.js
|
||||
script regress-462734-01.js
|
||||
script extensibility.js
|
||||
script regress-595207.js
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/licenses/publicdomain/
|
||||
* Contributor:
|
||||
* Jeff Walden <jwalden+code@mit.edu>
|
||||
*/
|
||||
|
||||
var gTestfile = '15.2.3.10-01.js';
|
||||
//-----------------------------------------------------------------------------
|
||||
var BUGNUMBER = 492849;
|
||||
var summary = 'ES5: Implement Object.preventExtensions, Object.isExtensible';
|
||||
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
/**************
|
||||
* BEGIN TEST *
|
||||
**************/
|
||||
|
||||
function trySetProperty(o, p, v, strict)
|
||||
{
|
||||
function strictSetProperty()
|
||||
{
|
||||
"use strict";
|
||||
o[p] = v;
|
||||
}
|
||||
|
||||
function setProperty()
|
||||
{
|
||||
o[p] = v;
|
||||
}
|
||||
|
||||
assertEq(Object.prototype.hasOwnProperty.call(o, p), false);
|
||||
|
||||
try
|
||||
{
|
||||
if (strict)
|
||||
strictSetProperty();
|
||||
else
|
||||
setProperty();
|
||||
if (o[p] === v)
|
||||
return "set";
|
||||
if (p in o)
|
||||
return "set-converted";
|
||||
return "swallowed";
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
return "throw";
|
||||
}
|
||||
}
|
||||
|
||||
function tryDefineProperty(o, p, v)
|
||||
{
|
||||
assertEq(Object.prototype.hasOwnProperty.call(o, p), false);
|
||||
|
||||
try
|
||||
{
|
||||
Object.defineProperty(o, p, { value: v });
|
||||
if (o[p] === v)
|
||||
return "set";
|
||||
if (p in o)
|
||||
return "set-converted";
|
||||
return "swallowed";
|
||||
}
|
||||
catch (e)
|
||||
{
|
||||
return "throw";
|
||||
}
|
||||
}
|
||||
|
||||
assertEq(typeof Object.preventExtensions, "function");
|
||||
assertEq(Object.preventExtensions.length, 1);
|
||||
|
||||
var slowArray = [1, 2, 3];
|
||||
slowArray.slow = 5;
|
||||
var objs =
|
||||
[{}, { 1: 2 }, { a: 3 }, [], [1], [, 1], slowArray, function a(){}, /a/];
|
||||
|
||||
for (var i = 0, sz = objs.length; i < sz; i++)
|
||||
{
|
||||
var o = objs[i];
|
||||
assertEq(Object.isExtensible(o), true, "object " + i + " not extensible?");
|
||||
|
||||
var o2 = Object.preventExtensions(o);
|
||||
assertEq(o, o2);
|
||||
|
||||
assertEq(Object.isExtensible(o), false, "object " + i + " is extensible?");
|
||||
|
||||
assertEq(trySetProperty(o, "baz", 17, true), "throw",
|
||||
"unexpected behavior for strict-mode property-addition to " +
|
||||
"object " + i);
|
||||
assertEq(trySetProperty(o, "baz", 17, false), "swallowed",
|
||||
"unexpected behavior for property-addition to object " + i);
|
||||
|
||||
assertEq(tryDefineProperty(o, "baz", 17), "throw",
|
||||
"unexpected behavior for new property definition on object " + i);
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
reportCompare(true, true);
|
||||
|
||||
print("All tests passed!");
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/licenses/publicdomain/
|
||||
* Contributor:
|
||||
* Jeff Walden <jwalden+code@mit.edu>
|
||||
*/
|
||||
|
||||
var gTestfile = '15.2.3.4-01.js';
|
||||
//-----------------------------------------------------------------------------
|
||||
var BUGNUMBER = 492849;
|
||||
var summary = 'ES5: Implement Object.preventExtensions, Object.isExtensible';
|
||||
|
||||
print(BUGNUMBER + ": " + summary);
|
||||
|
||||
/**************
|
||||
* BEGIN TEST *
|
||||
**************/
|
||||
|
||||
assertEq(typeof Object.isExtensible, "function");
|
||||
assertEq(Object.isExtensible.length, 1);
|
||||
|
||||
var slowArray = [1, 2, 3];
|
||||
slowArray.slow = 5;
|
||||
var objs =
|
||||
[{}, { 1: 2 }, { a: 3 }, [], [1], [, 1], slowArray, function a(){}, /a/];
|
||||
|
||||
for (var i = 0, sz = objs.length; i < sz; i++)
|
||||
{
|
||||
var o = objs[i];
|
||||
assertEq(Object.isExtensible(o), true, "object " + i + " not extensible?");
|
||||
|
||||
var o2 = Object.preventExtensions(o);
|
||||
assertEq(o, o2);
|
||||
|
||||
assertEq(Object.isExtensible(o), false, "object " + i + " is extensible?");
|
||||
}
|
||||
|
||||
/******************************************************************************/
|
||||
|
||||
reportCompare(true, true);
|
||||
|
||||
print("All tests passed!");
|
|
@ -14,6 +14,8 @@ skip-if(!xulRuntime.shell) script 15.2.3.6-redefinition-2-of-4.js # uses shell l
|
|||
skip-if(!xulRuntime.shell) script 15.2.3.6-redefinition-3-of-4.js # uses shell load() function
|
||||
skip-if(!xulRuntime.shell) script 15.2.3.6-redefinition-4-of-4.js # uses shell load() function
|
||||
script 15.2.3.7-01.js
|
||||
script extensibility-01.js
|
||||
script extensibility-02.js
|
||||
script 15.2.3.14-01.js # does not use reportCompare
|
||||
skip-if(!xulRuntime.shell) script 15.2.3.6-middle-redefinition-1-of-8.js # uses shell load() function
|
||||
skip-if(!xulRuntime.shell) script 15.2.3.6-middle-redefinition-2-of-8.js # uses shell load() function
|
||||
|
|
|
@ -189,7 +189,6 @@ skip script regress-437288-01.js # obsolete test
|
|||
script regress-44009.js
|
||||
script regress-443569.js
|
||||
script regress-446386.js
|
||||
script regress-449657.js
|
||||
script regress-452168.js
|
||||
script regress-452178.js
|
||||
script regress-452329.js
|
||||
|
|
|
@ -1,91 +0,0 @@
|
|||
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* ***** BEGIN LICENSE BLOCK *****
|
||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
||||
*
|
||||
* The contents of this file are subject to the Mozilla Public License Version
|
||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
||||
* the License. You may obtain a copy of the License at
|
||||
* http://www.mozilla.org/MPL/
|
||||
*
|
||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
||||
* for the specific language governing rights and limitations under the
|
||||
* License.
|
||||
*
|
||||
* The Original Code is JavaScript Engine testing utilities.
|
||||
*
|
||||
* The Initial Developer of the Original Code is
|
||||
* Mozilla Foundation.
|
||||
* Portions created by the Initial Developer are Copyright (C) 2008
|
||||
* the Initial Developer. All Rights Reserved.
|
||||
*
|
||||
* Contributor(s): Christopher Lenz
|
||||
* ash_mozilla
|
||||
*
|
||||
* Alternatively, the contents of this file may be used under the terms of
|
||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
||||
* of those above. If you wish to allow use of your version of this file only
|
||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
||||
* use your version of this file under the terms of the MPL, indicate your
|
||||
* decision by deleting the provisions above and replace them with the notice
|
||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
||||
* the provisions above, a recipient may use your version of this file under
|
||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
||||
*
|
||||
* ***** END LICENSE BLOCK ***** */
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
var BUGNUMBER = 449657;
|
||||
var summary = 'JS_SealObject on Arrays';
|
||||
var actual = '';
|
||||
var expect = '';
|
||||
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
test();
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
function test()
|
||||
{
|
||||
enterFunc ('test');
|
||||
printBugNumber(BUGNUMBER);
|
||||
printStatus (summary);
|
||||
|
||||
if (typeof seal != 'function')
|
||||
{
|
||||
expect = actual = 'JS_SealObject not supported, test skipped.';
|
||||
reportCompare(expect, actual, summary);
|
||||
}
|
||||
else
|
||||
{
|
||||
try
|
||||
{
|
||||
var a= [1, 2, 3];
|
||||
seal(a);
|
||||
}
|
||||
catch(ex)
|
||||
{
|
||||
actual = ex + '';
|
||||
}
|
||||
reportCompare(expect, actual, summary + ': 1');
|
||||
|
||||
expect = 'TypeError: a.length is read-only';
|
||||
actual = '';
|
||||
try
|
||||
{
|
||||
a = [1,2,3];
|
||||
a[4] = 2;
|
||||
seal(a);
|
||||
a.length = 5;
|
||||
}
|
||||
catch(ex)
|
||||
{
|
||||
actual = ex + '';
|
||||
}
|
||||
reportCompare(expect, actual, summary + ': 2');
|
||||
}
|
||||
|
||||
exitFunc ('test');
|
||||
}
|
|
@ -1263,7 +1263,7 @@ ProcessArgs(JSContext *cx, JSObject *obj, char **argv, int argc)
|
|||
if (JS_GET_CLASS(cx, JS_GetPrototype(cx, obj)) != &global_class) {
|
||||
JSObject *gobj;
|
||||
|
||||
if (!JS_SealObject(cx, obj, JS_TRUE))
|
||||
if (!JS_DeepFreezeObject(cx, obj))
|
||||
return JS_FALSE;
|
||||
gobj = JS_NewGlobalObject(cx, &global_class);
|
||||
if (!gobj)
|
||||
|
|
|
@ -1035,7 +1035,7 @@ XPCNativeWrapper::AttachNewConstructorObject(XPCCallContext &ccx,
|
|||
// Make sure our prototype chain is empty and that people can't mess
|
||||
// with XPCNativeWrapper.prototype.
|
||||
::JS_SetPrototype(ccx, class_obj, nsnull);
|
||||
if (!::JS_SealObject(ccx, class_obj, JS_FALSE)) {
|
||||
if (!::JS_FreezeObject(ccx, class_obj)) {
|
||||
NS_WARNING("Failed to seal XPCNativeWrapper.prototype");
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
|
|
@ -370,7 +370,7 @@ AttachNewConstructorObject(XPCCallContext &ccx, JSObject *aGlobalObject)
|
|||
// Make sure our prototype chain is empty and that people can't mess
|
||||
// with XPCSafeJSObjectWrapper.prototype.
|
||||
::JS_SetPrototype(ccx, class_obj, nsnull);
|
||||
if (!::JS_SealObject(ccx, class_obj, JS_FALSE)) {
|
||||
if (!::JS_FreezeObject(ccx, class_obj)) {
|
||||
NS_WARNING("Failed to seal XPCSafeJSObjectWrapper.prototype");
|
||||
return PR_FALSE;
|
||||
}
|
||||
|
|
|
@ -1317,6 +1317,7 @@ XPC_WN_JSOp_ThisObject(JSContext *cx, JSObject *obj);
|
|||
js::Valueify(XPC_WN_JSOp_Enumerate), \
|
||||
XPC_WN_JSOp_TypeOf_Function, \
|
||||
nsnull, /* trace */ \
|
||||
nsnull, /* fix */ \
|
||||
XPC_WN_JSOp_ThisObject, \
|
||||
XPC_WN_JSOp_Clear \
|
||||
}
|
||||
|
@ -1333,6 +1334,7 @@ XPC_WN_JSOp_ThisObject(JSContext *cx, JSObject *obj);
|
|||
js::Valueify(XPC_WN_JSOp_Enumerate), \
|
||||
XPC_WN_JSOp_TypeOf_Object, \
|
||||
nsnull, /* trace */ \
|
||||
nsnull, /* fix */ \
|
||||
XPC_WN_JSOp_ThisObject, \
|
||||
XPC_WN_JSOp_Clear \
|
||||
}
|
||||
|
|
|
@ -964,6 +964,7 @@ js::Class XPC_WN_NoHelper_JSClass = {
|
|||
js::Valueify(XPC_WN_JSOp_Enumerate),
|
||||
XPC_WN_JSOp_TypeOf_Object,
|
||||
nsnull, // trace
|
||||
nsnull, // fix
|
||||
XPC_WN_JSOp_ThisObject,
|
||||
XPC_WN_JSOp_Clear
|
||||
}
|
||||
|
|
|
@ -107,8 +107,7 @@ SealObjectAndPrototype(JSContext* cx, JSObject* parent, const char* name)
|
|||
return false;
|
||||
|
||||
JSObject* prototype = JSVAL_TO_OBJECT(prop);
|
||||
return JS_SealObject(cx, obj, JS_FALSE) &&
|
||||
JS_SealObject(cx, prototype, JS_FALSE);
|
||||
return JS_FreezeObject(cx, obj) && JS_FreezeObject(cx, prototype);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
|
@ -133,7 +132,7 @@ InitAndSealCTypesClass(JSContext* cx, JSObject* global)
|
|||
|
||||
// Finally, seal the global object, for good measure. (But not recursively;
|
||||
// this breaks things.)
|
||||
return JS_SealObject(cx, global, JS_FALSE);
|
||||
return JS_FreezeObject(cx, global);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
|
@ -81,8 +81,7 @@ SealObjectAndPrototype(JSContext* cx, JSObject* parent, const char* name)
|
|||
return false;
|
||||
|
||||
JSObject* prototype = JSVAL_TO_OBJECT(prop);
|
||||
return JS_SealObject(cx, obj, JS_FALSE) &&
|
||||
JS_SealObject(cx, prototype, JS_FALSE);
|
||||
return JS_FreezeObject(cx, obj) && JS_FreezeObject(cx, prototype);
|
||||
}
|
||||
|
||||
static JSBool
|
||||
|
@ -101,7 +100,7 @@ InitAndSealPerfMeasurementClass(JSContext* cx, JSObject* global)
|
|||
|
||||
// Finally, seal the global object, for good measure. (But not recursively;
|
||||
// this breaks things.)
|
||||
return JS_SealObject(cx, global, JS_FALSE);
|
||||
return JS_FreezeObject(cx, global);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
|
Загрузка…
Ссылка в новой задаче