[Bug 444608] optimizing E4X constructor calls. r=brendan
authorIgor Bukanov <igor@mir2.org>
Thu, 17 Jul 2008 16:19:30 +0200
changeset 16012 fc4af4573716a012fa302592df7997f014009c30
parent 16011 bee72059de1597d0f4d8428a55a002effc710e53
child 16015 8c360ab14ee456c8e628863230bb23ca4b997a0b
push idunknown
push userunknown
push dateunknown
reviewersbrendan
bugs444608
milestone1.9.1a1pre
[Bug 444608] optimizing E4X constructor calls. r=brendan
js/src/jsxml.cpp
--- a/js/src/jsxml.cpp
+++ b/js/src/jsxml.cpp
@@ -723,17 +723,18 @@ js_IsXMLName(JSContext *cx, jsval v)
             return JS_FALSE;
         }
     }
 
     return IsXMLName(JSSTRING_CHARS(name), JSSTRING_LENGTH(name));
 }
 
 static JSBool
-Namespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+NamespaceHelper(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
+                jsval *rval)
 {
     jsval urival, prefixval;
     JSObject *uriobj;
     JSBool isNamespace, isQName;
     JSClass *clasp;
     JSString *empty, *prefix;
     JSXMLNamespace *ns, *ns2;
     JSXMLQName *qn;
@@ -745,17 +746,17 @@ Namespace(JSContext *cx, JSObject *obj, 
         clasp = OBJ_GET_CLASS(cx, uriobj);
         isNamespace = (clasp == &js_NamespaceClass.base);
         isQName = (clasp == &js_QNameClass.base);
     }
 #ifdef __GNUC__         /* suppress bogus gcc warnings */
     else uriobj = NULL;
 #endif
 
-    if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
+    if (!obj) {
         /* Namespace called as function. */
         if (argc == 1 && isNamespace) {
             /* Namespace called with one Namespace argument is identity. */
             *rval = urival;
             return JS_TRUE;
         }
 
         /* Create and return a new QName object exactly as if constructed. */
@@ -832,47 +833,54 @@ Namespace(JSContext *cx, JSObject *obj, 
             ns->prefix = prefix;
         }
     }
 
     return JS_TRUE;
 }
 
 static JSBool
-QName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+Namespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+{
+    return NamespaceHelper(cx,
+                           (cx->fp->flags & JSFRAME_CONSTRUCTING) ? obj : NULL,
+                           argc, argv, rval);
+}
+
+static JSBool
+QNameHelper(JSContext *cx, JSObject *obj, JSClass *clasp, uintN argc,
+            jsval *argv, jsval *rval)
 {
     jsval nameval, nsval;
     JSBool isQName, isNamespace;
     JSXMLQName *qn;
     JSString *uri, *prefix, *name;
     JSObject *nsobj;
-    JSClass *clasp;
     JSXMLNamespace *ns;
 
+    JS_ASSERT(clasp == &js_QNameClass.base ||
+              clasp == &js_AttributeNameClass);
     nameval = argv[argc > 1];
     isQName =
         !JSVAL_IS_PRIMITIVE(nameval) &&
         OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nameval)) == &js_QNameClass.base;
 
-    if (!(cx->fp->flags & JSFRAME_CONSTRUCTING)) {
+    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 object exactly as if constructed.
-         * Use the constructor's clasp so we can be shared by AttributeName
-         * (see below after this function).
+         * Create and return a new QName or AttributeName object exactly as if
+         * constructed.
          */
-        obj = js_NewObject(cx,
-                           JS_ValueToFunction(cx, argv[-2])->u.n.clasp,
-                           NULL, NULL, 0);
+        obj = js_NewObject(cx, clasp, NULL, NULL, 0);
         if (!obj)
             return JS_FALSE;
         *rval = OBJECT_TO_JSVAL(obj);
     }
     METER(xml_stats.qnameobj);
     METER(xml_stats.liveqnameobj);
 
     if (isQName) {
@@ -957,24 +965,28 @@ out:
         return JS_FALSE;
     if (!JS_SetPrivate(cx, obj, qn))
         return JS_FALSE;
     qn->object = obj;
     return JS_TRUE;
 }
 
 static JSBool
+QName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
+{
+    return QNameHelper(cx, (cx->fp->flags & JSFRAME_CONSTRUCTING) ? obj : NULL,
+                       &js_QNameClass.base, argc, argv, rval);
+}
+
+static JSBool
 AttributeName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
               jsval *rval)
 {
-    /*
-     * Since js_AttributeNameClass was initialized, obj will have that as its
-     * class, not js_QNameClass.
-     */
-    return QName(cx, obj, argc, argv, rval);
+    return QNameHelper(cx, (cx->fp->flags & JSFRAME_CONSTRUCTING) ? obj : NULL,
+                       &js_AttributeNameClass, argc, argv, rval);
 }
 
 /*
  * XMLArray library functions.
  */
 static JSBool
 namespace_identity(const void *a, const void *b)
 {
@@ -5582,31 +5594,16 @@ JS_FRIEND_DATA(JSClass) js_XMLClass = {
     JSCLASS_HAS_PRIVATE | JSCLASS_MARK_IS_TRACE |
     JSCLASS_HAS_CACHED_PROTO(JSProto_XML),
     JS_PropertyStub,   JS_PropertyStub,   JS_PropertyStub,   JS_PropertyStub,
     JS_EnumerateStub,  JS_ResolveStub,    JS_ConvertStub,    xml_finalize,
     xml_getObjectOps,  NULL,              NULL,              NULL,
     NULL,              NULL,              JS_CLASS_TRACE(xml_trace), NULL
 };
 
-static JSObject *
-CallConstructorFunction(JSContext *cx, JSObject *obj, JSClass *clasp,
-                        uintN argc, jsval *argv)
-{
-    JSObject *tmp;
-    jsval rval;
-
-    while ((tmp = OBJ_GET_PARENT(cx, obj)) != NULL)
-        obj = tmp;
-    if (!JS_CallFunctionName(cx, obj, clasp->name, argc, argv, &rval))
-        return NULL;
-    JS_ASSERT(!JSVAL_IS_PRIMITIVE(rval));
-    return JSVAL_TO_OBJECT(rval);
-}
-
 static JSXML *
 StartNonListXMLMethod(JSContext *cx, jsval *vp, JSObject **objp)
 {
     JSXML *xml;
     JSFunction *fun;
     char numBuf[12];
 
     JS_ASSERT(VALUE_IS_FUNCTION(cx, *vp));
@@ -5647,36 +5644,35 @@ StartNonListXMLMethod(JSContext *cx, jsv
     JSXML *xml = StartNonListXMLMethod(cx, vp, &obj);                         \
     if (!xml)                                                                 \
         return JS_FALSE;                                                      \
     JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST)
 
 static JSBool
 xml_addNamespace(JSContext *cx, uintN argc, jsval *vp)
 {
-    JSObject *nsobj;
     JSXMLNamespace *ns;
 
     NON_LIST_XML_METHOD_PROLOG;
     if (xml->xml_class != JSXML_CLASS_ELEMENT)
-        return JS_TRUE;
+        goto done;
     xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
     if (!xml)
         return JS_FALSE;
 
-    nsobj = CallConstructorFunction(cx, obj, &js_NamespaceClass.base, 1,
-                                    vp + 2);
-    if (!nsobj)
+    if (!NamespaceHelper(cx, NULL, 1, vp + 2, vp))
         return JS_FALSE;
-    vp[2] = OBJECT_TO_JSVAL(nsobj);
-
-    ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj);
+    JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp));
+
+    ns = (JSXMLNamespace *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(*vp));
     if (!AddInScopeNamespace(cx, xml, ns))
         return JS_FALSE;
     ns->declared = JS_TRUE;
+
+  done:
     *vp = OBJECT_TO_JSVAL(obj);
     return JS_TRUE;
 }
 
 static JSBool
 xml_appendChild(JSContext *cx, uintN argc, jsval *vp)
 {
     jsval name, v;
@@ -6786,50 +6782,49 @@ xml_removeNamespace_helper(JSContext *cx
         }
     }
     return JS_TRUE;
 }
 
 static JSBool
 xml_removeNamespace(JSContext *cx, uintN argc, jsval *vp)
 {
-    JSObject *nsobj;
     JSXMLNamespace *ns;
 
     NON_LIST_XML_METHOD_PROLOG;
-    *vp = OBJECT_TO_JSVAL(obj);
     if (xml->xml_class != JSXML_CLASS_ELEMENT)
-        return JS_TRUE;
+        goto done;
     xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
     if (!xml)
         return JS_FALSE;
 
-    nsobj = CallConstructorFunction(cx, obj, &js_NamespaceClass.base, 1, vp + 2);
-    if (!nsobj)
+    if (!NamespaceHelper(cx, NULL, 1, vp + 2, vp))
         return JS_FALSE;
-    vp[2] = OBJECT_TO_JSVAL(nsobj);
-    ns = (JSXMLNamespace *) JS_GetPrivate(cx, nsobj);
+    JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp));
+    ns = (JSXMLNamespace *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(*vp));
 
     /* NOTE: remove ns from each ancestor if not used by that ancestor. */
-    return xml_removeNamespace_helper(cx, xml, ns);
+    if (!xml_removeNamespace_helper(cx, xml, ns))
+        return JS_FALSE;
+  done:
+    *vp = OBJECT_TO_JSVAL(obj);
+    return JS_TRUE;
 }
 
 static JSBool
 xml_replace(JSContext *cx, uintN argc, jsval *vp)
 {
-    jsval name, value;
+    jsval value;
     JSXML *vxml, *kid;
-    uint32 index, matchIndex;
-    JSObject *nameobj;
+    uint32 index, i;
     JSXMLQName *nameqn;
 
     NON_LIST_XML_METHOD_PROLOG;
-    *vp = OBJECT_TO_JSVAL(obj);
     if (xml->xml_class != JSXML_CLASS_ELEMENT)
-        return JS_TRUE;
+        goto done;
 
     value = vp[3];
     vxml = VALUE_IS_XML(cx, value)
            ? (JSXML *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(value))
            : NULL;
     if (!vxml) {
         if (!JS_ConvertValue(cx, value, JSTYPE_STRING, &vp[3]))
             return JS_FALSE;
@@ -6840,40 +6835,48 @@ xml_replace(JSContext *cx, uintN argc, j
             return JS_FALSE;
         value = vp[3] = OBJECT_TO_JSVAL(vxml->object);
     }
 
     xml = CHECK_COPY_ON_WRITE(cx, xml, obj);
     if (!xml)
         return JS_FALSE;
 
-    name = vp[2];
-    if (js_IdIsIndex(name, &index))
-        return Replace(cx, xml, index, value);
-
-    /* Call function QName per spec, not ToXMLName, to avoid attribute names. */
-    nameobj = CallConstructorFunction(cx, obj, &js_QNameClass.base, 1, &name);
-    if (!nameobj)
+    if (!js_IdIsIndex(vp[2], &index)) {
+        /*
+         * Call function QName per spec, not ToXMLName, to avoid attribute
+         * names.
+         */
+        if (!QNameHelper(cx, NULL, &js_QNameClass.base, 1, vp + 2, vp))
+            return JS_FALSE;
+        JS_ASSERT(!JSVAL_IS_PRIMITIVE(*vp));
+        nameqn = (JSXMLQName *) JS_GetPrivate(cx, 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);
+            if (kid && MatchElemName(nameqn, kid)) {
+                if (i != XML_NOT_FOUND)
+                    DeleteByIndex(cx, xml, i);
+                index = i;
+            }
+        }
+
+        if (index == XML_NOT_FOUND)
+            goto done;
+    }
+
+    if (!Replace(cx, xml, index, value))
         return JS_FALSE;
-    vp[2] = OBJECT_TO_JSVAL(nameobj);
-    nameqn = (JSXMLQName *) JS_GetPrivate(cx, nameobj);
-
-    index = xml->xml_kids.length;
-    matchIndex = XML_NOT_FOUND;
-    while (index != 0) {
-        --index;
-        kid = XMLARRAY_MEMBER(&xml->xml_kids, index, JSXML);
-        if (kid && MatchElemName(nameqn, kid)) {
-            if (matchIndex != XML_NOT_FOUND)
-                DeleteByIndex(cx, xml, matchIndex);
-            matchIndex = index;
-        }
-    }
-
-    return matchIndex == XML_NOT_FOUND || Replace(cx, xml, matchIndex, value);
+
+  done:
+    *vp = OBJECT_TO_JSVAL(obj);
+    return JS_TRUE;
 }
 
 static JSBool
 xml_setChildren(JSContext *cx, uintN argc, jsval *vp)
 {
     JSObject *obj;
 
     if (!StartNonListXMLMethod(cx, vp, &obj))