diff --git a/js/src/jsobj.c b/js/src/jsobj.c index 97204a74306d..e399ee68edb3 100644 --- a/js/src/jsobj.c +++ b/js/src/jsobj.c @@ -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); diff --git a/js/src/jsscope.h b/js/src/jsscope.h index b39ee7cf0adb..7fe19128425f 100644 --- a/js/src/jsscope.h +++ b/js/src/jsscope.h @@ -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); diff --git a/js/src/jsstr.c b/js/src/jsstr.c index 9d63cd955401..e0019abaf728 100644 --- a/js/src/jsstr.c +++ b/js/src/jsstr.c @@ -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 };