mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 430133 - Implement ES3.1's Object.defineProperty and Object.defineProperties. r=jorendorff
This commit is contained in:
parent
d56b9f7fc4
commit
285792a4b4
@ -317,3 +317,9 @@ MSG_DEF(JSMSG_DUPLICATE_PROPERTY, 234, 1, JSEXN_SYNTAXERR, "property name {0
|
|||||||
MSG_DEF(JSMSG_DEPRECATED_DELETE_OPERAND, 235, 0, JSEXN_SYNTAXERR, "Applying the 'delete' operator to an unqualified name is deprecated")
|
MSG_DEF(JSMSG_DEPRECATED_DELETE_OPERAND, 235, 0, JSEXN_SYNTAXERR, "Applying the 'delete' operator to an unqualified name is deprecated")
|
||||||
MSG_DEF(JSMSG_DEPRECATED_ASSIGN, 236, 1, JSEXN_SYNTAXERR, "assignment to {0} is deprecated")
|
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_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_CANT_DEFINE_ARRAY_INDEX,243, 0, JSEXN_TYPEERR, "can't define array index property")
|
||||||
|
697
js/src/jsobj.cpp
697
js/src/jsobj.cpp
@ -422,11 +422,11 @@ MarkSharpObjects(JSContext *cx, JSObject *obj, JSIdArray **idap)
|
|||||||
if (OBJ_IS_NATIVE(obj2) &&
|
if (OBJ_IS_NATIVE(obj2) &&
|
||||||
(attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
|
(attrs & (JSPROP_GETTER | JSPROP_SETTER))) {
|
||||||
JSScopeProperty *sprop = (JSScopeProperty *) prop;
|
JSScopeProperty *sprop = (JSScopeProperty *) prop;
|
||||||
val = JSVAL_NULL;
|
val = JSVAL_VOID;
|
||||||
if (attrs & JSPROP_GETTER)
|
if (attrs & JSPROP_GETTER)
|
||||||
val = sprop->getterValue();
|
val = sprop->getterValue();
|
||||||
if (attrs & JSPROP_SETTER) {
|
if (attrs & JSPROP_SETTER) {
|
||||||
if (val != JSVAL_NULL) {
|
if (val != JSVAL_VOID) {
|
||||||
/* Mark the getter, then set val to setter. */
|
/* Mark the getter, then set val to setter. */
|
||||||
ok = (MarkSharpObjects(cx, JSVAL_TO_OBJECT(val),
|
ok = (MarkSharpObjects(cx, JSVAL_TO_OBJECT(val),
|
||||||
NULL)
|
NULL)
|
||||||
@ -1693,45 +1693,45 @@ js_HasOwnPropertyHelper(JSContext *cx, JSLookupPropOp lookup, uintN argc,
|
|||||||
if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id))
|
if (!JS_ValueToId(cx, argc != 0 ? vp[2] : JSVAL_VOID, &id))
|
||||||
return JS_FALSE;
|
return JS_FALSE;
|
||||||
|
|
||||||
JSBool found;
|
|
||||||
JSObject *obj = JS_THIS_OBJECT(cx, vp);
|
JSObject *obj = JS_THIS_OBJECT(cx, vp);
|
||||||
if (!obj || !js_HasOwnProperty(cx, lookup, obj, id, &found))
|
JSObject *obj2;
|
||||||
|
JSProperty *prop;
|
||||||
|
if (!obj || !js_HasOwnProperty(cx, lookup, obj, id, &obj2, &prop))
|
||||||
return JS_FALSE;
|
return JS_FALSE;
|
||||||
*vp = BOOLEAN_TO_JSVAL(found);
|
if (prop) {
|
||||||
|
*vp = JSVAL_TRUE;
|
||||||
|
obj2->dropProperty(cx, prop);
|
||||||
|
} else {
|
||||||
|
*vp = JSVAL_FALSE;
|
||||||
|
}
|
||||||
return JS_TRUE;
|
return JS_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSBool
|
JSBool
|
||||||
js_HasOwnProperty(JSContext *cx, JSLookupPropOp lookup, JSObject *obj, jsid id,
|
js_HasOwnProperty(JSContext *cx, JSLookupPropOp lookup, JSObject *obj, jsid id,
|
||||||
JSBool *foundp)
|
JSObject **objp, JSProperty **propp)
|
||||||
{
|
{
|
||||||
JSObject *obj2;
|
if (!lookup(cx, obj, id, objp, propp))
|
||||||
JSProperty *prop;
|
|
||||||
JSScopeProperty *sprop;
|
|
||||||
|
|
||||||
if (!lookup(cx, obj, id, &obj2, &prop))
|
|
||||||
return JS_FALSE;
|
return JS_FALSE;
|
||||||
if (!prop) {
|
if (!*propp)
|
||||||
*foundp = JS_FALSE;
|
return JS_TRUE;
|
||||||
} else if (obj2 == obj) {
|
|
||||||
*foundp = JS_TRUE;
|
if (*objp == obj)
|
||||||
} else {
|
return JS_TRUE;
|
||||||
JSClass *clasp;
|
|
||||||
JSExtendedClass *xclasp;
|
JSExtendedClass *xclasp;
|
||||||
JSObject *outer;
|
JSObject *outer;
|
||||||
|
JSClass *clasp = (*objp)->getClass();
|
||||||
clasp = OBJ_GET_CLASS(cx, obj2);
|
|
||||||
if (!(clasp->flags & JSCLASS_IS_EXTENDED) ||
|
if (!(clasp->flags & JSCLASS_IS_EXTENDED) ||
|
||||||
!(xclasp = (JSExtendedClass *) clasp)->outerObject) {
|
!(xclasp = (JSExtendedClass *) clasp)->outerObject) {
|
||||||
outer = NULL;
|
outer = NULL;
|
||||||
} else {
|
} else {
|
||||||
outer = xclasp->outerObject(cx, obj2);
|
outer = xclasp->outerObject(cx, *objp);
|
||||||
if (!outer)
|
if (!outer)
|
||||||
return JS_FALSE;
|
return JS_FALSE;
|
||||||
}
|
}
|
||||||
if (outer == obj) {
|
if (outer != *objp) {
|
||||||
*foundp = JS_TRUE;
|
if (OBJ_IS_NATIVE(*objp) && obj->getClass() == clasp) {
|
||||||
} else if (OBJ_IS_NATIVE(obj2) && OBJ_GET_CLASS(cx, obj) == clasp) {
|
|
||||||
/*
|
/*
|
||||||
* The combination of JSPROP_SHARED and JSPROP_PERMANENT in a
|
* The combination of JSPROP_SHARED and JSPROP_PERMANENT in a
|
||||||
* delegated property makes that property appear to be direct in
|
* delegated property makes that property appear to be direct in
|
||||||
@ -1747,14 +1747,15 @@ js_HasOwnProperty(JSContext *cx, JSLookupPropOp lookup, JSObject *obj, jsid id,
|
|||||||
* the property, there is no way to tell whether it is directly
|
* the property, there is no way to tell whether it is directly
|
||||||
* owned, or indirectly delegated.
|
* owned, or indirectly delegated.
|
||||||
*/
|
*/
|
||||||
sprop = (JSScopeProperty *)prop;
|
if (!SPROP_IS_SHARED_PERMANENT((JSScopeProperty *) *propp)) {
|
||||||
*foundp = SPROP_IS_SHARED_PERMANENT(sprop);
|
(*objp)->dropProperty(cx, *propp);
|
||||||
|
*propp = NULL;
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
*foundp = JS_FALSE;
|
(*objp)->dropProperty(cx, *propp);
|
||||||
|
*propp = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (prop)
|
|
||||||
obj2->dropProperty(cx, prop);
|
|
||||||
return JS_TRUE;
|
return JS_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1763,15 +1764,18 @@ static JSBool FASTCALL
|
|||||||
Object_p_hasOwnProperty(JSContext* cx, JSObject* obj, JSString *str)
|
Object_p_hasOwnProperty(JSContext* cx, JSObject* obj, JSString *str)
|
||||||
{
|
{
|
||||||
jsid id;
|
jsid id;
|
||||||
JSBool found;
|
|
||||||
|
|
||||||
|
JSObject *pobj;
|
||||||
|
JSProperty *prop;
|
||||||
if (!js_ValueToStringId(cx, STRING_TO_JSVAL(str), &id) ||
|
if (!js_ValueToStringId(cx, STRING_TO_JSVAL(str), &id) ||
|
||||||
!js_HasOwnProperty(cx, obj->map->ops->lookupProperty, obj, id, &found)) {
|
!js_HasOwnProperty(cx, obj->map->ops->lookupProperty, obj, id, &pobj, &prop)) {
|
||||||
js_SetBuiltinError(cx);
|
js_SetBuiltinError(cx);
|
||||||
return JSVAL_TO_BOOLEAN(JSVAL_VOID);
|
return JSVAL_TO_BOOLEAN(JSVAL_VOID);
|
||||||
}
|
}
|
||||||
|
|
||||||
return found;
|
if (prop)
|
||||||
|
pobj->dropProperty(cx, prop);
|
||||||
|
return !!prop;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -2020,20 +2024,15 @@ obj_getOwnPropertyDescriptor(JSContext *cx, uintN argc, jsval *vp)
|
|||||||
if (!JS_ValueToId(cx, argc >= 2 ? vp[3] : JSVAL_VOID, nameidr.addr()))
|
if (!JS_ValueToId(cx, argc >= 2 ? vp[3] : JSVAL_VOID, nameidr.addr()))
|
||||||
return JS_FALSE;
|
return JS_FALSE;
|
||||||
|
|
||||||
JSBool found;
|
JSObject *pobj;
|
||||||
if (!js_HasOwnProperty(cx, obj->map->ops->lookupProperty, obj, nameidr.id(), &found))
|
JSProperty *prop;
|
||||||
|
if (!js_HasOwnProperty(cx, obj->map->ops->lookupProperty, obj, nameidr.id(), &pobj, &prop))
|
||||||
return JS_FALSE;
|
return JS_FALSE;
|
||||||
if (!found) {
|
if (!prop) {
|
||||||
*vp = JSVAL_VOID;
|
*vp = JSVAL_VOID;
|
||||||
return JS_TRUE;
|
return JS_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
JSObject *pobj;
|
|
||||||
JSProperty *prop;
|
|
||||||
if (!obj->lookupProperty(cx, nameidr.id(), &pobj, &prop))
|
|
||||||
return JS_FALSE;
|
|
||||||
JS_ASSERT(prop);
|
|
||||||
|
|
||||||
uintN attrs;
|
uintN attrs;
|
||||||
if (!pobj->getAttributes(cx, nameidr.id(), prop, &attrs)) {
|
if (!pobj->getAttributes(cx, nameidr.id(), prop, &attrs)) {
|
||||||
pobj->dropProperty(cx, prop);
|
pobj->dropProperty(cx, prop);
|
||||||
@ -2046,9 +2045,9 @@ obj_getOwnPropertyDescriptor(JSContext *cx, uintN argc, jsval *vp)
|
|||||||
if (OBJ_IS_NATIVE(obj)) {
|
if (OBJ_IS_NATIVE(obj)) {
|
||||||
JSScopeProperty *sprop = reinterpret_cast<JSScopeProperty *>(prop);
|
JSScopeProperty *sprop = reinterpret_cast<JSScopeProperty *>(prop);
|
||||||
if (attrs & JSPROP_GETTER)
|
if (attrs & JSPROP_GETTER)
|
||||||
roots[0] = js_CastAsObjectJSVal(sprop->getter);
|
roots[0] = sprop->getterValue();
|
||||||
if (attrs & JSPROP_SETTER)
|
if (attrs & JSPROP_SETTER)
|
||||||
roots[1] = js_CastAsObjectJSVal(sprop->setter);
|
roots[1] = sprop->setterValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
pobj->dropProperty(cx, prop);
|
pobj->dropProperty(cx, prop);
|
||||||
@ -2143,6 +2142,618 @@ obj_keys(JSContext *cx, uintN argc, jsval *vp)
|
|||||||
return JS_TRUE;
|
return JS_TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static JSBool
|
||||||
|
HasProperty(JSContext* cx, JSObject* obj, jsid id, jsval* vp, JSBool* answerp)
|
||||||
|
{
|
||||||
|
if (!JS_HasPropertyById(cx, obj, id, answerp))
|
||||||
|
return JS_FALSE;
|
||||||
|
if (!*answerp) {
|
||||||
|
*vp = JSVAL_VOID;
|
||||||
|
return JS_TRUE;
|
||||||
|
}
|
||||||
|
return JS_GetPropertyById(cx, obj, id, vp);
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyDescriptor::PropertyDescriptor()
|
||||||
|
: id(INT_JSVAL_TO_JSID(JSVAL_ZERO)),
|
||||||
|
value(JSVAL_VOID),
|
||||||
|
get(JSVAL_VOID),
|
||||||
|
set(JSVAL_VOID),
|
||||||
|
attrs(0),
|
||||||
|
hasGet(false),
|
||||||
|
hasSet(false),
|
||||||
|
hasValue(false),
|
||||||
|
hasWritable(false),
|
||||||
|
hasEnumerable(false),
|
||||||
|
hasConfigurable(false)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
PropertyDescriptor::initialize(JSContext* cx, jsid id, jsval v)
|
||||||
|
{
|
||||||
|
this->id = id;
|
||||||
|
|
||||||
|
/* 8.10.5 step 1 */
|
||||||
|
if (JSVAL_IS_PRIMITIVE(v)) {
|
||||||
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||||
|
JSMSG_NOT_NONNULL_OBJECT,
|
||||||
|
js_getter_str);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
JSObject* desc = JSVAL_TO_OBJECT(v);
|
||||||
|
|
||||||
|
/* Start with the proper defaults. */
|
||||||
|
attrs = JSPROP_PERMANENT | JSPROP_READONLY;
|
||||||
|
|
||||||
|
JSBool hasProperty;
|
||||||
|
|
||||||
|
/* 8.10.5 step 3 */
|
||||||
|
if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.enumerableAtom), &v,
|
||||||
|
&hasProperty)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (hasProperty) {
|
||||||
|
hasEnumerable = JS_TRUE;
|
||||||
|
if (js_ValueToBoolean(v))
|
||||||
|
attrs |= JSPROP_ENUMERATE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 8.10.5 step 4 */
|
||||||
|
if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.configurableAtom), &v,
|
||||||
|
&hasProperty)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (hasProperty) {
|
||||||
|
hasConfigurable = JS_TRUE;
|
||||||
|
if (js_ValueToBoolean(v))
|
||||||
|
attrs &= ~JSPROP_PERMANENT;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 8.10.5 step 5 */
|
||||||
|
if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.valueAtom), &v, &hasProperty))
|
||||||
|
return false;
|
||||||
|
if (hasProperty) {
|
||||||
|
hasValue = true;
|
||||||
|
value = v;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 8.10.6 step 6 */
|
||||||
|
if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.writableAtom), &v, &hasProperty))
|
||||||
|
return false;
|
||||||
|
if (hasProperty) {
|
||||||
|
hasWritable = JS_TRUE;
|
||||||
|
if (js_ValueToBoolean(v))
|
||||||
|
attrs &= ~JSPROP_READONLY;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 8.10.7 step 7 */
|
||||||
|
if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.getAtom), &v, &hasProperty))
|
||||||
|
return false;
|
||||||
|
if (hasProperty) {
|
||||||
|
if ((JSVAL_IS_PRIMITIVE(v) || !js_IsCallable(JSVAL_TO_OBJECT(v), cx)) &&
|
||||||
|
v != JSVAL_VOID) {
|
||||||
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||||
|
JSMSG_BAD_GETTER_OR_SETTER,
|
||||||
|
js_getter_str);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
hasGet = true;
|
||||||
|
get = v;
|
||||||
|
attrs |= JSPROP_GETTER | JSPROP_SHARED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 8.10.7 step 8 */
|
||||||
|
if (!HasProperty(cx, desc, ATOM_TO_JSID(cx->runtime->atomState.setAtom), &v, &hasProperty))
|
||||||
|
return false;
|
||||||
|
if (hasProperty) {
|
||||||
|
if ((JSVAL_IS_PRIMITIVE(v) || !js_IsCallable(JSVAL_TO_OBJECT(v), cx)) &&
|
||||||
|
v != JSVAL_VOID) {
|
||||||
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||||
|
JSMSG_BAD_GETTER_OR_SETTER,
|
||||||
|
js_setter_str);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
hasSet = true;
|
||||||
|
set = v;
|
||||||
|
attrs |= JSPROP_SETTER | JSPROP_SHARED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 8.10.7 step 9 */
|
||||||
|
if ((hasGet || hasSet) && (hasValue || hasWritable)) {
|
||||||
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_INVALID_DESCRIPTOR);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef js::Vector<PropertyDescriptor, 1> PropertyDescriptorArray;
|
||||||
|
|
||||||
|
class AutoDescriptorArray : private JSTempValueRooter
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
JSContext *cx;
|
||||||
|
PropertyDescriptorArray descriptors;
|
||||||
|
|
||||||
|
public:
|
||||||
|
AutoDescriptorArray(JSContext *cx)
|
||||||
|
: cx(cx), descriptors(cx)
|
||||||
|
{
|
||||||
|
JS_PUSH_TEMP_ROOT_TRACE(cx, trace, this);
|
||||||
|
}
|
||||||
|
~AutoDescriptorArray() {
|
||||||
|
JS_POP_TEMP_ROOT(cx, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool append(PropertyDescriptor &desc) {
|
||||||
|
return descriptors.append(desc);
|
||||||
|
}
|
||||||
|
|
||||||
|
PropertyDescriptor& operator[](size_t i) {
|
||||||
|
JS_ASSERT(i < descriptors.length());
|
||||||
|
return descriptors[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void trace(JSTracer *trc, JSTempValueRooter *tvr) {
|
||||||
|
PropertyDescriptorArray &descs =
|
||||||
|
static_cast<AutoDescriptorArray *>(tvr)->descriptors;
|
||||||
|
for (size_t i = 0, len = descs.length(); i < len; i++) {
|
||||||
|
PropertyDescriptor &desc = descs[i];
|
||||||
|
|
||||||
|
JS_CALL_VALUE_TRACER(trc, desc.value, "PropertyDescriptor::value");
|
||||||
|
JS_CALL_VALUE_TRACER(trc, desc.get, "PropertyDescriptor::get");
|
||||||
|
JS_CALL_VALUE_TRACER(trc, desc.set, "PropertyDescriptor::set");
|
||||||
|
js_TraceId(trc, desc.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
static JSBool
|
||||||
|
Reject(JSContext *cx, uintN errorNumber, bool throwError, jsid id, bool *rval)
|
||||||
|
{
|
||||||
|
if (throwError) {
|
||||||
|
jsid idstr;
|
||||||
|
if (!js_ValueToStringId(cx, ID_TO_VALUE(id), &idstr))
|
||||||
|
return JS_FALSE;
|
||||||
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, errorNumber,
|
||||||
|
JS_GetStringBytes(JSVAL_TO_STRING(ID_TO_VALUE(idstr))));
|
||||||
|
return JS_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
*rval = false;
|
||||||
|
return JS_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSBool
|
||||||
|
Reject(JSContext *cx, uintN errorNumber, bool throwError, bool *rval)
|
||||||
|
{
|
||||||
|
if (throwError) {
|
||||||
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, errorNumber);
|
||||||
|
return JS_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
*rval = false;
|
||||||
|
return JS_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSBool
|
||||||
|
Reject(JSContext *cx, JSObject *obj, JSProperty *prop, uintN errorNumber, bool throwError,
|
||||||
|
jsid id, bool *rval)
|
||||||
|
{
|
||||||
|
obj->dropProperty(cx, prop);
|
||||||
|
return Reject(cx, errorNumber, throwError, id, rval);
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSBool
|
||||||
|
DefinePropertyObject(JSContext *cx, JSObject *obj, const PropertyDescriptor &desc,
|
||||||
|
bool throwError, bool *rval)
|
||||||
|
{
|
||||||
|
/* 8.12.9 step 1. */
|
||||||
|
JSProperty *current;
|
||||||
|
JSObject *obj2;
|
||||||
|
JS_ASSERT(obj->map->ops->lookupProperty == js_LookupProperty);
|
||||||
|
if (!js_HasOwnProperty(cx, js_LookupProperty, obj, desc.id, &obj2, ¤t))
|
||||||
|
return JS_FALSE;
|
||||||
|
|
||||||
|
JS_ASSERT(obj->map->ops->defineProperty == js_DefineProperty);
|
||||||
|
|
||||||
|
/* 8.12.9 steps 2-4. */
|
||||||
|
JSScope *scope = OBJ_SCOPE(obj);
|
||||||
|
if (!current) {
|
||||||
|
if (scope->sealed())
|
||||||
|
return Reject(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
|
||||||
|
|
||||||
|
*rval = true;
|
||||||
|
|
||||||
|
if (desc.isGenericDescriptor() || desc.isDataDescriptor()) {
|
||||||
|
JS_ASSERT(obj->map->ops->defineProperty == js_DefineProperty);
|
||||||
|
return js_DefineProperty(cx, obj, desc.id, desc.value,
|
||||||
|
JS_PropertyStub, JS_PropertyStub, desc.attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
JS_ASSERT(desc.isAccessorDescriptor());
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Getters and setters are just like watchpoints from an access
|
||||||
|
* control point of view.
|
||||||
|
*/
|
||||||
|
jsval dummy;
|
||||||
|
uintN dummyAttrs;
|
||||||
|
JS_ASSERT(obj->map->ops->checkAccess == js_CheckAccess);
|
||||||
|
if (!js_CheckAccess(cx, obj, desc.id, JSACC_WATCH, &dummy, &dummyAttrs))
|
||||||
|
return JS_FALSE;
|
||||||
|
|
||||||
|
return js_DefineProperty(cx, obj, desc.id, JSVAL_VOID,
|
||||||
|
desc.getterObject() ? desc.getter() : JS_PropertyStub,
|
||||||
|
desc.setterObject() ? desc.setter() : JS_PropertyStub,
|
||||||
|
desc.attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 8.12.9 steps 5-6 (note 5 is merely a special case of 6). */
|
||||||
|
jsval v = JSVAL_VOID;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* In the special case of shared permanent properties, the "own" property
|
||||||
|
* can be found on a different object. In that case the returned property
|
||||||
|
* might not be native, except: the shared permanent property optimization
|
||||||
|
* is not applied if the objects have different classes (bug 320854), as
|
||||||
|
* must be enforced by js_HasOwnProperty for the JSScopeProperty cast below
|
||||||
|
* to be safe.
|
||||||
|
*/
|
||||||
|
JS_ASSERT(obj->getClass() == obj2->getClass());
|
||||||
|
|
||||||
|
JSScopeProperty *sprop = reinterpret_cast<JSScopeProperty *>(current);
|
||||||
|
do {
|
||||||
|
if (desc.isAccessorDescriptor()) {
|
||||||
|
if (!sprop->isAccessorDescriptor())
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (desc.hasGet &&
|
||||||
|
!js_SameValue(desc.getterValue(),
|
||||||
|
(sprop->attrs & JSPROP_GETTER)
|
||||||
|
? sprop->getterValue()
|
||||||
|
: JSVAL_VOID, cx)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (desc.hasSet &&
|
||||||
|
!js_SameValue(desc.setterValue(),
|
||||||
|
(sprop->attrs & JSPROP_SETTER)
|
||||||
|
? sprop->setterValue()
|
||||||
|
: JSVAL_VOID, cx)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/*
|
||||||
|
* Determine the current value of the property once, if the current
|
||||||
|
* value might actually need to be used or preserved later. NB: we
|
||||||
|
* guard on whether the current property is a data descriptor to
|
||||||
|
* avoid calling a getter; we won't need the value if it's not a
|
||||||
|
* data descriptor.
|
||||||
|
*/
|
||||||
|
if (sprop->isDataDescriptor()) {
|
||||||
|
/*
|
||||||
|
* Non-standard: if the property is non-configurable and is
|
||||||
|
* represented by a native getter or setter, don't permit
|
||||||
|
* redefinition. We expose properties with native getter/setter
|
||||||
|
* as though they were data properties, for the most part, but
|
||||||
|
* in this particular case we must worry about integrity
|
||||||
|
* concerns for JSAPI users who expected that
|
||||||
|
* permanent+getter/setter means precisely controlled behavior.
|
||||||
|
* If we permitted such redefinitions, such a property could be
|
||||||
|
* "fixed" to some specific previous value, no longer varying
|
||||||
|
* according to the intent of the native getter/setter for the
|
||||||
|
* property.
|
||||||
|
*
|
||||||
|
* Other engines expose properties of this nature using ECMA
|
||||||
|
* getter/setter pairs, but we can't because we use them even
|
||||||
|
* for properties which ECMA specifies as being true data
|
||||||
|
* descriptors ([].length, Function.length, /regex/.lastIndex,
|
||||||
|
* &c.). Longer-term perhaps we should convert such properties
|
||||||
|
* to use data descriptors (at which point representing a
|
||||||
|
* descriptor with native getter/setter as an accessor
|
||||||
|
* descriptor would be fine) and take a small memory hit, but
|
||||||
|
* for now we'll simply forbid their redefinition.
|
||||||
|
*/
|
||||||
|
if (!sprop->configurable() &&
|
||||||
|
(!SPROP_HAS_STUB_GETTER(sprop) || !SPROP_HAS_STUB_SETTER(sprop))) {
|
||||||
|
return Reject(cx, obj2, current, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP,
|
||||||
|
throwError, desc.id, rval);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!js_NativeGet(cx, obj, obj2, sprop, JSGET_NO_METHOD_BARRIER, &v)) {
|
||||||
|
/* current was dropped when the failure occurred. */
|
||||||
|
return JS_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (desc.isDataDescriptor()) {
|
||||||
|
if (!sprop->isDataDescriptor())
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (desc.hasValue && !js_SameValue(desc.value, v, cx))
|
||||||
|
break;
|
||||||
|
if (desc.hasWritable && desc.writable() != sprop->writable())
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
/* The only fields in desc will be handled below. */
|
||||||
|
JS_ASSERT(desc.isGenericDescriptor());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (desc.hasConfigurable && desc.configurable() != sprop->configurable())
|
||||||
|
break;
|
||||||
|
if (desc.hasEnumerable && desc.enumerable() != sprop->enumerable())
|
||||||
|
break;
|
||||||
|
|
||||||
|
/* The conditions imposed by step 5 or step 6 apply. */
|
||||||
|
obj2->dropProperty(cx, current);
|
||||||
|
*rval = true;
|
||||||
|
return JS_TRUE;
|
||||||
|
} while (0);
|
||||||
|
|
||||||
|
/* 8.12.9 step 7. */
|
||||||
|
if (!sprop->configurable()) {
|
||||||
|
/*
|
||||||
|
* Since [[Configurable]] defaults to false, we don't need to check
|
||||||
|
* whether it was specified. We can't do likewise for [[Enumerable]]
|
||||||
|
* because its putative value is used in a comparison -- a comparison
|
||||||
|
* whose result must always be false per spec if the [[Enumerable]]
|
||||||
|
* field is not present. Perfectly pellucid logic, eh?
|
||||||
|
*/
|
||||||
|
JS_ASSERT_IF(!desc.hasConfigurable, !desc.configurable());
|
||||||
|
if (desc.configurable() ||
|
||||||
|
(desc.hasEnumerable && desc.enumerable() != sprop->enumerable())) {
|
||||||
|
return Reject(cx, obj2, current, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP, throwError,
|
||||||
|
desc.id, rval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (desc.isGenericDescriptor()) {
|
||||||
|
/* 8.12.9 step 8, no validation required */
|
||||||
|
} else if (desc.isDataDescriptor() != sprop->isDataDescriptor()) {
|
||||||
|
/* 8.12.9 step 9. */
|
||||||
|
if (!sprop->configurable()) {
|
||||||
|
return Reject(cx, obj2, current, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP,
|
||||||
|
throwError, desc.id, rval);
|
||||||
|
}
|
||||||
|
} else if (desc.isDataDescriptor() && sprop->isDataDescriptor()) {
|
||||||
|
/* 8.12.9 step 10. */
|
||||||
|
if (!sprop->configurable() && !sprop->writable()) {
|
||||||
|
if ((desc.hasWritable && desc.writable()) ||
|
||||||
|
(desc.hasValue && !js_SameValue(desc.value, v, cx))) {
|
||||||
|
return Reject(cx, obj2, current, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP,
|
||||||
|
throwError, desc.id, rval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* 8.12.9 step 11. */
|
||||||
|
JS_ASSERT(desc.isAccessorDescriptor() && sprop->isAccessorDescriptor());
|
||||||
|
if (!sprop->configurable()) {
|
||||||
|
if ((desc.hasSet &&
|
||||||
|
!js_SameValue(desc.setterValue(),
|
||||||
|
(sprop->attrs & JSPROP_SETTER) ? sprop->setterValue() : JSVAL_VOID,
|
||||||
|
cx)) ||
|
||||||
|
(desc.hasGet &&
|
||||||
|
!js_SameValue(desc.getterValue(),
|
||||||
|
(sprop->attrs & JSPROP_GETTER) ? sprop->getterValue() : JSVAL_VOID,
|
||||||
|
cx)))
|
||||||
|
{
|
||||||
|
return Reject(cx, obj2, current, JSMSG_CANT_REDEFINE_UNCONFIGURABLE_PROP,
|
||||||
|
throwError, desc.id, rval);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 8.12.9 step 12. */
|
||||||
|
uintN attrs;
|
||||||
|
JSPropertyOp getter, setter;
|
||||||
|
if (desc.isGenericDescriptor()) {
|
||||||
|
uintN changed = 0;
|
||||||
|
if (desc.hasConfigurable)
|
||||||
|
changed |= JSPROP_PERMANENT;
|
||||||
|
if (desc.hasEnumerable)
|
||||||
|
changed |= JSPROP_ENUMERATE;
|
||||||
|
|
||||||
|
attrs = (sprop->attrs & ~changed) | (desc.attrs & changed);
|
||||||
|
getter = sprop->getter;
|
||||||
|
setter = sprop->setter;
|
||||||
|
} else if (desc.isDataDescriptor()) {
|
||||||
|
uintN unchanged = 0;
|
||||||
|
if (!desc.hasConfigurable)
|
||||||
|
unchanged |= JSPROP_PERMANENT;
|
||||||
|
if (!desc.hasEnumerable)
|
||||||
|
unchanged |= JSPROP_ENUMERATE;
|
||||||
|
if (!desc.hasWritable)
|
||||||
|
unchanged |= JSPROP_READONLY;
|
||||||
|
|
||||||
|
if (desc.hasValue)
|
||||||
|
v = desc.value;
|
||||||
|
attrs = (desc.attrs & ~unchanged) | (sprop->attrs & unchanged);
|
||||||
|
getter = setter = JS_PropertyStub;
|
||||||
|
} else {
|
||||||
|
JS_ASSERT(desc.isAccessorDescriptor());
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Getters and setters are just like watchpoints from an access
|
||||||
|
* control point of view.
|
||||||
|
*/
|
||||||
|
jsval dummy;
|
||||||
|
JS_ASSERT(obj2->map->ops->checkAccess == js_CheckAccess);
|
||||||
|
if (!js_CheckAccess(cx, obj2, desc.id, JSACC_WATCH, &dummy, &attrs)) {
|
||||||
|
obj2->dropProperty(cx, current);
|
||||||
|
return JS_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 8.12.9 step 12. */
|
||||||
|
uintN changed = 0;
|
||||||
|
if (desc.hasConfigurable)
|
||||||
|
changed |= JSPROP_PERMANENT;
|
||||||
|
if (desc.hasEnumerable)
|
||||||
|
changed |= JSPROP_ENUMERATE;
|
||||||
|
if (desc.hasGet)
|
||||||
|
changed |= JSPROP_GETTER | JSPROP_SHARED;
|
||||||
|
if (desc.hasSet)
|
||||||
|
changed |= JSPROP_SETTER | JSPROP_SHARED;
|
||||||
|
|
||||||
|
attrs = (desc.attrs & changed) | (sprop->attrs & ~changed);
|
||||||
|
if (desc.hasGet)
|
||||||
|
getter = desc.getterObject() ? desc.getter() : JS_PropertyStub;
|
||||||
|
else
|
||||||
|
getter = sprop->getter;
|
||||||
|
if (desc.hasSet)
|
||||||
|
setter = desc.setterObject() ? desc.setter() : JS_PropertyStub;
|
||||||
|
else
|
||||||
|
setter = sprop->setter;
|
||||||
|
}
|
||||||
|
|
||||||
|
*rval = true;
|
||||||
|
obj2->dropProperty(cx, current);
|
||||||
|
return js_DefineProperty(cx, obj, desc.id, v, getter, setter, attrs);
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSBool
|
||||||
|
DefinePropertyArray(JSContext *cx, JSObject *obj, const PropertyDescriptor &desc,
|
||||||
|
bool throwError, bool *rval)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* We probably should optimize dense array property definitions where
|
||||||
|
* the descriptor describes a traditional array property (enumerable,
|
||||||
|
* configurable, writable, numeric index or length without altering its
|
||||||
|
* attributes). Such definitions are probably unlikely, so we don't bother
|
||||||
|
* for now.
|
||||||
|
*/
|
||||||
|
if (OBJ_IS_DENSE_ARRAY(cx, obj) && !js_MakeArraySlow(cx, obj))
|
||||||
|
return JS_FALSE;
|
||||||
|
|
||||||
|
jsuint oldLen = obj->fslots[JSSLOT_ARRAY_LENGTH];
|
||||||
|
|
||||||
|
if (desc.id == ATOM_TO_JSID(cx->runtime->atomState.lengthAtom)) {
|
||||||
|
/*
|
||||||
|
* Our optimization of storage of the length property of arrays makes
|
||||||
|
* it very difficult to properly implement defining the property. For
|
||||||
|
* now simply throw an exception (NB: not merely Reject) on any attempt
|
||||||
|
* 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);
|
||||||
|
return JS_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32 index;
|
||||||
|
if (js_IdIsIndex(desc.id, &index)) {
|
||||||
|
/*
|
||||||
|
// Disabled until we support defining "length":
|
||||||
|
if (index >= oldLen && lengthPropertyNotWritable())
|
||||||
|
return ThrowTypeError(cx, JSMSG_CANT_APPEND_PROPERTIES_TO_UNWRITABLE_LENGTH_ARRAY);
|
||||||
|
*/
|
||||||
|
if (!DefinePropertyObject(cx, obj, desc, false, rval))
|
||||||
|
return JS_FALSE;
|
||||||
|
if (!*rval)
|
||||||
|
return Reject(cx, JSMSG_CANT_DEFINE_ARRAY_INDEX, throwError, rval);
|
||||||
|
|
||||||
|
if (index >= oldLen) {
|
||||||
|
JS_ASSERT(index != UINT32_MAX);
|
||||||
|
obj->fslots[JSSLOT_ARRAY_LENGTH] = index + 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
*rval = true;
|
||||||
|
return JS_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return DefinePropertyObject(cx, obj, desc, throwError, rval);
|
||||||
|
}
|
||||||
|
|
||||||
|
static JSBool
|
||||||
|
DefineProperty(JSContext *cx, JSObject *obj, const PropertyDescriptor &desc, bool throwError,
|
||||||
|
bool *rval)
|
||||||
|
{
|
||||||
|
if (OBJ_IS_ARRAY(cx, obj))
|
||||||
|
return DefinePropertyArray(cx, obj, desc, throwError, rval);
|
||||||
|
|
||||||
|
if (!OBJ_IS_NATIVE(obj))
|
||||||
|
return Reject(cx, JSMSG_OBJECT_NOT_EXTENSIBLE, throwError, rval);
|
||||||
|
|
||||||
|
return DefinePropertyObject(cx, obj, desc, throwError, rval);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ES5 15.2.3.6: Object.defineProperty(O, P, Attributes) */
|
||||||
|
static JSBool
|
||||||
|
obj_defineProperty(JSContext* cx, uintN argc, jsval* vp)
|
||||||
|
{
|
||||||
|
/* 15.2.3.6 steps 1 and 5. */
|
||||||
|
jsval v = argc == 0 ? JSVAL_VOID : vp[2];
|
||||||
|
if (JSVAL_IS_PRIMITIVE(v)) {
|
||||||
|
JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
|
||||||
|
JSMSG_NOT_NONNULL_OBJECT,
|
||||||
|
js_getter_str);
|
||||||
|
return JS_FALSE;
|
||||||
|
}
|
||||||
|
*vp = vp[2];
|
||||||
|
JSObject* obj = JSVAL_TO_OBJECT(*vp);
|
||||||
|
|
||||||
|
/* 15.2.3.6 step 2. */
|
||||||
|
JSAutoTempIdRooter nameidr(cx);
|
||||||
|
if (!JS_ValueToId(cx, argc >= 2 ? vp[3] : JSVAL_VOID, nameidr.addr()))
|
||||||
|
return JS_FALSE;
|
||||||
|
|
||||||
|
/* 15.2.3.6 step 3. */
|
||||||
|
AutoDescriptorArray descs(cx);
|
||||||
|
PropertyDescriptor desc;
|
||||||
|
if (!desc.initialize(cx, nameidr.id(), argc >= 3 ? vp[4] : JSVAL_VOID) || !descs.append(desc))
|
||||||
|
return JS_FALSE;
|
||||||
|
|
||||||
|
/* 15.2.3.6 step 4 */
|
||||||
|
bool dummy;
|
||||||
|
return DefineProperty(cx, obj, desc, true, &dummy);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ES5 15.2.3.7: Object.defineProperties(O, Properties) */
|
||||||
|
static JSBool
|
||||||
|
obj_defineProperties(JSContext* cx, uintN argc, jsval* vp)
|
||||||
|
{
|
||||||
|
/* 15.2.3.6 steps 1 and 5. */
|
||||||
|
jsval v = argc > 0 ? vp[2] : JSVAL_VOID;
|
||||||
|
if (JSVAL_IS_PRIMITIVE(v)) {
|
||||||
|
js_ReportValueError(cx, JSMSG_NOT_NONNULL_OBJECT, -1, v, NULL);
|
||||||
|
return JS_FALSE;
|
||||||
|
}
|
||||||
|
*vp = vp[2];
|
||||||
|
|
||||||
|
v = argc > 1 ? vp[3] : JSVAL_VOID;
|
||||||
|
if (JSVAL_IS_PRIMITIVE(v)) {
|
||||||
|
js_ReportValueError(cx, JSMSG_NOT_NONNULL_OBJECT, -1, v, NULL);
|
||||||
|
return JS_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSObject* props = JSVAL_TO_OBJECT(v);
|
||||||
|
JSAutoIdArray ida(cx, JS_Enumerate(cx, props));
|
||||||
|
if (!ida)
|
||||||
|
return JS_FALSE;
|
||||||
|
|
||||||
|
AutoDescriptorArray descs(cx);
|
||||||
|
size_t len = ida.length();
|
||||||
|
for (size_t i = 0; i < len; i++) {
|
||||||
|
PropertyDescriptor desc;
|
||||||
|
jsid id = ida[i];
|
||||||
|
if (!JS_GetPropertyById(cx, props, id, &vp[1]) || !desc.initialize(cx, id, vp[1]) ||
|
||||||
|
!descs.append(desc)) {
|
||||||
|
return JS_FALSE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
JSObject *obj = JSVAL_TO_OBJECT(*vp);
|
||||||
|
bool dummy;
|
||||||
|
for (size_t i = 0; i < len; i++) {
|
||||||
|
if (!DefineProperty(cx, obj, descs[i], true, &dummy))
|
||||||
|
return JS_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return JS_TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#if JS_HAS_OBJ_WATCHPOINT
|
#if JS_HAS_OBJ_WATCHPOINT
|
||||||
const char js_watch_str[] = "watch";
|
const char js_watch_str[] = "watch";
|
||||||
@ -2192,6 +2803,8 @@ static JSFunctionSpec object_static_methods[] = {
|
|||||||
JS_FN("getPrototypeOf", obj_getPrototypeOf, 1,0),
|
JS_FN("getPrototypeOf", obj_getPrototypeOf, 1,0),
|
||||||
JS_FN("getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor,2,0),
|
JS_FN("getOwnPropertyDescriptor", obj_getOwnPropertyDescriptor,2,0),
|
||||||
JS_FN("keys", obj_keys, 1,0),
|
JS_FN("keys", obj_keys, 1,0),
|
||||||
|
JS_FN("defineProperty", obj_defineProperty, 3,0),
|
||||||
|
JS_FN("defineProperties", obj_defineProperties, 2,0),
|
||||||
JS_FS_END
|
JS_FS_END
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -49,8 +49,85 @@
|
|||||||
* is reference counted and the slot vector is malloc'ed.
|
* is reference counted and the slot vector is malloc'ed.
|
||||||
*/
|
*/
|
||||||
#include "jshash.h" /* Added by JSIFY */
|
#include "jshash.h" /* Added by JSIFY */
|
||||||
#include "jsprvtd.h"
|
|
||||||
#include "jspubtd.h"
|
#include "jspubtd.h"
|
||||||
|
#include "jsprvtd.h"
|
||||||
|
#include "jsstdint.h"
|
||||||
|
|
||||||
|
/*
|
||||||
|
* A representation of ECMA-262 ed. 5's internal property descriptor data
|
||||||
|
* structure.
|
||||||
|
*/
|
||||||
|
struct PropertyDescriptor {
|
||||||
|
PropertyDescriptor();
|
||||||
|
|
||||||
|
/* 8.10.5 ToPropertyDescriptor(Obj) */
|
||||||
|
bool initialize(JSContext* cx, jsid id, jsval v);
|
||||||
|
|
||||||
|
/* 8.10.1 IsAccessorDescriptor(desc) */
|
||||||
|
bool isAccessorDescriptor() const {
|
||||||
|
return hasGet || hasSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 8.10.2 IsDataDescriptor(desc) */
|
||||||
|
bool isDataDescriptor() const {
|
||||||
|
return hasValue || hasWritable;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 8.10.3 IsGenericDescriptor(desc) */
|
||||||
|
bool isGenericDescriptor() const {
|
||||||
|
return !isAccessorDescriptor() && !isDataDescriptor();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool configurable() const {
|
||||||
|
return (attrs & JSPROP_PERMANENT) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool enumerable() const {
|
||||||
|
return (attrs & JSPROP_ENUMERATE) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool writable() const {
|
||||||
|
return (attrs & JSPROP_READONLY) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSObject* getterObject() const {
|
||||||
|
return get != JSVAL_VOID ? JSVAL_TO_OBJECT(get) : NULL;
|
||||||
|
}
|
||||||
|
JSObject* setterObject() const {
|
||||||
|
return set != JSVAL_VOID ? JSVAL_TO_OBJECT(set) : NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
jsval getterValue() const {
|
||||||
|
return get;
|
||||||
|
}
|
||||||
|
jsval setterValue() const {
|
||||||
|
return set;
|
||||||
|
}
|
||||||
|
|
||||||
|
JSPropertyOp getter() const {
|
||||||
|
return js_CastAsPropertyOp(getterObject());
|
||||||
|
}
|
||||||
|
JSPropertyOp setter() const {
|
||||||
|
return js_CastAsPropertyOp(setterObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void traceDescriptorArray(JSTracer* trc, JSObject* obj);
|
||||||
|
static void finalizeDescriptorArray(JSContext* cx, JSObject* obj);
|
||||||
|
|
||||||
|
jsid id;
|
||||||
|
jsval value, get, set;
|
||||||
|
|
||||||
|
/* Property descriptor boolean fields. */
|
||||||
|
uint8_t attrs;
|
||||||
|
|
||||||
|
/* Bits indicating which values are set. */
|
||||||
|
bool hasGet : 1;
|
||||||
|
bool hasSet : 1;
|
||||||
|
bool hasValue : 1;
|
||||||
|
bool hasWritable : 1;
|
||||||
|
bool hasEnumerable : 1;
|
||||||
|
bool hasConfigurable : 1;
|
||||||
|
};
|
||||||
|
|
||||||
JS_BEGIN_EXTERN_C
|
JS_BEGIN_EXTERN_C
|
||||||
|
|
||||||
@ -581,7 +658,7 @@ js_HasOwnPropertyHelper(JSContext *cx, JSLookupPropOp lookup, uintN argc,
|
|||||||
|
|
||||||
extern JSBool
|
extern JSBool
|
||||||
js_HasOwnProperty(JSContext *cx, JSLookupPropOp lookup, JSObject *obj, jsid id,
|
js_HasOwnProperty(JSContext *cx, JSLookupPropOp lookup, JSObject *obj, jsid id,
|
||||||
JSBool *foundp);
|
JSObject **objp, JSProperty **propp);
|
||||||
|
|
||||||
extern JSBool
|
extern JSBool
|
||||||
js_PropertyIsEnumerable(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
|
js_PropertyIsEnumerable(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
|
||||||
|
@ -312,15 +312,18 @@ JO(JSContext *cx, jsval *vp, StringifyContext *scx)
|
|||||||
// Don't include prototype properties, since this operation is
|
// Don't include prototype properties, since this operation is
|
||||||
// supposed to be implemented as if by ES3.1 Object.keys()
|
// supposed to be implemented as if by ES3.1 Object.keys()
|
||||||
jsid id;
|
jsid id;
|
||||||
JSBool found = JS_FALSE;
|
JSObject *obj2;
|
||||||
|
JSProperty *prop;
|
||||||
if (!js_ValueToStringId(cx, STRING_TO_JSVAL(ks), &id) ||
|
if (!js_ValueToStringId(cx, STRING_TO_JSVAL(ks), &id) ||
|
||||||
!js_HasOwnProperty(cx, obj->map->ops->lookupProperty, obj, id, &found)) {
|
!js_HasOwnProperty(cx, obj->map->ops->lookupProperty, obj, id, &obj2, &prop)) {
|
||||||
goto error_break;
|
goto error_break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!found)
|
if (!prop)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
obj2->dropProperty(cx, prop);
|
||||||
|
|
||||||
if (!JS_GetPropertyById(cx, obj, id, &outputValue))
|
if (!JS_GetPropertyById(cx, obj, id, &outputValue))
|
||||||
goto error_break;
|
goto error_break;
|
||||||
|
|
||||||
|
@ -159,6 +159,12 @@ class Vector;
|
|||||||
/* Common instantiations. */
|
/* Common instantiations. */
|
||||||
typedef js::Vector<jschar, 32> JSCharBuffer;
|
typedef js::Vector<jschar, 32> JSCharBuffer;
|
||||||
|
|
||||||
|
static inline JSPropertyOp
|
||||||
|
js_CastAsPropertyOp(JSObject *object)
|
||||||
|
{
|
||||||
|
return JS_DATA_TO_FUNC_PTR(JSPropertyOp, object);
|
||||||
|
}
|
||||||
|
|
||||||
} /* export "C++" */
|
} /* export "C++" */
|
||||||
#endif /* __cplusplus */
|
#endif /* __cplusplus */
|
||||||
|
|
||||||
|
@ -1228,7 +1228,6 @@ JSScope::addProperty(JSContext *cx, jsid id,
|
|||||||
JS_ASSERT(!JSVAL_IS_NULL(id));
|
JS_ASSERT(!JSVAL_IS_NULL(id));
|
||||||
JS_ASSERT_IF(attrs & JSPROP_GETTER, getter);
|
JS_ASSERT_IF(attrs & JSPROP_GETTER, getter);
|
||||||
JS_ASSERT_IF(attrs & JSPROP_SETTER, setter);
|
JS_ASSERT_IF(attrs & JSPROP_SETTER, setter);
|
||||||
|
|
||||||
JS_ASSERT_IF(!cx->runtime->gcRegenShapes,
|
JS_ASSERT_IF(!cx->runtime->gcRegenShapes,
|
||||||
hasRegenFlag(cx->runtime->gcRegenShapesScopeFlag));
|
hasRegenFlag(cx->runtime->gcRegenShapesScopeFlag));
|
||||||
|
|
||||||
@ -1810,11 +1809,11 @@ JSScopeProperty::trace(JSTracer *trc)
|
|||||||
|
|
||||||
#if JS_HAS_GETTER_SETTER
|
#if JS_HAS_GETTER_SETTER
|
||||||
if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
|
if (attrs & (JSPROP_GETTER | JSPROP_SETTER)) {
|
||||||
if (attrs & JSPROP_GETTER) {
|
if ((attrs & JSPROP_GETTER) && getter) {
|
||||||
JS_SET_TRACING_DETAILS(trc, PrintPropertyGetterOrSetter, this, 0);
|
JS_SET_TRACING_DETAILS(trc, PrintPropertyGetterOrSetter, this, 0);
|
||||||
JS_CallTracer(trc, getterObject(), JSTRACE_OBJECT);
|
JS_CallTracer(trc, getterObject(), JSTRACE_OBJECT);
|
||||||
}
|
}
|
||||||
if (attrs & JSPROP_SETTER) {
|
if ((attrs & JSPROP_SETTER) && setter) {
|
||||||
JS_SET_TRACING_DETAILS(trc, PrintPropertyGetterOrSetter, this, 1);
|
JS_SET_TRACING_DETAILS(trc, PrintPropertyGetterOrSetter, this, 1);
|
||||||
JS_CallTracer(trc, setterObject(), JSTRACE_OBJECT);
|
JS_CallTracer(trc, setterObject(), JSTRACE_OBJECT);
|
||||||
}
|
}
|
||||||
|
@ -507,12 +507,6 @@ js_CastAsObjectJSVal(JSPropertyOp op)
|
|||||||
return OBJECT_TO_JSVAL(JS_FUNC_TO_DATA_PTR(JSObject *, op));
|
return OBJECT_TO_JSVAL(JS_FUNC_TO_DATA_PTR(JSObject *, op));
|
||||||
}
|
}
|
||||||
|
|
||||||
inline JSPropertyOp
|
|
||||||
js_CastAsPropertyOp(JSObject *object)
|
|
||||||
{
|
|
||||||
return JS_DATA_TO_FUNC_PTR(JSPropertyOp, object);
|
|
||||||
}
|
|
||||||
|
|
||||||
struct JSScopeProperty {
|
struct JSScopeProperty {
|
||||||
jsid id; /* int-tagged jsval/untagged JSAtom* */
|
jsid id; /* int-tagged jsval/untagged JSAtom* */
|
||||||
JSPropertyOp getter; /* getter and setter hooks or objects */
|
JSPropertyOp getter; /* getter and setter hooks or objects */
|
||||||
@ -553,34 +547,43 @@ struct JSScopeProperty {
|
|||||||
return js_CastAsObjectJSVal(getter);
|
return js_CastAsObjectJSVal(getter);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasGetterObject() const {
|
|
||||||
return attrs & JSPROP_GETTER;
|
|
||||||
}
|
|
||||||
JSObject *getterObject() const {
|
JSObject *getterObject() const {
|
||||||
JS_ASSERT(hasGetterObject());
|
JS_ASSERT(attrs & JSPROP_GETTER);
|
||||||
return js_CastAsObject(getter);
|
return js_CastAsObject(getter);
|
||||||
}
|
}
|
||||||
jsval getterValue() const {
|
jsval getterValue() const {
|
||||||
JS_ASSERT(hasGetterObject());
|
JS_ASSERT(attrs & JSPROP_GETTER);
|
||||||
return js_CastAsObjectJSVal(getter);
|
jsval getterVal = getter ? js_CastAsObjectJSVal(getter) : JSVAL_VOID;
|
||||||
|
JS_ASSERT_IF(getter, VALUE_IS_FUNCTION(BOGUS_CX, getterVal));
|
||||||
|
return getterVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hasSetterObject() const {
|
|
||||||
return attrs & JSPROP_SETTER;
|
|
||||||
}
|
|
||||||
JSObject *setterObject() const {
|
JSObject *setterObject() const {
|
||||||
JS_ASSERT(hasSetterObject());
|
JS_ASSERT((attrs & JSPROP_SETTER) && setter);
|
||||||
return js_CastAsObject(setter);
|
return js_CastAsObject(setter);
|
||||||
}
|
}
|
||||||
jsval setterValue() const {
|
jsval setterValue() const {
|
||||||
JS_ASSERT(hasSetterObject());
|
JS_ASSERT(attrs & JSPROP_SETTER);
|
||||||
return js_CastAsObjectJSVal(setter);
|
jsval setterVal = setter ? js_CastAsObjectJSVal(setter) : JSVAL_VOID;
|
||||||
|
JS_ASSERT_IF(setter, VALUE_IS_FUNCTION(BOGUS_CX, setterVal));
|
||||||
|
return setterVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool get(JSContext* cx, JSObject* obj, JSObject *pobj, jsval* vp);
|
bool get(JSContext* cx, JSObject* obj, JSObject *pobj, jsval* vp);
|
||||||
bool set(JSContext* cx, JSObject* obj, jsval* vp);
|
bool set(JSContext* cx, JSObject* obj, jsval* vp);
|
||||||
|
|
||||||
void trace(JSTracer *trc);
|
void trace(JSTracer *trc);
|
||||||
|
|
||||||
|
bool configurable() { return (attrs & JSPROP_PERMANENT) == 0; }
|
||||||
|
bool enumerable() { return (attrs & JSPROP_ENUMERATE) != 0; }
|
||||||
|
bool writable() { return (attrs & JSPROP_READONLY) == 0; }
|
||||||
|
|
||||||
|
bool isDataDescriptor() {
|
||||||
|
return (attrs & (JSPROP_SETTER | JSPROP_GETTER)) == 0;
|
||||||
|
}
|
||||||
|
bool isAccessorDescriptor() {
|
||||||
|
return (attrs & (JSPROP_SETTER | JSPROP_GETTER)) != 0;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/* JSScopeProperty pointer tag bit indicating a collision. */
|
/* JSScopeProperty pointer tag bit indicating a collision. */
|
||||||
|
35
js/src/tests/ecma_5/Object/15.2.3.6-function-length.js
Normal file
35
js/src/tests/ecma_5/Object/15.2.3.6-function-length.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Any copyright is dedicated to the Public Domain.
|
||||||
|
// http://creativecommons.org/licenses/publicdomain/
|
||||||
|
|
||||||
|
var gTestfile = '15.2.3.6-function-length.js';
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
var BUGNUMBER = 430133;
|
||||||
|
var summary = 'ES5 Object.defineProperty(O, P, Attributes): Function.length';
|
||||||
|
|
||||||
|
print(BUGNUMBER + ": " + summary);
|
||||||
|
|
||||||
|
load("ecma_5/Object/defineProperty-setup.js");
|
||||||
|
|
||||||
|
/**************
|
||||||
|
* BEGIN TEST *
|
||||||
|
**************/
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
new TestRunner().runFunctionLengthTests();
|
||||||
|
}
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
|
throw "Error thrown during testing: " + e +
|
||||||
|
" at line " + e.lineNumber + "\n" +
|
||||||
|
(e.stack
|
||||||
|
? "Stack: " + e.stack.split("\n").slice(2).join("\n") + "\n"
|
||||||
|
: "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
if (typeof reportCompare === "function")
|
||||||
|
reportCompare(true, true);
|
||||||
|
|
||||||
|
print("Tests complete!");
|
65
js/src/tests/ecma_5/Object/15.2.3.6-miscellaneous.js
Normal file
65
js/src/tests/ecma_5/Object/15.2.3.6-miscellaneous.js
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
// Any copyright is dedicated to the Public Domain.
|
||||||
|
// http://creativecommons.org/licenses/publicdomain/
|
||||||
|
|
||||||
|
var gTestfile = '15.2.3.6-miscellaneous.js';
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
var BUGNUMBER = 430133;
|
||||||
|
var summary = 'ES5 Object.defineProperty(O, P, Attributes)';
|
||||||
|
|
||||||
|
print(BUGNUMBER + ": " + summary);
|
||||||
|
|
||||||
|
/**************
|
||||||
|
* BEGIN TEST *
|
||||||
|
**************/
|
||||||
|
|
||||||
|
var o = [];
|
||||||
|
Object.defineProperty(o, 0, { value: 17 });
|
||||||
|
var desc = Object.getOwnPropertyDescriptor(o, 0);
|
||||||
|
assertEq(desc !== undefined, true);
|
||||||
|
assertEq(desc.value, 17);
|
||||||
|
assertEq(desc.enumerable, false);
|
||||||
|
assertEq(desc.configurable, false);
|
||||||
|
assertEq(desc.writable, false);
|
||||||
|
|
||||||
|
desc = Object.getOwnPropertyDescriptor(o, "length");
|
||||||
|
assertEq(desc !== undefined, true);
|
||||||
|
assertEq(desc.enumerable, false);
|
||||||
|
assertEq(desc.configurable, false);
|
||||||
|
assertEq(desc.writable, true);
|
||||||
|
assertEq(desc.value, 1);
|
||||||
|
assertEq(o.length, 1);
|
||||||
|
|
||||||
|
Object.defineProperty(o, "foobar",
|
||||||
|
{ value: 42, enumerable: false, configurable: true });
|
||||||
|
assertEq(o.foobar, 42);
|
||||||
|
desc = Object.getOwnPropertyDescriptor(o, "foobar");
|
||||||
|
assertEq(desc !== undefined, true);
|
||||||
|
assertEq(desc.value, 42);
|
||||||
|
assertEq(desc.configurable, true);
|
||||||
|
assertEq(desc.enumerable, false);
|
||||||
|
assertEq(desc.writable, false);
|
||||||
|
|
||||||
|
var called = false;
|
||||||
|
o = { set x(a) { called = true; } };
|
||||||
|
Object.defineProperty(o, "x", { get: function() { return "get"; } });
|
||||||
|
desc = Object.getOwnPropertyDescriptor(o, "x");
|
||||||
|
assertEq("set" in desc, true);
|
||||||
|
assertEq("get" in desc, true);
|
||||||
|
assertEq(called, false);
|
||||||
|
o.x = 13;
|
||||||
|
assertEq(called, true);
|
||||||
|
|
||||||
|
var toSource = Object.prototype.toSource || function() { };
|
||||||
|
toSource.call(o); // a test for this not crashing
|
||||||
|
|
||||||
|
/*
|
||||||
|
* XXX need tests for Object.defineProperty(array, "length", { ... }) when we
|
||||||
|
* support it!
|
||||||
|
*/
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
if (typeof reportCompare === "function")
|
||||||
|
reportCompare(true, true);
|
||||||
|
|
||||||
|
print("All tests passed!");
|
35
js/src/tests/ecma_5/Object/15.2.3.6-new-definition.js
Normal file
35
js/src/tests/ecma_5/Object/15.2.3.6-new-definition.js
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
// Any copyright is dedicated to the Public Domain.
|
||||||
|
// http://creativecommons.org/licenses/publicdomain/
|
||||||
|
|
||||||
|
var gTestfile = '15.2.3.6-new-definition.js';
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
var BUGNUMBER = 430133;
|
||||||
|
var summary = 'ES5 Object.defineProperty(O, P, Attributes): new definition';
|
||||||
|
|
||||||
|
print(BUGNUMBER + ": " + summary);
|
||||||
|
|
||||||
|
load("ecma_5/Object/defineProperty-setup.js");
|
||||||
|
|
||||||
|
/**************
|
||||||
|
* BEGIN TEST *
|
||||||
|
**************/
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
new TestRunner().runNotPresentTests();
|
||||||
|
}
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
|
throw "Error thrown during testing: " + e +
|
||||||
|
" at line " + e.lineNumber + "\n" +
|
||||||
|
(e.stack
|
||||||
|
? "Stack: " + e.stack.split("\n").slice(2).join("\n") + "\n"
|
||||||
|
: "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
if (typeof reportCompare === "function")
|
||||||
|
reportCompare(true, true);
|
||||||
|
|
||||||
|
print("Tests complete!");
|
39
js/src/tests/ecma_5/Object/15.2.3.6-redefinition-1-of-4.js
Normal file
39
js/src/tests/ecma_5/Object/15.2.3.6-redefinition-1-of-4.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// Any copyright is dedicated to the Public Domain.
|
||||||
|
// http://creativecommons.org/licenses/publicdomain/
|
||||||
|
|
||||||
|
var PART = 1, PARTS = 4;
|
||||||
|
|
||||||
|
var gTestfile = '15.2.3.6-redefinition-1-of-4.js';
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
var BUGNUMBER = 430133;
|
||||||
|
var summary =
|
||||||
|
'ES5 Object.defineProperty(O, P, Attributes): redefinition ' +
|
||||||
|
PART + ' of ' + PARTS;
|
||||||
|
|
||||||
|
print(BUGNUMBER + ": " + summary);
|
||||||
|
|
||||||
|
load("ecma_5/Object/defineProperty-setup.js");
|
||||||
|
|
||||||
|
/**************
|
||||||
|
* BEGIN TEST *
|
||||||
|
**************/
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
new TestRunner().runPropertyPresentTestsFraction(PART, PARTS);
|
||||||
|
}
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
|
throw "Error thrown during testing: " + e +
|
||||||
|
" at line " + e.lineNumber + "\n" +
|
||||||
|
(e.stack
|
||||||
|
? "Stack: " + e.stack.split("\n").slice(2).join("\n") + "\n"
|
||||||
|
: "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
if (typeof reportCompare === "function")
|
||||||
|
reportCompare(true, true);
|
||||||
|
|
||||||
|
print("Tests complete!");
|
39
js/src/tests/ecma_5/Object/15.2.3.6-redefinition-2-of-4.js
Normal file
39
js/src/tests/ecma_5/Object/15.2.3.6-redefinition-2-of-4.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// Any copyright is dedicated to the Public Domain.
|
||||||
|
// http://creativecommons.org/licenses/publicdomain/
|
||||||
|
|
||||||
|
var PART = 2, PARTS = 4;
|
||||||
|
|
||||||
|
var gTestfile = '15.2.3.6-redefinition-2-of-4.js';
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
var BUGNUMBER = 430133;
|
||||||
|
var summary =
|
||||||
|
'ES5 Object.defineProperty(O, P, Attributes): redefinition ' +
|
||||||
|
PART + ' of ' + PARTS;
|
||||||
|
|
||||||
|
print(BUGNUMBER + ": " + summary);
|
||||||
|
|
||||||
|
load("ecma_5/Object/defineProperty-setup.js");
|
||||||
|
|
||||||
|
/**************
|
||||||
|
* BEGIN TEST *
|
||||||
|
**************/
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
new TestRunner().runPropertyPresentTestsFraction(PART, PARTS);
|
||||||
|
}
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
|
throw "Error thrown during testing: " + e +
|
||||||
|
" at line " + e.lineNumber + "\n" +
|
||||||
|
(e.stack
|
||||||
|
? "Stack: " + e.stack.split("\n").slice(2).join("\n") + "\n"
|
||||||
|
: "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
if (typeof reportCompare === "function")
|
||||||
|
reportCompare(true, true);
|
||||||
|
|
||||||
|
print("Tests complete!");
|
39
js/src/tests/ecma_5/Object/15.2.3.6-redefinition-3-of-4.js
Normal file
39
js/src/tests/ecma_5/Object/15.2.3.6-redefinition-3-of-4.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// Any copyright is dedicated to the Public Domain.
|
||||||
|
// http://creativecommons.org/licenses/publicdomain/
|
||||||
|
|
||||||
|
var PART = 3, PARTS = 4;
|
||||||
|
|
||||||
|
var gTestfile = '15.2.3.6-redefinition-3-of-4.js';
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
var BUGNUMBER = 430133;
|
||||||
|
var summary =
|
||||||
|
'ES5 Object.defineProperty(O, P, Attributes): redefinition ' +
|
||||||
|
PART + ' of ' + PARTS;
|
||||||
|
|
||||||
|
print(BUGNUMBER + ": " + summary);
|
||||||
|
|
||||||
|
load("ecma_5/Object/defineProperty-setup.js");
|
||||||
|
|
||||||
|
/**************
|
||||||
|
* BEGIN TEST *
|
||||||
|
**************/
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
new TestRunner().runPropertyPresentTestsFraction(PART, PARTS);
|
||||||
|
}
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
|
throw "Error thrown during testing: " + e +
|
||||||
|
" at line " + e.lineNumber + "\n" +
|
||||||
|
(e.stack
|
||||||
|
? "Stack: " + e.stack.split("\n").slice(2).join("\n") + "\n"
|
||||||
|
: "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
if (typeof reportCompare === "function")
|
||||||
|
reportCompare(true, true);
|
||||||
|
|
||||||
|
print("Tests complete!");
|
39
js/src/tests/ecma_5/Object/15.2.3.6-redefinition-4-of-4.js
Normal file
39
js/src/tests/ecma_5/Object/15.2.3.6-redefinition-4-of-4.js
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
// Any copyright is dedicated to the Public Domain.
|
||||||
|
// http://creativecommons.org/licenses/publicdomain/
|
||||||
|
|
||||||
|
var PART = 4, PARTS = 4;
|
||||||
|
|
||||||
|
var gTestfile = '15.2.3.6-redefinition-4-of-4.js';
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
var BUGNUMBER = 430133;
|
||||||
|
var summary =
|
||||||
|
'ES5 Object.defineProperty(O, P, Attributes): redefinition ' +
|
||||||
|
PART + ' of ' + PARTS;
|
||||||
|
|
||||||
|
print(BUGNUMBER + ": " + summary);
|
||||||
|
|
||||||
|
load("ecma_5/Object/defineProperty-setup.js");
|
||||||
|
|
||||||
|
/**************
|
||||||
|
* BEGIN TEST *
|
||||||
|
**************/
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
new TestRunner().runPropertyPresentTestsFraction(PART, PARTS);
|
||||||
|
}
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
|
throw "Error thrown during testing: " + e +
|
||||||
|
" at line " + e.lineNumber + "\n" +
|
||||||
|
(e.stack
|
||||||
|
? "Stack: " + e.stack.split("\n").slice(2).join("\n") + "\n"
|
||||||
|
: "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
if (typeof reportCompare === "function")
|
||||||
|
reportCompare(true, true);
|
||||||
|
|
||||||
|
print("Tests complete!");
|
83
js/src/tests/ecma_5/Object/15.2.3.7-01.js
Normal file
83
js/src/tests/ecma_5/Object/15.2.3.7-01.js
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
/*
|
||||||
|
* Any copyright is dedicated to the Public Domain.
|
||||||
|
* http://creativecommons.org/licenses/publicdomain/
|
||||||
|
*/
|
||||||
|
|
||||||
|
var gTestfile = '15.2.3.7-01.js';
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
var BUGNUMBER = 430133;
|
||||||
|
var summary = 'ES5 Object.defineProperties(O, Properties)';
|
||||||
|
|
||||||
|
print(BUGNUMBER + ": " + summary);
|
||||||
|
|
||||||
|
/**************
|
||||||
|
* BEGIN TEST *
|
||||||
|
**************/
|
||||||
|
|
||||||
|
assertEq("defineProperties" in Object, true);
|
||||||
|
assertEq(Object.defineProperties.length, 2);
|
||||||
|
|
||||||
|
var o, props, desc, passed;
|
||||||
|
|
||||||
|
o = {};
|
||||||
|
props =
|
||||||
|
{
|
||||||
|
a: { value: 17, enumerable: true, configurable: true, writable: true },
|
||||||
|
b: { value: 42, enumerable: false, configurable: false, writable: false }
|
||||||
|
};
|
||||||
|
Object.defineProperties(o, props);
|
||||||
|
assertEq("a" in o, true);
|
||||||
|
assertEq("b" in o, true);
|
||||||
|
desc = Object.getOwnPropertyDescriptor(o, "a");
|
||||||
|
assertEq(desc.value, 17);
|
||||||
|
assertEq(desc.enumerable, true);
|
||||||
|
assertEq(desc.configurable, true);
|
||||||
|
assertEq(desc.writable, true);
|
||||||
|
desc = Object.getOwnPropertyDescriptor(o, "b");
|
||||||
|
assertEq(desc.value, 42);
|
||||||
|
assertEq(desc.enumerable, false);
|
||||||
|
assertEq(desc.configurable, false);
|
||||||
|
assertEq(desc.writable, false);
|
||||||
|
|
||||||
|
props =
|
||||||
|
{
|
||||||
|
c: { value: NaN, enumerable: false, configurable: true, writable: true },
|
||||||
|
b: { value: 44 }
|
||||||
|
};
|
||||||
|
var error = "before";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Object.defineProperties(o, props);
|
||||||
|
error = "no exception thrown";
|
||||||
|
}
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
|
if (e instanceof TypeError)
|
||||||
|
error = "typeerror";
|
||||||
|
else
|
||||||
|
error = "bad exception: " + e;
|
||||||
|
}
|
||||||
|
assertEq(error, "typeerror", "didn't throw or threw wrongly");
|
||||||
|
assertEq("c" in o, true, "new property added");
|
||||||
|
assertEq(o.b, 42, "old property value preserved");
|
||||||
|
|
||||||
|
function Properties() { }
|
||||||
|
Properties.prototype = { b: { value: 42, enumerable: true } };
|
||||||
|
props = new Properties();
|
||||||
|
Object.defineProperty(props, "a", { enumerable: false });
|
||||||
|
o = {};
|
||||||
|
Object.defineProperties(o, props);
|
||||||
|
assertEq("a" in o, false);
|
||||||
|
assertEq(Object.getOwnPropertyDescriptor(o, "a"), undefined,
|
||||||
|
"Object.defineProperties(O, Properties) should only use enumerable " +
|
||||||
|
"properties on Properties");
|
||||||
|
assertEq("b" in o, false);
|
||||||
|
assertEq(Object.getOwnPropertyDescriptor(o, "b"), undefined,
|
||||||
|
"Object.defineProperties(O, Properties) should only use enumerable " +
|
||||||
|
"properties directly on Properties");
|
||||||
|
|
||||||
|
/******************************************************************************/
|
||||||
|
|
||||||
|
reportCompare(true, true);
|
||||||
|
|
||||||
|
print("All tests passed!");
|
876
js/src/tests/ecma_5/Object/defineProperty-setup.js
Normal file
876
js/src/tests/ecma_5/Object/defineProperty-setup.js
Normal file
@ -0,0 +1,876 @@
|
|||||||
|
// Any copyright is dedicated to the Public Domain.
|
||||||
|
// http://creativecommons.org/licenses/publicdomain/
|
||||||
|
|
||||||
|
assertEq("defineProperty" in Object, true);
|
||||||
|
assertEq(Object.defineProperty.length, 3);
|
||||||
|
|
||||||
|
if (!Object.prototype.toSource)
|
||||||
|
{
|
||||||
|
Object.defineProperty(Object.prototype, "toSource",
|
||||||
|
{
|
||||||
|
value: function toSource()
|
||||||
|
{
|
||||||
|
if (this instanceof RegExp)
|
||||||
|
{
|
||||||
|
var v = "new RegExp(" + uneval(this.source);
|
||||||
|
var f = (this.multiline ? "m" : "") +
|
||||||
|
(this.global ? "g" : "") +
|
||||||
|
(this.ignoreCase ? "i" : "");
|
||||||
|
return v + (f ? ", '" + f + "'" : "") + ")";
|
||||||
|
}
|
||||||
|
return JSON.stringify(this);
|
||||||
|
},
|
||||||
|
enumerable: false,
|
||||||
|
configurable: true,
|
||||||
|
writable: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (!("uneval" in this))
|
||||||
|
{
|
||||||
|
Object.defineProperty(this, "uneval",
|
||||||
|
{
|
||||||
|
value: function uneval(v)
|
||||||
|
{
|
||||||
|
if (v === null)
|
||||||
|
return "null";
|
||||||
|
if (typeof v === "object")
|
||||||
|
return v.toSource();
|
||||||
|
if (typeof v === "string")
|
||||||
|
{
|
||||||
|
v = JSON.stringify({v:v});
|
||||||
|
return v.substring(5, v.length - 1);
|
||||||
|
}
|
||||||
|
return "" + v;
|
||||||
|
},
|
||||||
|
enumerable: false,
|
||||||
|
configurable: true,
|
||||||
|
writable: true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// reimplemented for the benefit of engines which don't have this helper
|
||||||
|
function assertEq(v1, v2, m)
|
||||||
|
{
|
||||||
|
if (!SameValue(v1, v2))
|
||||||
|
{
|
||||||
|
throw "assertion failed: " +
|
||||||
|
"got " + uneval(v1) + ", expected " + uneval(v2) +
|
||||||
|
(m ? ": " + m : "");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function SameValue(v1, v2)
|
||||||
|
{
|
||||||
|
if (v1 === 0 && v2 === 0)
|
||||||
|
return 1 / v1 === 1 / v2;
|
||||||
|
if (v1 !== v1 && v2 !== v2)
|
||||||
|
return true;
|
||||||
|
return v1 === v2;
|
||||||
|
}
|
||||||
|
|
||||||
|
function PropertyDescriptor(pd)
|
||||||
|
{
|
||||||
|
if (pd)
|
||||||
|
this.update(pd);
|
||||||
|
}
|
||||||
|
PropertyDescriptor.prototype.update = function update(pd)
|
||||||
|
{
|
||||||
|
if ("get" in pd)
|
||||||
|
this.get = pd.get;
|
||||||
|
if ("set" in pd)
|
||||||
|
this.set = pd.set;
|
||||||
|
if ("configurable" in pd)
|
||||||
|
this.configurable = pd.configurable;
|
||||||
|
if ("writable" in pd)
|
||||||
|
this.writable = pd.writable;
|
||||||
|
if ("enumerable" in pd)
|
||||||
|
this.enumerable = pd.enumerable;
|
||||||
|
if ("value" in pd)
|
||||||
|
this.value = pd.value;
|
||||||
|
};
|
||||||
|
PropertyDescriptor.prototype.convertToDataDescriptor = function convertToDataDescriptor()
|
||||||
|
{
|
||||||
|
delete this.get;
|
||||||
|
delete this.set;
|
||||||
|
this.writable = false;
|
||||||
|
this.value = undefined;
|
||||||
|
};
|
||||||
|
PropertyDescriptor.prototype.convertToAccessorDescriptor = function convertToAccessorDescriptor()
|
||||||
|
{
|
||||||
|
delete this.writable;
|
||||||
|
delete this.value;
|
||||||
|
this.get = undefined;
|
||||||
|
this.set = undefined;
|
||||||
|
};
|
||||||
|
|
||||||
|
function compareDescriptors(d1, d2)
|
||||||
|
{
|
||||||
|
if (d1 === undefined)
|
||||||
|
{
|
||||||
|
assertEq(d2, undefined, "non-descriptors");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (d2 === undefined)
|
||||||
|
{
|
||||||
|
assertEq(true, false, "descriptor-equality mismatch: " + uneval(d1) + ", " + uneval(d2));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var props = ["value", "get", "set", "enumerable", "configurable", "writable"];
|
||||||
|
for (var i = 0, sz = props.length; i < sz; i++)
|
||||||
|
{
|
||||||
|
var p = props[i];
|
||||||
|
assertEq(p in d1, p in d2, p + " different in d1/d2");
|
||||||
|
if (p in d1)
|
||||||
|
assertEq(d1[p], d2[p], p);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function examine(desc, field, allowDefault)
|
||||||
|
{
|
||||||
|
if (field in desc)
|
||||||
|
return desc[field];
|
||||||
|
assertEq(allowDefault, true, "reimplementation error");
|
||||||
|
switch (field)
|
||||||
|
{
|
||||||
|
case "value":
|
||||||
|
case "get":
|
||||||
|
case "set":
|
||||||
|
return undefined;
|
||||||
|
case "writable":
|
||||||
|
case "enumerable":
|
||||||
|
case "configurable":
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
assertEq(true, false, "bad field name: " + field);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function IsAccessorDescriptor(desc)
|
||||||
|
{
|
||||||
|
if (!desc)
|
||||||
|
return false;
|
||||||
|
if (!("get" in desc) && !("set" in desc))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function IsDataDescriptor(desc)
|
||||||
|
{
|
||||||
|
if (!desc)
|
||||||
|
return false;
|
||||||
|
if (!("value" in desc) && !("writable" in desc))
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function IsGenericDescriptor(desc)
|
||||||
|
{
|
||||||
|
if (!desc)
|
||||||
|
return false;
|
||||||
|
if (!IsAccessorDescriptor(desc) && !IsDataDescriptor(desc))
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
function CustomObject()
|
||||||
|
{
|
||||||
|
this.properties = {};
|
||||||
|
this.extensible = true;
|
||||||
|
}
|
||||||
|
CustomObject.prototype =
|
||||||
|
{
|
||||||
|
_reject: function _reject(throwing, msg)
|
||||||
|
{
|
||||||
|
if (throwing)
|
||||||
|
throw new TypeError(msg + "; rejected!");
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
defineOwnProperty: function defineOwnProperty(propname, desc, throwing)
|
||||||
|
{
|
||||||
|
assertEq(typeof propname, "string", "non-string propname");
|
||||||
|
|
||||||
|
// Step 1.
|
||||||
|
var current = this.properties[propname];
|
||||||
|
|
||||||
|
// Step 2.
|
||||||
|
var extensible = this.extensible;
|
||||||
|
|
||||||
|
// Step 3.
|
||||||
|
if (current === undefined && !extensible)
|
||||||
|
return this._reject(throwing, "object not extensible");
|
||||||
|
|
||||||
|
// Step 4.
|
||||||
|
if (current === undefined && extensible)
|
||||||
|
{
|
||||||
|
var p;
|
||||||
|
// Step 4(a).
|
||||||
|
if (IsGenericDescriptor(desc) || IsDataDescriptor(desc))
|
||||||
|
{
|
||||||
|
p = new PropertyDescriptor();
|
||||||
|
p.value = examine(desc, "value", true);
|
||||||
|
p.writable = examine(desc, "writable", true);
|
||||||
|
p.enumerable = examine(desc, "enumerable", true);
|
||||||
|
p.configurable = examine(desc, "configurable", true);
|
||||||
|
}
|
||||||
|
// Step 4(b).
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p = new PropertyDescriptor();
|
||||||
|
p.get = examine(desc, "get", true);
|
||||||
|
p.set = examine(desc, "set", true);
|
||||||
|
p.enumerable = examine(desc, "enumerable", true);
|
||||||
|
p.configurable = examine(desc, "configurable", true);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.properties[propname] = p;
|
||||||
|
|
||||||
|
// Step 4(c).
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 5.
|
||||||
|
if (!("value" in desc) && !("get" in desc) && !("set" in desc) &&
|
||||||
|
!("writable" in desc) && !("enumerable" in desc) &&
|
||||||
|
!("configurable" in desc))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 6.
|
||||||
|
do
|
||||||
|
{
|
||||||
|
if ("value" in desc)
|
||||||
|
{
|
||||||
|
if (!("value" in current) || !SameValue(desc.value, current.value))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ("get" in desc)
|
||||||
|
{
|
||||||
|
if (!("get" in current) || !SameValue(desc.get, current.get))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ("set" in desc)
|
||||||
|
{
|
||||||
|
if (!("set" in current) || !SameValue(desc.set, current.set))
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if ("writable" in desc)
|
||||||
|
{
|
||||||
|
if (!("writable" in current) ||
|
||||||
|
!SameValue(desc.writable, current.writable))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ("enumerable" in desc)
|
||||||
|
{
|
||||||
|
if (!("enumerable" in current) ||
|
||||||
|
!SameValue(desc.enumerable, current.enumerable))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ("configurable" in desc)
|
||||||
|
{
|
||||||
|
if (!("configurable" in current) ||
|
||||||
|
!SameValue(desc.configurable, current.configurable))
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// all fields in desc also in current, with the same values
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
while (false);
|
||||||
|
|
||||||
|
// Step 7.
|
||||||
|
if (!examine(current, "configurable"))
|
||||||
|
{
|
||||||
|
if ("configurable" in desc && examine(desc, "configurable"))
|
||||||
|
return this._reject(throwing, "can't make configurable again");
|
||||||
|
if ("enumerable" in desc &&
|
||||||
|
examine(current, "enumerable") !== examine(desc, "enumerable"))
|
||||||
|
{
|
||||||
|
return this._reject(throwing, "can't change enumerability");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 8.
|
||||||
|
if (IsGenericDescriptor(desc))
|
||||||
|
{
|
||||||
|
// do nothing
|
||||||
|
}
|
||||||
|
// Step 9.
|
||||||
|
else if (IsDataDescriptor(current) !== IsDataDescriptor(desc))
|
||||||
|
{
|
||||||
|
// Step 9(a).
|
||||||
|
if (!examine(current, "configurable"))
|
||||||
|
return this._reject(throwing, "can't change unconfigurable descriptor's type");
|
||||||
|
// Step 9(b).
|
||||||
|
if (IsDataDescriptor(current))
|
||||||
|
current.convertToAccessorDescriptor();
|
||||||
|
// Step 9(c).
|
||||||
|
else
|
||||||
|
current.convertToDataDescriptor();
|
||||||
|
}
|
||||||
|
// Step 10.
|
||||||
|
else if (IsDataDescriptor(current) && IsDataDescriptor(desc))
|
||||||
|
{
|
||||||
|
// Step 10(a)
|
||||||
|
if (!examine(current, "configurable"))
|
||||||
|
{
|
||||||
|
// Step 10(a).i.
|
||||||
|
if (!examine(current, "writable") &&
|
||||||
|
"writable" in desc && examine(desc, "writable"))
|
||||||
|
{
|
||||||
|
return this._reject(throwing, "can't make data property writable again");
|
||||||
|
}
|
||||||
|
// Step 10(a).ii.
|
||||||
|
if (!examine(current, "writable"))
|
||||||
|
{
|
||||||
|
if ("value" in desc &&
|
||||||
|
!SameValue(examine(desc, "value"), examine(current, "value")))
|
||||||
|
{
|
||||||
|
return this._reject(throwing, "can't change value if not writable");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Step 10(b).
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assertEq(examine(current, "configurable"), true,
|
||||||
|
"spec bug step 10(b)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Step 11.
|
||||||
|
else
|
||||||
|
{
|
||||||
|
assertEq(IsAccessorDescriptor(current) && IsAccessorDescriptor(desc),
|
||||||
|
true,
|
||||||
|
"spec bug");
|
||||||
|
|
||||||
|
// Step 11(a).
|
||||||
|
if (!examine(current, "configurable"))
|
||||||
|
{
|
||||||
|
// Step 11(a).i.
|
||||||
|
if ("set" in desc &&
|
||||||
|
!SameValue(examine(desc, "set"), examine(current, "set")))
|
||||||
|
{
|
||||||
|
return this._reject(throwing, "can't change setter if not configurable");
|
||||||
|
}
|
||||||
|
// Step 11(a).ii.
|
||||||
|
if ("get" in desc &&
|
||||||
|
!SameValue(examine(desc, "get"), examine(current, "get")))
|
||||||
|
{
|
||||||
|
return this._reject(throwing, "can't change getter if not configurable");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Step 12.
|
||||||
|
current.update(desc);
|
||||||
|
|
||||||
|
// Step 13.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function IsCallable(v)
|
||||||
|
{
|
||||||
|
return typeof v === "undefined" || typeof v === "function";
|
||||||
|
}
|
||||||
|
|
||||||
|
var NativeTest =
|
||||||
|
{
|
||||||
|
newObject: function newObject()
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
},
|
||||||
|
defineProperty: function defineProperty(obj, propname, propdesc)
|
||||||
|
{
|
||||||
|
Object.defineProperty(obj, propname, propdesc);
|
||||||
|
},
|
||||||
|
getDescriptor: function getDescriptor(obj, propname)
|
||||||
|
{
|
||||||
|
return Object.getOwnPropertyDescriptor(obj, propname);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var ReimplTest =
|
||||||
|
{
|
||||||
|
newObject: function newObject()
|
||||||
|
{
|
||||||
|
return new CustomObject();
|
||||||
|
},
|
||||||
|
defineProperty: function defineProperty(obj, propname, propdesc)
|
||||||
|
{
|
||||||
|
assertEq(obj instanceof CustomObject, true, "obj not instanceof CustomObject");
|
||||||
|
if ("get" in propdesc || "set" in propdesc)
|
||||||
|
{
|
||||||
|
if ("value" in propdesc || "writable" in propdesc)
|
||||||
|
throw new TypeError("get/set and value/writable");
|
||||||
|
if (!IsCallable(propdesc.get))
|
||||||
|
throw new TypeError("get defined, uncallable");
|
||||||
|
if (!IsCallable(propdesc.set))
|
||||||
|
throw new TypeError("set defined, uncallable");
|
||||||
|
}
|
||||||
|
return obj.defineOwnProperty(propname, propdesc, true);
|
||||||
|
},
|
||||||
|
getDescriptor: function getDescriptor(obj, propname)
|
||||||
|
{
|
||||||
|
if (!(propname in obj.properties))
|
||||||
|
return undefined;
|
||||||
|
|
||||||
|
return new PropertyDescriptor(obj.properties[propname]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var JSVAL_INT_MAX = Math.pow(2, 30) - 1;
|
||||||
|
var JSVAL_INT_MIN = -Math.pow(2, 30);
|
||||||
|
|
||||||
|
|
||||||
|
function isValidDescriptor(propdesc)
|
||||||
|
{
|
||||||
|
if ("get" in propdesc || "set" in propdesc)
|
||||||
|
{
|
||||||
|
if ("value" in propdesc || "writable" in propdesc)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// We permit null here simply because this test's author believes the
|
||||||
|
// implementation may sometime be susceptible to making mistakes in this
|
||||||
|
// regard and would prefer to be cautious.
|
||||||
|
if (propdesc.get !== null && propdesc.get !== undefined && !IsCallable(propdesc.get))
|
||||||
|
return false;
|
||||||
|
if (propdesc.set !== null && propdesc.set !== undefined && !IsCallable(propdesc.set))
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
var OMIT = {};
|
||||||
|
var VALUES =
|
||||||
|
[-Infinity, JSVAL_INT_MIN, -0, +0, 1.5, JSVAL_INT_MAX, Infinity,
|
||||||
|
NaN, "foo", "bar", null, undefined, true, false, {}, /a/, OMIT];
|
||||||
|
var GETS =
|
||||||
|
[undefined, function get1() { return 1; }, function get2() { return 2; },
|
||||||
|
null, 5, OMIT];
|
||||||
|
var SETS =
|
||||||
|
[undefined, function set1() { return 1; }, function set2() { return 2; },
|
||||||
|
null, 5, OMIT];
|
||||||
|
var ENUMERABLES = [true, false, OMIT];
|
||||||
|
var CONFIGURABLES = [true, false, OMIT];
|
||||||
|
var WRITABLES = [true, false, OMIT];
|
||||||
|
|
||||||
|
function mapTestDescriptors(filter)
|
||||||
|
{
|
||||||
|
var descs = [];
|
||||||
|
var desc = {};
|
||||||
|
|
||||||
|
function put(field, value)
|
||||||
|
{
|
||||||
|
if (value !== OMIT)
|
||||||
|
desc[field] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
VALUES.forEach(function(value)
|
||||||
|
{
|
||||||
|
GETS.forEach(function(get)
|
||||||
|
{
|
||||||
|
SETS.forEach(function(set)
|
||||||
|
{
|
||||||
|
ENUMERABLES.forEach(function(enumerable)
|
||||||
|
{
|
||||||
|
CONFIGURABLES.forEach(function(configurable)
|
||||||
|
{
|
||||||
|
WRITABLES.forEach(function(writable)
|
||||||
|
{
|
||||||
|
desc = {};
|
||||||
|
put("value", value);
|
||||||
|
put("get", get);
|
||||||
|
put("set", set);
|
||||||
|
put("enumerable", enumerable);
|
||||||
|
put("configurable", configurable);
|
||||||
|
put("writable", writable);
|
||||||
|
if (filter(desc))
|
||||||
|
descs.push(desc);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return descs;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ALL_DESCRIPTORS = mapTestDescriptors(function(d) { return true; });
|
||||||
|
var VALID_DESCRIPTORS = mapTestDescriptors(isValidDescriptor);
|
||||||
|
|
||||||
|
var SKIP_FULL_FUNCTION_LENGTH_TESTS = true;
|
||||||
|
|
||||||
|
function TestRunner()
|
||||||
|
{
|
||||||
|
this._logLines = [];
|
||||||
|
}
|
||||||
|
TestRunner.prototype =
|
||||||
|
{
|
||||||
|
// MAIN METHODS
|
||||||
|
|
||||||
|
runFunctionLengthTests: function runFunctionLengthTests()
|
||||||
|
{
|
||||||
|
var self = this;
|
||||||
|
function functionLengthTests()
|
||||||
|
{
|
||||||
|
if (SKIP_FULL_FUNCTION_LENGTH_TESTS)
|
||||||
|
{
|
||||||
|
print("Skipping full tests for redefining Function.length for now " +
|
||||||
|
"because we don't support redefinition of properties with " +
|
||||||
|
"native getter or setter...");
|
||||||
|
self._simpleFunctionLengthTests();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
self._simpleFunctionLengthTests();
|
||||||
|
self._fullFunctionLengthTests(function() { }, 0);
|
||||||
|
self._fullFunctionLengthTests(function(one) { }, 1);
|
||||||
|
self._fullFunctionLengthTests(function(one, two) { }, 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._runTestSet(functionLengthTests, "Function length tests completed!");
|
||||||
|
},
|
||||||
|
|
||||||
|
runNotPresentTests: function runNotPresentTests()
|
||||||
|
{
|
||||||
|
var self = this;
|
||||||
|
function notPresentTests()
|
||||||
|
{
|
||||||
|
print("Running not-present tests now...");
|
||||||
|
|
||||||
|
for (var i = 0, sz = ALL_DESCRIPTORS.length; i < sz; i++)
|
||||||
|
self._runSingleNotPresentTest(ALL_DESCRIPTORS[i]);
|
||||||
|
};
|
||||||
|
|
||||||
|
this._runTestSet(notPresentTests, "Not-present length tests completed!");
|
||||||
|
},
|
||||||
|
|
||||||
|
runPropertyPresentTestsFraction:
|
||||||
|
function runPropertyPresentTestsFraction(part, parts)
|
||||||
|
{
|
||||||
|
var self = this;
|
||||||
|
function propertyPresentTests()
|
||||||
|
{
|
||||||
|
print("Running already-present tests now...");
|
||||||
|
|
||||||
|
var total = VALID_DESCRIPTORS.length;
|
||||||
|
var start = Math.floor((part - 1) / parts * total);
|
||||||
|
var end = Math.floor(part / parts * total);
|
||||||
|
|
||||||
|
for (var i = start; i < end; i++)
|
||||||
|
{
|
||||||
|
var old = VALID_DESCRIPTORS[i];
|
||||||
|
print("Starting test with old descriptor " + old.toSource() + "...");
|
||||||
|
|
||||||
|
for (var j = 0, sz2 = VALID_DESCRIPTORS.length; j < sz2; j++)
|
||||||
|
self._runSinglePropertyPresentTest(old, VALID_DESCRIPTORS[j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._runTestSet(propertyPresentTests,
|
||||||
|
"Property-present fraction " + part + " of " + parts +
|
||||||
|
" completed!");
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
// HELPERS
|
||||||
|
|
||||||
|
runPropertyPresentTests: function runPropertyPresentTests()
|
||||||
|
{
|
||||||
|
print("Running already-present tests now...");
|
||||||
|
|
||||||
|
for (var i = 0, sz = VALID_DESCRIPTORS.length; i < sz; i++)
|
||||||
|
{
|
||||||
|
var old = VALID_DESCRIPTORS[i];
|
||||||
|
print("Starting test with old descriptor " + old.toSource() + "...");
|
||||||
|
|
||||||
|
for (var j = 0, sz2 = VALID_DESCRIPTORS.length; j < sz2; j++)
|
||||||
|
this._runSinglePropertyPresentTest(old, VALID_DESCRIPTORS[j]);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_runTestSet: function _runTestSet(fun, completeMessage)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
fun();
|
||||||
|
|
||||||
|
print(completeMessage);
|
||||||
|
}
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
|
print("ERROR, EXITING (line " + (e.lineNumber || -1) + "): " + e);
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
this._reportAllErrors();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_reportAllErrors: function _reportAllErrors()
|
||||||
|
{
|
||||||
|
var errorCount = this._logLines.length;
|
||||||
|
print("Full accumulated number of errors: " + errorCount);
|
||||||
|
if (errorCount > 0)
|
||||||
|
throw errorCount + " errors detected, FAIL";
|
||||||
|
},
|
||||||
|
_simpleFunctionLengthTests: function _simpleFunctionLengthTests(fun)
|
||||||
|
{
|
||||||
|
print("Running simple Function.length tests now..");
|
||||||
|
|
||||||
|
function expectThrowTypeError(o, p, desc)
|
||||||
|
{
|
||||||
|
var err = "<none>", passed = false;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Object.defineProperty(o, p, desc);
|
||||||
|
}
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
|
err = e;
|
||||||
|
passed = e instanceof TypeError;
|
||||||
|
}
|
||||||
|
assertEq(passed, true, fun + " didn't throw TypeError when called: " + err);
|
||||||
|
}
|
||||||
|
|
||||||
|
expectThrowTypeError(function a() { }, "length", { value: 1 });
|
||||||
|
expectThrowTypeError(function a() { }, "length", { enumerable: true });
|
||||||
|
expectThrowTypeError(function a() { }, "length", { configurable: true });
|
||||||
|
expectThrowTypeError(function a() { }, "length", { writable: true });
|
||||||
|
},
|
||||||
|
_fullFunctionLengthTests: function _fullFunctionLengthTests(fun)
|
||||||
|
{
|
||||||
|
var len = fun.length;
|
||||||
|
print("Running Function.length (" + len + ") tests now...");
|
||||||
|
|
||||||
|
var desc;
|
||||||
|
var gen = new DescriptorState();
|
||||||
|
while ((desc = gen.nextDescriptor()))
|
||||||
|
this._runSingleFunctionLengthTest(fun, len, desc);
|
||||||
|
},
|
||||||
|
_log: function _log(v)
|
||||||
|
{
|
||||||
|
var m = "" + v;
|
||||||
|
print(m);
|
||||||
|
this._logLines.push(m);
|
||||||
|
},
|
||||||
|
_runSingleNotPresentTest: function _runSingleNotPresentTest(desc)
|
||||||
|
{
|
||||||
|
var nativeObj = NativeTest.newObject();
|
||||||
|
var reimplObj = ReimplTest.newObject();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
NativeTest.defineProperty(nativeObj, "foo", desc);
|
||||||
|
}
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ReimplTest.defineProperty(reimplObj, "foo", desc);
|
||||||
|
}
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._log("Difference when comparing native/reimplementation " +
|
||||||
|
"behavior for new descriptor " + desc.toSource() +
|
||||||
|
", error " + e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ReimplTest.defineProperty(reimplObj, "foo", desc);
|
||||||
|
}
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
|
this._log("Reimpl threw defining new descriptor " + desc.toSource() +
|
||||||
|
", error: " + e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var nativeDesc = NativeTest.getDescriptor(nativeObj, "foo");
|
||||||
|
var reimplDesc = ReimplTest.getDescriptor(reimplObj, "foo");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
compareDescriptors(nativeDesc, reimplDesc);
|
||||||
|
}
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
|
this._log("Difference comparing returned descriptors for new " +
|
||||||
|
"property defined with descriptor " + desc.toSource() +
|
||||||
|
"; error: " + e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_runSinglePropertyPresentTest: function _runSinglePropertyPresentTest(old, add)
|
||||||
|
{
|
||||||
|
var nativeObj = NativeTest.newObject();
|
||||||
|
var reimplObj = ReimplTest.newObject();
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
NativeTest.defineProperty(nativeObj, "foo", old);
|
||||||
|
}
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
|
if (!SameValue(NativeTest.getDescriptor(nativeObj, "foo"),
|
||||||
|
undefined))
|
||||||
|
{
|
||||||
|
this._log("defining bad property descriptor: " + old.toSource());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ReimplTest.defineProperty(reimplObj, "foo", old);
|
||||||
|
}
|
||||||
|
catch (e2)
|
||||||
|
{
|
||||||
|
if (!SameValue(ReimplTest.getDescriptor(reimplObj, "foo"),
|
||||||
|
undefined))
|
||||||
|
{
|
||||||
|
this._log("defining bad property descriptor: " + old.toSource() +
|
||||||
|
"; reimplObj: " + uneval(reimplObj));
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this._log("Difference defining a property with descriptor " +
|
||||||
|
old.toSource() + ", error " + e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ReimplTest.defineProperty(reimplObj, "foo", old);
|
||||||
|
}
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
|
this._log("Difference when comparing native/reimplementation " +
|
||||||
|
"behavior when adding descriptor " + add.toSource() +
|
||||||
|
", error: " + e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
NativeTest.defineProperty(nativeObj, "foo", add);
|
||||||
|
}
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ReimplTest.defineProperty(reimplObj, "foo", add);
|
||||||
|
}
|
||||||
|
catch (e2)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._log("Difference when comparing native/reimplementation " +
|
||||||
|
"behavior for added descriptor " + add.toSource() + ", " +
|
||||||
|
"initial was " + old.toSource() + "; error: " + e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ReimplTest.defineProperty(reimplObj, "foo", add);
|
||||||
|
}
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
|
this._log("Difference when comparing native/reimplementation " +
|
||||||
|
"behavior for readded descriptor " + add.toSource() + ", " +
|
||||||
|
"initial was " + old.toSource() + "; native readd didn't " +
|
||||||
|
"throw, reimpl add did, error: " + e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var nativeDesc = NativeTest.getDescriptor(nativeObj, "foo");
|
||||||
|
var reimplDesc = ReimplTest.getDescriptor(reimplObj, "foo");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
compareDescriptors(nativeDesc, reimplDesc);
|
||||||
|
}
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
|
this._log("Difference comparing returned descriptors for readded " +
|
||||||
|
"property defined with descriptor " + add.toSource() + "; " +
|
||||||
|
"initial was " + old.toSource() + "; error: " + e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_runSingleFunctionLengthTest: function _runSingleFunctionLengthTest(fun, len, desc)
|
||||||
|
{
|
||||||
|
var nativeObj = fun;
|
||||||
|
var reimplObj = ReimplTest.newObject();
|
||||||
|
ReimplTest.defineProperty(reimplObj, "length",
|
||||||
|
{
|
||||||
|
value: len,
|
||||||
|
enumerable: false,
|
||||||
|
configurable: false,
|
||||||
|
writable: false
|
||||||
|
});
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
NativeTest.defineProperty(nativeObj, "length", desc);
|
||||||
|
}
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ReimplTest.defineProperty(reimplObj, "length", desc);
|
||||||
|
}
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this._log("Difference when comparing Function.length native/reimpl " +
|
||||||
|
"behavior for descriptor " + desc.toSource() +
|
||||||
|
", native impl threw error " + e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ReimplTest.defineProperty(reimplObj, "length", desc);
|
||||||
|
}
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
|
this._log("Difference defining new Function.length descriptor: impl " +
|
||||||
|
"succeeded, reimpl threw for descriptor " +
|
||||||
|
desc.toSource() + ", error: " + e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var nativeDesc = NativeTest.getDescriptor(nativeObj, "length");
|
||||||
|
var reimplDesc = ReimplTest.getDescriptor(reimplObj, "length");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
compareDescriptors(nativeDesc, reimplDesc);
|
||||||
|
}
|
||||||
|
catch (e)
|
||||||
|
{
|
||||||
|
this._log("Difference comparing returned descriptors for " +
|
||||||
|
"Function.length with descriptor " + desc.toSource() +
|
||||||
|
"; error: " + e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
@ -1,3 +1,11 @@
|
|||||||
url-prefix ../../jsreftest.html?test=ecma_5/Object/
|
url-prefix ../../jsreftest.html?test=ecma_5/Object/
|
||||||
script 15.2.3.3-01.js # does not use reportCompare
|
script 15.2.3.3-01.js # does not use reportCompare
|
||||||
|
script 15.2.3.6-function-length.js
|
||||||
|
script 15.2.3.6-miscellaneous.js
|
||||||
|
script 15.2.3.6-new-definition.js
|
||||||
|
skip-if(!xulRuntime.shell) script 15.2.3.6-redefinition-1-of-4.js # uses shell load() function
|
||||||
|
skip-if(!xulRuntime.shell) script 15.2.3.6-redefinition-2-of-4.js # uses shell load() function
|
||||||
|
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 15.2.3.14-01.js # does not use reportCompare
|
script 15.2.3.14-01.js # does not use reportCompare
|
||||||
|
Loading…
Reference in New Issue
Block a user