Bug 619529 - avoid any exposure of XML's AttributeName and AnyName objects. r=brendan
authorIgor Bukanov <igor@mir2.org>
Sun, 09 Jan 2011 00:01:52 +0100
changeset 60566 1e5925b72c513e2c5e4643b6174930f7ad531112
parent 60565 1b1ae38d5c3711e20bcd6ca2dd4eca4947ba1dea
child 60567 4f71ecca94fe1c220de703e52aa6ca78b05eab45
push id18037
push usercleary@mozilla.com
push dateFri, 14 Jan 2011 17:42:55 +0000
treeherdermozilla-central@4e0501a0c5e5 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbrendan
bugs619529
milestone2.0b10pre
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 619529 - avoid any exposure of XML's AttributeName and AnyName objects. r=brendan
js/src/jit-test/tests/basic/bug613151.js
js/src/jsapi.cpp
js/src/jsfun.cpp
js/src/jsobj.cpp
js/src/jsobjinlines.h
js/src/jsproto.tbl
js/src/jstracer.cpp
js/src/jsxml.cpp
js/src/jsxml.h
js/src/tests/e4x/QName/jstests.list
js/src/tests/e4x/QName/regress-619529.js
js/src/tests/ecma_5/extensions/Object-keys-and-object-ids.js
js/src/tests/js1_8_5/extensions/clone-object.js
--- a/js/src/jit-test/tests/basic/bug613151.js
+++ b/js/src/jit-test/tests/basic/bug613151.js
@@ -2,17 +2,17 @@
 function n() {}
 function g() {}
 eval("\
   function a() {}\
   function b() {\
     for (w in this) {}\
     Object.defineProperty(\
       this, \
-      new AttributeName, \
+      new QName, \
       ({enumerable: true})\
     )\
   }\
   for (z in [0, 0, 0]) b()\
 ")
 
 // Test it doesn't assert.
 
--- a/js/src/jsapi.cpp
+++ b/js/src/jsapi.cpp
@@ -1587,18 +1587,16 @@ static JSStdName standard_class_names[] 
     {js_InitExceptionClasses,   EAGER_CLASS_ATOM(EvalError), CLASP(Error)},
     {js_InitExceptionClasses,   EAGER_CLASS_ATOM(RangeError), CLASP(Error)},
     {js_InitExceptionClasses,   EAGER_CLASS_ATOM(ReferenceError), CLASP(Error)},
     {js_InitExceptionClasses,   EAGER_CLASS_ATOM(SyntaxError), CLASP(Error)},
     {js_InitExceptionClasses,   EAGER_CLASS_ATOM(TypeError), CLASP(Error)},
     {js_InitExceptionClasses,   EAGER_CLASS_ATOM(URIError), CLASP(Error)},
 
 #if JS_HAS_XML_SUPPORT
-    {js_InitAnyNameClass,       EAGER_ATOM_AND_CLASP(AnyName)},
-    {js_InitAttributeNameClass, EAGER_ATOM_AND_CLASP(AttributeName)},
     {js_InitXMLClass,           LAZY_ATOM(XMLList), CLASP(XML)},
     {js_InitXMLClass,           LAZY_ATOM(isXMLName), CLASP(XML)},
 #endif
 
 #if JS_HAS_GENERATORS
     {js_InitIteratorClasses,    EAGER_ATOM_AND_CLASP(Iterator)},
     {js_InitIteratorClasses,    EAGER_ATOM_AND_CLASP(Generator)},
 #endif
@@ -1719,17 +1717,17 @@ JS_ResolveStandardClass(JSContext *cx, J
         }
     }
 
     if (stdnm) {
         /*
          * If this standard class is anonymous, then we don't want to resolve
          * by name.
          */
-        JS_ASSERT(obj->getClass()->flags & JSCLASS_IS_GLOBAL);
+        JS_ASSERT(obj->isGlobal());
         if (stdnm->clasp->flags & JSCLASS_IS_ANONYMOUS)
             return JS_TRUE;
 
         JSProtoKey key = JSCLASS_CACHED_PROTO_KEY(stdnm->clasp);
         if (obj->getReservedSlot(key).isObject())
             return JS_TRUE;
 
         if (!stdnm->init(cx, obj))
@@ -3814,20 +3812,18 @@ JS_ClearScope(JSContext *cx, JSObject *o
     JSFinalizeOp clearOp = obj->getOps()->clear;
     if (clearOp)
         clearOp(cx, obj);
 
     if (obj->isNative())
         js_ClearNative(cx, obj);
 
     /* Clear cached class objects on the global object. */
-    if (obj->getClass()->flags & JSCLASS_IS_GLOBAL) {
-        int key;
-
-        for (key = JSProto_Null; key < JSProto_LIMIT * 3; key++)
+    if (obj->isGlobal()) {
+        for (int key = JSProto_Null; key < JSProto_LIMIT * 3; key++)
             JS_SetReservedSlot(cx, obj, key, JSVAL_VOID);
     }
 
     js_InitRandom(cx);
 }
 
 JS_PUBLIC_API(JSIdArray *)
 JS_Enumerate(JSContext *cx, JSObject *obj)
--- a/js/src/jsfun.cpp
+++ b/js/src/jsfun.cpp
@@ -2632,17 +2632,17 @@ js_InitFunctionClass(JSContext *cx, JSOb
     script->noScriptRval = true;
     script->code[0] = JSOP_STOP;
     script->code[1] = SRC_NULL;
 #ifdef CHECK_SCRIPT_OWNER
     script->owner = NULL;
 #endif
     fun->u.i.script = script;
 
-    if (obj->getClass()->flags & JSCLASS_IS_GLOBAL) {
+    if (obj->isGlobal()) {
         /* ES5 13.2.3: Construct the unique [[ThrowTypeError]] function object. */
         JSObject *throwTypeError =
             js_NewFunction(cx, NULL, reinterpret_cast<Native>(ThrowTypeError), 0,
                            0, obj, NULL);
         if (!throwTypeError)
             return NULL;
 
         JS_ALWAYS_TRUE(js_SetReservedSlot(cx, obj, JSRESERVED_GLOBAL_THROWTYPEERROR,
--- a/js/src/jsobj.cpp
+++ b/js/src/jsobj.cpp
@@ -3739,17 +3739,17 @@ DefineStandardSlot(JSContext *cx, JSObje
     jsid id = ATOM_TO_JSID(atom);
 
     if (key != JSProto_Null) {
         /*
          * Initializing an actual standard class on a global object. If the
          * property is not yet present, force it into a new one bound to a
          * reserved slot. Otherwise, go through the normal property path.
          */
-        JS_ASSERT(obj->getClass()->flags & JSCLASS_IS_GLOBAL);
+        JS_ASSERT(obj->isGlobal());
         JS_ASSERT(obj->isNative());
 
         if (!obj->ensureClassReservedSlots(cx))
             return false;
 
         const Shape *shape = obj->nativeLookup(id);
         if (!shape) {
             uint32 slot = 2 * JSProto_LIMIT + key;
@@ -3836,20 +3836,17 @@ js_InitClass(JSContext *cx, JSObject *ob
     JSObject *ctor;
     if (!constructor) {
         /*
          * Lacking a constructor, name the prototype (e.g., Math) unless this
          * class (a) is anonymous, i.e. for internal use only; (b) the class
          * of obj (the global object) is has a reserved slot indexed by key;
          * and (c) key is not the null key.
          */
-        if (!(clasp->flags & JSCLASS_IS_ANONYMOUS) ||
-            !(obj->getClass()->flags & JSCLASS_IS_GLOBAL) ||
-            key == JSProto_Null)
-        {
+        if (!(clasp->flags & JSCLASS_IS_ANONYMOUS) || !obj->isGlobal() || key == JSProto_Null) {
             uint32 attrs = (clasp->flags & JSCLASS_IS_ANONYMOUS)
                            ? JSPROP_READONLY | JSPROP_PERMANENT
                            : 0;
             if (!DefineStandardSlot(cx, obj, key, atom, ObjectValue(*proto), attrs, named))
                 goto bad;
         }
 
         ctor = proto;
@@ -4116,26 +4113,25 @@ SetProto(JSContext *cx, JSObject *obj, J
 }
 
 }
 
 JSBool
 js_GetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key,
                   JSObject **objp)
 {
-    JSObject *tmp, *cobj;
+    JSObject *cobj;
     JSResolvingKey rkey;
     JSResolvingEntry *rentry;
     uint32 generation;
     JSObjectOp init;
     Value v;
 
-    while ((tmp = obj->getParent()) != NULL)
-        obj = tmp;
-    if (!(obj->getClass()->flags & JSCLASS_IS_GLOBAL)) {
+    obj = obj->getGlobal();
+    if (!obj->isGlobal()) {
         *objp = NULL;
         return JS_TRUE;
     }
 
     v = obj->getReservedSlot(key);
     if (v.isObject()) {
         *objp = &v.toObject();
         return JS_TRUE;
@@ -4169,17 +4165,17 @@ js_GetClassObject(JSContext *cx, JSObjec
     *objp = cobj;
     return ok;
 }
 
 JSBool
 js_SetClassObject(JSContext *cx, JSObject *obj, JSProtoKey key, JSObject *cobj, JSObject *proto)
 {
     JS_ASSERT(!obj->getParent());
-    if (!(obj->getClass()->flags & JSCLASS_IS_GLOBAL))
+    if (!obj->isGlobal())
         return JS_TRUE;
 
     return js_SetReservedSlot(cx, obj, key, ObjectOrNullValue(cobj)) &&
            js_SetReservedSlot(cx, obj, JSProto_LIMIT + key, ObjectOrNullValue(proto));
 }
 
 JSBool
 js_FindClassObject(JSContext *cx, JSObject *start, JSProtoKey protoKey,
@@ -6135,17 +6131,17 @@ js_GetClassPrototype(JSContext *cx, JSOb
                 scopeobj = cx->globalObject;
                 if (!scopeobj) {
                     *protop = NULL;
                     return true;
                 }
             }
         }
         scopeobj = scopeobj->getGlobal();
-        if (scopeobj->getClass()->flags & JSCLASS_IS_GLOBAL) {
+        if (scopeobj->isGlobal()) {
             const Value &v = scopeobj->getReservedSlot(JSProto_LIMIT + protoKey);
             if (v.isObject()) {
                 *protop = &v.toObject();
                 return true;
             }
         }
     }
 
@@ -6398,18 +6394,17 @@ js_PrintObjectSlotName(JSTracer *trc, ch
         if (shape->slot != slot)
             shape = NULL;
     } else {
         shape = NULL;
     }
 
     if (!shape) {
         const char *slotname = NULL;
-        Class *clasp = obj->getClass();
-        if (clasp->flags & JSCLASS_IS_GLOBAL) {
+        if (obj->isGlobal()) {
 #define JS_PROTO(name,code,init)                                              \
     if ((code) == slot) { slotname = js_##name##_str; goto found; }
 #include "jsproto.tbl"
 #undef JS_PROTO
         }
       found:
         if (slotname)
             JS_snprintf(buf, bufsize, "CLASS_OBJECT(%s)", slotname);
--- a/js/src/jsobjinlines.h
+++ b/js/src/jsobjinlines.h
@@ -958,17 +958,17 @@ NewBuiltinClassInstance(JSContext *cx, C
     if (!cx->hasfp()) {
         global = cx->globalObject;
         OBJ_TO_INNER_OBJECT(cx, global);
         if (!global)
             return NULL;
     } else {
         global = cx->fp()->scopeChain().getGlobal();
     }
-    JS_ASSERT(global->getClass()->flags & JSCLASS_IS_GLOBAL);
+    JS_ASSERT(global->isGlobal());
 
     const Value &v = global->getReservedSlot(JSProto_LIMIT + protoKey);
     JSObject *proto;
     if (v.isObject()) {
         proto = &v.toObject();
         JS_ASSERT(proto->getParent() == global);
     } else {
         if (!FindClassPrototype(cx, global, protoKey, &proto, clasp))
--- a/js/src/jsproto.tbl
+++ b/js/src/jsproto.tbl
@@ -37,25 +37,21 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "jsversion.h"
 
 #if JS_HAS_XML_SUPPORT
 # define XML_INIT                       js_InitXMLClass
 # define NAMESPACE_INIT                 js_InitNamespaceClass
 # define QNAME_INIT                     js_InitQNameClass
-# define ANYNAME_INIT                   js_InitAnyNameClass
-# define ATTRIBUTE_INIT                 js_InitAttributeNameClass
 # define XMLFILTER_INIT                 js_InitXMLFilterClass
 #else
 # define XML_INIT                       js_InitNullClass
 # define NAMESPACE_INIT                 js_InitNullClass
 # define QNAME_INIT                     js_InitNullClass
-# define ANYNAME_INIT                   js_InitNullClass
-# define ATTRIBUTE_INIT                 js_InitNullClass
 # define XMLFILTER_INIT                 js_InitNullClass
 #endif
 
 #if JS_HAS_GENERATORS
 # define GENERATOR_INIT                 js_InitIteratorClasses
 #else
 # define GENERATOR_INIT                 js_InitNullClass
 #endif
@@ -74,18 +70,18 @@ JS_PROTO(JSON,                   5,     
 JS_PROTO(Date,                   6,     js_InitDateClass)
 JS_PROTO(Math,                   7,     js_InitMathClass)
 JS_PROTO(Number,                 8,     js_InitNumberClass)
 JS_PROTO(String,                 9,     js_InitStringClass)
 JS_PROTO(RegExp,                10,     js_InitRegExpClass)
 JS_PROTO(XML,                   11,     XML_INIT)
 JS_PROTO(Namespace,             12,     NAMESPACE_INIT)
 JS_PROTO(QName,                 13,     QNAME_INIT)
-JS_PROTO(AnyName,               14,     ANYNAME_INIT)
-JS_PROTO(AttributeName,         15,     ATTRIBUTE_INIT)
+JS_PROTO(Reflect,               14,     js_InitReflectClass)
+JS_PROTO(ASTNode,               15,     js_InitReflectClass)
 JS_PROTO(Error,                 16,     js_InitExceptionClasses)
 JS_PROTO(InternalError,         17,     js_InitExceptionClasses)
 JS_PROTO(EvalError,             18,     js_InitExceptionClasses)
 JS_PROTO(RangeError,            19,     js_InitExceptionClasses)
 JS_PROTO(ReferenceError,        20,     js_InitExceptionClasses)
 JS_PROTO(SyntaxError,           21,     js_InitExceptionClasses)
 JS_PROTO(TypeError,             22,     js_InitExceptionClasses)
 JS_PROTO(URIError,              23,     js_InitExceptionClasses)
@@ -98,17 +94,13 @@ JS_PROTO(Uint8Array,            29,     
 JS_PROTO(Int16Array,            30,     js_InitTypedArrayClasses)
 JS_PROTO(Uint16Array,           31,     js_InitTypedArrayClasses)
 JS_PROTO(Int32Array,            32,     js_InitTypedArrayClasses)
 JS_PROTO(Uint32Array,           33,     js_InitTypedArrayClasses)
 JS_PROTO(Float32Array,          34,     js_InitTypedArrayClasses)
 JS_PROTO(Float64Array,          35,     js_InitTypedArrayClasses)
 JS_PROTO(Uint8ClampedArray,     36,     js_InitTypedArrayClasses)
 JS_PROTO(Proxy,                 37,     js_InitProxyClass)
-JS_PROTO(Reflect,               38,     js_InitReflectClass)
-JS_PROTO(ASTNode,               39,     js_InitReflectClass)
 
 #undef XML_INIT
 #undef NAMESPACE_INIT
 #undef QNAME_INIT
-#undef ANYNAME_INIT
-#undef ATTRIBUTE_INIT
 #undef GENERATOR_INIT
--- a/js/src/jstracer.cpp
+++ b/js/src/jstracer.cpp
@@ -6487,17 +6487,17 @@ ScopeChainCheck(JSContext* cx, TreeFragm
             debug_only_print0(LC_TMTracer,"Blacklist: non-cacheable object on scope chain.\n");
             Blacklist((jsbytecode*) f->root->ip);
             return false;
         }
         child = parent;
     }
     JS_ASSERT(child == f->globalObj);
 
-    if (!(f->globalObj->getClass()->flags & JSCLASS_IS_GLOBAL)) {
+    if (!f->globalObj->isGlobal()) {
         debug_only_print0(LC_TMTracer, "Blacklist: non-global at root of scope chain.\n");
         Blacklist((jsbytecode*) f->root->ip);
         return false;
     }
 
     return true;
 }
 
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -134,16 +134,18 @@ static struct {
 #else
 #define METER(x)        /* nothing */
 #define UNMETER(x)      /* nothing */
 #endif
 
 /*
  * Random utilities and global functions.
  */
+const char js_AttributeName_str[] = "AttributeName";
+const char js_AnyName_str[]       = "AnyName";
 const char js_isXMLName_str[]     = "isXMLName";
 const char js_XMLList_str[]       = "XMLList";
 const char js_localName_str[]     = "localName";
 const char js_xml_parent_str[]    = "parent";
 const char js_prefix_str[]        = "prefix";
 const char js_toXMLString_str[]   = "toXMLString";
 const char js_uri_str[]           = "uri";
 
@@ -388,31 +390,31 @@ JS_FRIEND_DATA(Class) js_QNameClass = {
  * are like QName, except that they have no property getters.  They share the
  * qname_toString method, and therefore are exposed as constructable objects
  * in this implementation.
  */
 JS_FRIEND_DATA(Class) js_AttributeNameClass = {
     js_AttributeName_str,
     JSCLASS_CONSTRUCT_PROTOTYPE |
     JSCLASS_HAS_RESERVED_SLOTS(JSObject::QNAME_CLASS_RESERVED_SLOTS) |
-    JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_AttributeName),
+    JSCLASS_MARK_IS_TRACE | JSCLASS_IS_ANONYMOUS,
     PropertyStub,   /* addProperty */
     PropertyStub,   /* delProperty */
     PropertyStub,   /* getProperty */
     PropertyStub,   /* setProperty */
     EnumerateStub,
     ResolveStub,
     ConvertStub
 };
 
 JS_FRIEND_DATA(Class) js_AnyNameClass = {
     js_AnyName_str,
     JSCLASS_CONSTRUCT_PROTOTYPE |
     JSCLASS_HAS_RESERVED_SLOTS(JSObject::QNAME_CLASS_RESERVED_SLOTS) |
-    JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_AnyName),
+    JSCLASS_MARK_IS_TRACE | JSCLASS_IS_ANONYMOUS,
     PropertyStub,   /* addProperty */
     PropertyStub,   /* delProperty */
     PropertyStub,   /* getProperty */
     PropertyStub,   /* setProperty */
     EnumerateStub,
     ResolveStub,
     ConvertStub,
     anyname_finalize
@@ -421,102 +423,118 @@ JS_FRIEND_DATA(Class) js_AnyNameClass = 
 #define QNAME_ATTRS (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
 
 static JSPropertySpec qname_props[] = {
     {js_uri_str,       0, QNAME_ATTRS, QNameNameURI_getter,   0},
     {js_localName_str, 0, QNAME_ATTRS, QNameLocalName_getter, 0},
     {0,0,0,0,0}
 };
 
-static JSBool
-qname_toString(JSContext *cx, uintN argc, Value *vp)
-{
-    JSObject *obj;
-    Class *clasp;
-    JSString *uri, *str, *qualstr;
-    size_t length;
-    jschar *chars;
-
-    obj = ComputeThisFromVp(cx, vp);
-    if (!obj)
-        return JS_FALSE;
-    clasp = obj->getClass();
-    if (clasp != &js_AttributeNameClass &&
-        clasp != &js_AnyNameClass &&
-        !JS_InstanceOf(cx, obj, Jsvalify(&js_QNameClass), Jsvalify(vp + 2))) {
-            return JS_FALSE;
-    }
-
-    uri = obj->getNameURI();
+static JSString *
+ConvertQNameToString(JSContext *cx, JSObject *obj)
+{
+    JS_ASSERT(obj->isQName());
+    JSString *uri = obj->getNameURI();
+    JSString *str;
     if (!uri) {
         /* No uri means wildcard qualifier. */
         str = ATOM_TO_STRING(cx->runtime->atomState.starQualifierAtom);
     } else if (uri->empty()) {
         /* Empty string for uri means localName is in no namespace. */
         str = cx->runtime->emptyString;
     } else {
-        qualstr = ATOM_TO_STRING(cx->runtime->atomState.qualifierAtom);
+        JSString *qualstr = ATOM_TO_STRING(cx->runtime->atomState.qualifierAtom);
         str = js_ConcatStrings(cx, uri, qualstr);
         if (!str)
-            return JS_FALSE;
+            return NULL;
     }
     str = js_ConcatStrings(cx, str, obj->getQNameLocalName());
     if (!str)
-        return JS_FALSE;
-
-    if (str && clasp == &js_AttributeNameClass) {
-        length = str->length();
-        chars = (jschar *) cx->malloc((length + 2) * sizeof(jschar));
+        return NULL;
+
+    if (obj->getClass() == &js_AttributeNameClass) {
+        size_t length = str->length();
+        jschar *chars = (jschar *) cx->malloc((length + 2) * sizeof(jschar));
         if (!chars)
             return JS_FALSE;
         *chars = '@';
         const jschar *strChars = str->getChars(cx);
         if (!strChars) {
             cx->free(chars);
-            return JS_FALSE;
+            return NULL;
         }
         js_strncpy(chars + 1, strChars, length);
         chars[++length] = 0;
         str = js_NewString(cx, chars, length);
         if (!str) {
             cx->free(chars);
-            return JS_FALSE;
-        }
-    }
+            return NULL;
+        }
+    }
+    return str;
+}
+
+static JSBool
+qname_toString(JSContext *cx, uintN argc, Value *vp)
+{
+    JSObject *obj = ComputeThisFromVp(cx, vp);
+    if (!obj || !InstanceOf(cx, obj, &js_QNameClass, vp + 2))
+        return false;
+
+    JSString *str = ConvertQNameToString(cx, obj);
+    if (!str)
+        return false;
 
     vp->setString(str);
-    return JS_TRUE;
+    return true;
 }
 
 static JSFunctionSpec qname_methods[] = {
     JS_FN(js_toString_str,  qname_toString,    0,0),
     JS_FS_END
 };
 
 
 static void
 InitXMLQName(JSObject *obj, JSLinearString *uri, JSLinearString *prefix,
              JSLinearString *localName)
 {
+    JS_ASSERT(obj->isQName());
     JS_ASSERT(JSVAL_IS_VOID(obj->getNamePrefixVal()));
     JS_ASSERT(JSVAL_IS_VOID(obj->getNameURIVal()));
     JS_ASSERT(JSVAL_IS_VOID(obj->getQNameLocalNameVal()));
     if (uri)
         obj->setNameURI(uri);
     if (prefix)
         obj->setNamePrefix(prefix);
     if (localName)
         obj->setQNameLocalName(localName);
 }
 
 static JSObject *
 NewXMLQName(JSContext *cx, JSLinearString *uri, JSLinearString *prefix,
-            JSLinearString *localName, Class *clasp = &js_QNameClass)
-{
-    JSObject *obj = NewBuiltinClassInstanceXML(cx, clasp);
+            JSLinearString *localName)
+{
+    JSObject *obj = NewBuiltinClassInstanceXML(cx, &js_QNameClass);
+    if (!obj)
+        return NULL;
+    InitXMLQName(obj, uri, prefix, localName);
+    METER(xml_stats.qname);
+    return obj;
+}
+
+static JSObject *
+NewXMLAttributeName(JSContext *cx, JSLinearString *uri, JSLinearString *prefix,
+                    JSLinearString *localName)
+{
+    /*
+     * AttributeName is an internal anonymous class which instances are not
+     * exposed to scripts.
+     */
+    JSObject *obj = NewNonFunction<WithProto::Given>(cx, &js_AttributeNameClass, NULL, NULL);
     if (!obj)
         return NULL;
     JS_ASSERT(obj->isQName());
     InitXMLQName(obj, uri, prefix, localName);
     METER(xml_stats.qname);
     return obj;
 }
 
@@ -704,27 +722,24 @@ Namespace(JSContext *cx, uintN argc, Val
     return NamespaceHelper(cx, thisobj, argc, Jsvalify(vp + 2), Jsvalify(vp));
 }
 
 /*
  * When argc is -1, it indicates argv is empty but the code should behave as
  * if argc is 1 and argv[0] is JSVAL_VOID.
  */
 static JSBool
-QNameHelper(JSContext *cx, JSObject *obj, Class *clasp, intN argc,
-            jsval *argv, jsval *rval)
+QNameHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv, jsval *rval)
 {
     jsval nameval, nsval;
     JSBool isQName, isNamespace;
     JSObject *qn;
     JSLinearString *uri, *prefix, *name;
     JSObject *obj2;
 
-    JS_ASSERT(clasp == &js_QNameClass ||
-              clasp == &js_AttributeNameClass);
     if (argc <= 0) {
         nameval = JSVAL_VOID;
         isQName = JS_FALSE;
     } else {
         nameval = argv[argc > 1];
         isQName =
             !JSVAL_IS_PRIMITIVE(nameval) &&
             JSVAL_TO_OBJECT(nameval)->getClass() == &js_QNameClass;
@@ -733,21 +748,18 @@ QNameHelper(JSContext *cx, JSObject *obj
     if (!obj) {
         /* QName called as function. */
         if (argc == 1 && isQName) {
             /* QName called with one QName argument is identity. */
             *rval = nameval;
             return JS_TRUE;
         }
 
-        /*
-         * Create and return a new QName or AttributeName object exactly as if
-         * constructed.
-         */
-        obj = NewBuiltinClassInstanceXML(cx, clasp);
+        /* Create and return a new QName object exactly as if constructed. */
+        obj = NewBuiltinClassInstanceXML(cx, &js_QNameClass);
         if (!obj)
             return JS_FALSE;
     }
     *rval = OBJECT_TO_JSVAL(obj);
     METER(xml_stats.qname);
 
     if (isQName) {
         /* If namespace is not specified and name is a QName, clone it. */
@@ -798,19 +810,18 @@ QNameHelper(JSContext *cx, JSObject *obj
          * nsval passed as the only argument, to compute the uri and prefix
          * for the constructed namespace, without actually allocating the
          * object or computing other members.  See ECMA-357 13.3.2 6(a) and
          * 13.2.2.
          */
         isNamespace = isQName = JS_FALSE;
         if (!JSVAL_IS_PRIMITIVE(nsval)) {
             obj2 = JSVAL_TO_OBJECT(nsval);
-            clasp = obj2->getClass();
-            isNamespace = (clasp == &js_NamespaceClass);
-            isQName = (clasp == &js_QNameClass);
+            isNamespace = (obj2->getClass() == &js_NamespaceClass);
+            isQName = (obj2->getClass() == &js_QNameClass);
         }
 #ifdef __GNUC__         /* suppress bogus gcc warnings */
         else obj2 = NULL;
 #endif
 
         if (isNamespace) {
             uri = obj2->getNameURI();
             prefix = obj2->getNamePrefix();
@@ -837,25 +848,17 @@ out:
     return JS_TRUE;
 }
 
 static JSBool
 QName(JSContext *cx, uintN argc, Value *vp)
 {
     JSObject *thisobj = NULL;
     (void)IsConstructing_PossiblyWithGivenThisObject(vp, &thisobj);
-    return QNameHelper(cx, thisobj, &js_QNameClass, argc, Jsvalify(vp + 2), Jsvalify(vp));
-}
-
-static JSBool
-AttributeName(JSContext *cx, uintN argc, Value *vp)
-{
-    JSObject *thisobj = NULL;
-    (void)IsConstructing_PossiblyWithGivenThisObject(vp, &thisobj);
-    return QNameHelper(cx, thisobj, &js_AttributeNameClass, argc, Jsvalify(vp + 2), Jsvalify(vp));
+    return QNameHelper(cx, thisobj, argc, Jsvalify(vp + 2), Jsvalify(vp));
 }
 
 /*
  * XMLArray library functions.
  */
 static JSBool
 namespace_identity(const void *a, const void *b)
 {
@@ -2818,17 +2821,17 @@ ToAttributeName(JSContext *cx, jsval v)
                 name = str->ensureLinear(cx);
                 if (!name)
                     return NULL;
             }
             uri = prefix = cx->runtime->emptyString;
         }
     }
 
-    qn = NewXMLQName(cx, uri, prefix, name, &js_AttributeNameClass);
+    qn = NewXMLAttributeName(cx, uri, prefix, name);
     if (!qn)
         return NULL;
     return qn;
 }
 
 static void
 ReportBadXMLName(JSContext *cx, const Value &idval)
 {
@@ -4056,19 +4059,18 @@ PutProperty(JSContext *cx, JSObject *obj
         /* 2(e). */
         kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
         if (!kid)
             goto out;
         parent = kid->parent;
         if (kid->xml_class == JSXML_CLASS_ATTRIBUTE) {
             nameobj = kid->name;
             if (nameobj->getClass() != &js_AttributeNameClass) {
-                nameobj = NewXMLQName(cx, nameobj->getNameURI(), nameobj->getNamePrefix(),
-                                      nameobj->getQNameLocalName(),
-                                      &js_AttributeNameClass);
+                nameobj = NewXMLAttributeName(cx, nameobj->getNameURI(), nameobj->getNamePrefix(),
+                                              nameobj->getQNameLocalName());
                 if (!nameobj)
                     goto bad;
             }
             id = OBJECT_TO_JSID(nameobj);
 
             if (parent) {
                 /* 2(e)(i). */
                 parentobj = js_GetXMLObject(cx, parent);
@@ -6377,20 +6379,18 @@ xml_replace(JSContext *cx, uintN argc, j
             return JS_FALSE;
     }
 
     if (!haveIndex) {
         /*
          * Call function QName per spec, not ToXMLName, to avoid attribute
          * names.
          */
-        if (!QNameHelper(cx, NULL, &js_QNameClass, argc == 0 ? -1 : 1,
-                         vp + 2, vp)) {
+        if (!QNameHelper(cx, NULL, argc == 0 ? -1 : 1, vp + 2, vp))
             return JS_FALSE;
-        }
         JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp));
         nameqn = JSVAL_TO_OBJECT(*vp);
 
         i = xml->xml_kids.length;
         index = XML_NOT_FOUND;
         while (i != 0) {
             --i;
             kid = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
@@ -7116,33 +7116,16 @@ js_InitNamespaceClass(JSContext *cx, JSO
 JSObject *
 js_InitQNameClass(JSContext *cx, JSObject *obj)
 {
     return js_InitClass(cx, obj, NULL, &js_QNameClass, QName, 2,
                         qname_props, qname_methods, NULL, NULL);
 }
 
 JSObject *
-js_InitAttributeNameClass(JSContext *cx, JSObject *obj)
-{
-    return js_InitClass(cx, obj, NULL, &js_AttributeNameClass, AttributeName, 2,
-                        qname_props, qname_methods, NULL, NULL);
-}
-
-JSObject *
-js_InitAnyNameClass(JSContext *cx, JSObject *obj)
-{
-    jsid id;
-
-    if (!js_GetAnyName(cx, &id))
-        return NULL;
-    return JSID_TO_OBJECT(id);
-}
-
-JSObject *
 js_InitXMLClass(JSContext *cx, JSObject *obj)
 {
     JSObject *proto, *pobj;
     JSFunction *fun;
     JSXML *xml;
     JSProperty *prop;
     Shape *shape;
     jsval cval, vp[3];
@@ -7201,20 +7184,16 @@ js_InitXMLClass(JSContext *cx, JSObject 
 
 JSObject *
 js_InitXMLClasses(JSContext *cx, JSObject *obj)
 {
     if (!js_InitNamespaceClass(cx, obj))
         return NULL;
     if (!js_InitQNameClass(cx, obj))
         return NULL;
-    if (!js_InitAttributeNameClass(cx, obj))
-        return NULL;
-    if (!js_InitAnyNameClass(cx, obj))
-        return NULL;
     return js_InitXMLClass(cx, obj);
 }
 
 JSBool
 js_GetFunctionNamespace(JSContext *cx, Value *vp)
 {
     JSObject *obj;
     JSLinearString *prefix, *uri;
@@ -7374,50 +7353,34 @@ js_EscapeElementValue(JSContext *cx, JSS
 }
 
 JSString *
 js_ValueToXMLString(JSContext *cx, const Value &v)
 {
     return ToXMLString(cx, Jsvalify(v), 0);
 }
 
-static JSBool
-anyname_toString(JSContext *cx, uintN argc, jsval *vp)
-{
-    *vp = ATOM_TO_JSVAL(cx->runtime->atomState.starAtom);
-    return JS_TRUE;
-}
-
 JSBool
 js_GetAnyName(JSContext *cx, jsid *idp)
 {
-    JSObject *obj;
-
-    obj = cx->compartment->anynameObject;
+    JSObject *obj = cx->compartment->anynameObject;
     if (!obj) {
-        JSRuntime *rt = cx->runtime;
-
+        /*
+         * Avoid entraining any Object.prototype found via cx's scope
+         * chain or global object for this internal AnyName object.
+         */
         obj = NewNonFunction<WithProto::Given>(cx, &js_AnyNameClass, NULL, NULL);
         if (!obj)
             return false;
 
+        JSRuntime *rt = cx->runtime;
         InitXMLQName(obj, rt->emptyString, rt->emptyString,
                      ATOM_TO_STRING(rt->atomState.starAtom));
         METER(xml_stats.qname);
 
-        /*
-         * Avoid entraining any Object.prototype found via cx's scope
-         * chain or global object.  This loses the default toString,
-         * but no big deal: we want to customize toString anyway for
-         * clearer diagnostics.
-         */
-        if (!JS_DefineFunction(cx, obj, js_toString_str,
-                               anyname_toString, 0, 0))
-            return false;
-
         JS_ASSERT(!obj->getProto());
         JS_ASSERT(!obj->getParent());
 
         cx->compartment->anynameObject = obj;
     }
 
     *idp = OBJECT_TO_JSID(obj);
     return true;
@@ -7483,17 +7446,18 @@ js_FindXMLProperty(JSContext *cx, const 
                 *idp = funid;
                 *objp = target;
                 return JS_TRUE;
             }
         }
     } while ((obj = obj->getParent()) != NULL);
 
     JSAutoByteString printable;
-    if (js_ValueToPrintable(cx, ObjectValue(*nameobj), &printable)) {
+    JSString *str = ConvertQNameToString(cx, nameobj);
+    if (str && js_ValueToPrintable(cx, StringValue(str), &printable)) {
         JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
                                      JSMSG_UNDEFINED_XML_NAME, printable.ptr());
     }
     return JS_FALSE;
 }
 
 static JSBool
 GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp)
--- a/js/src/jsxml.h
+++ b/js/src/jsxml.h
@@ -269,22 +269,16 @@ IsXML(const js::Value &v)
 
 extern JSObject *
 js_InitNamespaceClass(JSContext *cx, JSObject *obj);
 
 extern JSObject *
 js_InitQNameClass(JSContext *cx, JSObject *obj);
 
 extern JSObject *
-js_InitAttributeNameClass(JSContext *cx, JSObject *obj);
-
-extern JSObject *
-js_InitAnyNameClass(JSContext *cx, JSObject *obj);
-
-extern JSObject *
 js_InitXMLClass(JSContext *cx, JSObject *obj);
 
 extern JSObject *
 js_InitXMLClasses(JSContext *cx, JSObject *obj);
 
 extern JSBool
 js_GetFunctionNamespace(JSContext *cx, js::Value *vp);
 
--- a/js/src/tests/e4x/QName/jstests.list
+++ b/js/src/tests/e4x/QName/jstests.list
@@ -1,8 +1,9 @@
 url-prefix ../../jsreftest.html?test=e4x/QName/
 script 13.3.1.js
 script 13.3.2.js
 script 13.3.5.js
 script regress-373595-01.js
 script regress-373595-02.js
 script regress-373595-03.js
 script regress-444608.js
+script regress-619529.js
new file mode 100644
--- /dev/null
+++ b/js/src/tests/e4x/QName/regress-619529.js
@@ -0,0 +1,17 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * Any copyright is dedicated to the Public Domain.
+ * http://creativecommons.org/licenses/publicdomain/
+ */
+
+var b = Proxy.create({ enumerateOwn: function () { @f; }});
+Object.freeze(this);
+
+try {
+    @r;
+    throw 1; // should not be reached
+} catch (e) {
+    assertEq(e instanceof ReferenceError, true);
+}
+
+reportCompare(0, 0, "ok");
--- a/js/src/tests/ecma_5/extensions/Object-keys-and-object-ids.js
+++ b/js/src/tests/ecma_5/extensions/Object-keys-and-object-ids.js
@@ -1,12 +1,12 @@
 var o = { normal:"a" };
-Object.defineProperty(o, new AttributeName, { enumerable:true });
+Object.defineProperty(o, new QName, { enumerable:true });
 var keys = Object.keys(o);
 assertEq(keys.length, 1);
 assertEq(keys[0], "normal");
 
 var o = {};
-Object.defineProperty(o, new AttributeName, { enumerable:true });
+Object.defineProperty(o, new QName, { enumerable:true });
 var keys = Object.keys(o);
 assertEq(keys.length, 0);
 
 reportCompare(true, true);
--- a/js/src/tests/js1_8_5/extensions/clone-object.js
+++ b/js/src/tests/js1_8_5/extensions/clone-object.js
@@ -219,15 +219,15 @@ function test() {
                            configurable: true,
                            writable: true,
                            value: 3}});
     check(b, "selfModifyingObject");
 
     // Ignore properties with object-ids.
     var uri = "http://example.net";
     b = {x: 1, y: 2};
-    Object.defineProperty(b, AttributeName(uri, "x"), {enumerable: true, value: 3});
+    Object.defineProperty(b, QName(uri, "x"), {enumerable: true, value: 3});
     Object.defineProperty(b, QName(uri, "y"), {enumerable: true, value: 5});
     check(b);
 }
 
 test();
 reportCompare(0, 0, 'ok');