Restore enumeable, permanent, readonly single-char elements to string objects; fix propertyIsEnumerable to work with shared permanent proto-properties (167910, r=rogerl).

This commit is contained in:
brendan%mozilla.org 2002-09-12 19:34:59 +00:00
parent 65f9649028
commit d02561c5d2
3 changed files with 74 additions and 25 deletions

View File

@ -1123,7 +1123,6 @@ obj_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
JSObject *obj2;
JSProperty *prop;
JSScopeProperty *sprop;
JSBool sharedPermanent;
if (!JS_ValueToId(cx, argv[0], &id))
return JS_FALSE;
@ -1135,9 +1134,7 @@ obj_hasOwnProperty(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
*rval = JSVAL_TRUE;
} else if (OBJ_IS_NATIVE(obj2)) {
sprop = (JSScopeProperty *)prop;
sharedPermanent =
(~sprop->attrs & (JSPROP_SHARED | JSPROP_PERMANENT)) == 0;
*rval = BOOLEAN_TO_JSVAL(sharedPermanent);
*rval = BOOLEAN_TO_JSVAL(SPROP_IS_SHARED_PERMANENT(sprop));
} else {
*rval = JSVAL_FALSE;
}
@ -1176,14 +1173,27 @@ obj_propertyIsEnumerable(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
if (!OBJ_LOOKUP_PROPERTY(cx, obj, id, &obj2, &prop))
return JS_FALSE;
/* XXX ECMA spec error compatible: return false unless hasOwnProperty. */
if (prop && obj2 != obj) {
/*
* XXX ECMA spec error compatible: return false unless hasOwnProperty.
* The ECMA spec really should be fixed so propertyIsEnumerable and the
* for..in loop agree on whether prototype properties are enumerable,
* obviously by fixing this method (not by breaking the for..in loop!).
*
* We check here for shared permanent prototype properties, which should
* be treated as if they are local to obj. They are an implementation
* technique used to satisfy ECMA requirements; users should not be able
* to distinguish a shared permanent proto-property from a local one.
*/
if (prop &&
obj2 != obj &&
!(OBJ_IS_NATIVE(obj2) &&
SPROP_IS_SHARED_PERMANENT((JSScopeProperty *)prop))) {
OBJ_DROP_PROPERTY(cx, obj2, prop);
*rval = JSVAL_FALSE;
return JS_TRUE;
}
ok = OBJ_GET_ATTRIBUTES(cx, obj, id, prop, &attrs);
ok = OBJ_GET_ATTRIBUTES(cx, obj2, id, prop, &attrs);
if (prop)
OBJ_DROP_PROPERTY(cx, obj2, prop);
if (ok)
@ -2804,7 +2814,7 @@ js_DeleteProperty(JSContext *cx, JSObject *obj, jsid id, jsval *rval)
if (prop) {
if (OBJ_IS_NATIVE(proto)) {
sprop = (JSScopeProperty *)prop;
if ((~sprop->attrs & (JSPROP_SHARED | JSPROP_PERMANENT)) == 0)
if (SPROP_IS_SHARED_PERMANENT(sprop))
*rval = JSVAL_FALSE;
}
OBJ_DROP_PROPERTY(cx, proto, prop);

View File

@ -315,6 +315,10 @@ struct JSScopeProperty {
JSMSG_GETTER_ONLY, NULL), JS_FALSE) \
: SPROP_CALL_SETTER(cx, sprop, (sprop)->setter, obj, obj2, vp))
/* Macro for common expression to test for shared permanent attributes. */
#define SPROP_IS_SHARED_PERMANENT(sprop) \
((~(sprop)->attrs & (JSPROP_SHARED | JSPROP_PERMANENT)) == 0)
extern JSScope *
js_GetMutableScope(JSContext *cx, JSObject *obj);

View File

@ -502,7 +502,7 @@ static JSPropertySpec string_props[] = {
static JSBool
str_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
{
JSString *str, *str1;
JSString *str;
jsint slot;
if (!JSVAL_IS_INT(id))
@ -511,22 +511,57 @@ str_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
if (!str)
return JS_FALSE;
slot = JSVAL_TO_INT(id);
if (slot == STRING_LENGTH) {
if (slot == STRING_LENGTH)
*vp = INT_TO_JSVAL((jsint) JSSTRING_LENGTH(str));
} else {
/*
* ECMA extension: if the property has not been set already (possibly
* to a user-defined value), return as its value the character at the
* given index. Don't use resolve, which penalizes all string method
* calls and other property accesses. Let the property attributes be
* the default, so the user can override, enumerate, etc. and get the
* expected results.
*/
if (JSVAL_IS_VOID(*vp) && (size_t)slot < JSSTRING_LENGTH(str)) {
str1 = js_NewDependentString(cx, str, (size_t)slot, 1, 0);
if (!str1)
return JS_FALSE;
*vp = STRING_TO_JSVAL(str1);
return JS_TRUE;
}
#define STRING_ELEMENT_ATTRS (JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT)
static JSBool
str_enumerate(JSContext *cx, JSObject *obj)
{
JSString *str, *str1;
size_t i, length;
str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
if (!str)
return JS_FALSE;
length = JSSTRING_LENGTH(str);
for (i = 0; i < length; i++) {
str1 = js_NewDependentString(cx, str, i, 1, 0);
if (!str1)
return JS_FALSE;
if (!OBJ_DEFINE_PROPERTY(cx, obj, INT_TO_JSVAL(i),
STRING_TO_JSVAL(str1), NULL, NULL,
STRING_ELEMENT_ATTRS, NULL)) {
return JS_FALSE;
}
}
return JS_TRUE;
}
static JSBool
str_resolve(JSContext *cx, JSObject *obj, jsval id)
{
JSString *str, *str1;
jsint slot;
if (!JSVAL_IS_INT(id))
return JS_TRUE;
str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
if (!str)
return JS_FALSE;
slot = JSVAL_TO_INT(id);
if ((size_t)slot < JSSTRING_LENGTH(str)) {
str1 = js_NewDependentString(cx, str, (size_t)slot, 1, 0);
if (!str1)
return JS_FALSE;
if (!OBJ_DEFINE_PROPERTY(cx, obj, INT_TO_JSVAL(slot),
STRING_TO_JSVAL(str1), NULL, NULL,
STRING_ELEMENT_ATTRS, NULL)) {
return JS_FALSE;
}
}
return JS_TRUE;
@ -536,7 +571,7 @@ static JSClass string_class = {
js_String_str,
JSCLASS_HAS_PRIVATE,
JS_PropertyStub, JS_PropertyStub, str_getProperty, JS_PropertyStub,
JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
str_enumerate, str_resolve, JS_ConvertStub, JS_FinalizeStub,
JSCLASS_NO_OPTIONAL_MEMBERS
};