diff --git a/js/src/jsxml.c b/js/src/jsxml.c index 87f365def0a6..e185d6c71c41 100644 --- a/js/src/jsxml.c +++ b/js/src/jsxml.c @@ -4056,34 +4056,72 @@ SyncInScopeNamespaces(JSContext *cx, JSXML *xml) return JS_TRUE; } +static JSBool +GetNamedProperty(JSContext *cx, JSXML *xml, JSXMLQName* nameqn, + JSBool attributes, JSXML *list) +{ + JSXMLArray *array; + JSXMLNameMatcher matcher; + JSXMLArrayCursor cursor; + JSXML *kid; + JSBool ok; + + if (!JSXML_HAS_KIDS(xml)) + return JS_TRUE; + + if (attributes) { + array = &xml->xml_attrs; + matcher = MatchAttrName; + } else { + array = &xml->xml_kids; + matcher = MatchElemName; + } + + XMLArrayCursorInit(&cursor, array); + while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { + if (matcher(nameqn, kid)) { + if (!attributes && kid->xml_class == JSXML_CLASS_ELEMENT) { + ok = SyncInScopeNamespaces(cx, kid); + if (!ok) + goto out; + } + ok = Append(cx, list, kid); + if (!ok) + goto out; + } + } + ok = JS_TRUE; + + out: + XMLArrayCursorFinish(&cursor); + return ok; +} + /* ECMA-357 9.1.1.1 XML [[Get]] and 9.2.1.1 XMLList [[Get]]. */ static JSBool GetProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp) { JSXML *xml, *list, *kid; uint32 index; - JSObject *kidobj, *listobj, *nameobj; + JSObject *kidobj, *listobj; JSXMLQName *nameqn; jsid funid; - JSBool ok; + jsval roots[2]; + JSTempValueRooter tvr; + JSBool attributes; JSXMLArrayCursor cursor; - jsval kidval; - JSXMLArray *array; - JSXMLNameMatcher matcher; xml = (JSXML *) JS_GetInstancePrivate(cx, obj, &js_XMLClass, NULL); if (!xml) return JS_TRUE; -#ifdef __GNUC__ - list = NULL; /* quell GCC overwarning */ -#endif - -retry: - if (xml->xml_class == JSXML_CLASS_LIST) { - /* ECMA-357 9.2.1.1 starts here. */ - if (js_IdIsIndex(id, &index)) { + if (js_IdIsIndex(id, &index)) { + if (xml->xml_class != JSXML_CLASS_LIST) { + *vp = (index == 0) ? OBJECT_TO_JSVAL(obj) : JSVAL_VOID; + } else { /* + * ECMA-357 9.2.1.1 starts here. + * * Erratum: 9.2 is not completely clear that indexed properties * correspond to kids, but that's what it seems to say, and it's * what any sane user would want. @@ -4102,133 +4140,61 @@ retry: } else { *vp = JSVAL_VOID; } - return JS_TRUE; } + return JS_TRUE; + } - nameqn = ToXMLName(cx, id, &funid); - if (!nameqn) - return JS_FALSE; - if (funid) - return GetFunction(cx, obj, xml, funid, vp); + /* + * ECMA-357 9.2.1.1/9.1.1.1 qname case. + */ + nameqn = ToXMLName(cx, id, &funid); + if (!nameqn) + return JS_FALSE; + if (funid) + return GetFunction(cx, obj, xml, funid, vp); - /* - * Recursion through GetProperty may allocate more list objects, so - * we make use of local root scopes here. Each new allocation will - * push the newborn onto the local root stack. - */ - ok = js_EnterLocalRootScope(cx); - if (!ok) - return JS_FALSE; + roots[0] = OBJECT_TO_JSVAL(nameqn->object); + JS_PUSH_TEMP_ROOT(cx, 1, roots, &tvr); - /* - * NB: nameqn is already protected from GC by cx->newborn[GCX_OBJECT] - * until listobj is created. After that, a local root keeps listobj - * alive, and listobj's private keeps nameqn alive via targetprop. - */ - listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (!listobj) { - ok = JS_FALSE; - } else { - list = (JSXML *) JS_GetPrivate(cx, listobj); - list->xml_target = xml; + listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); + if (listobj) { + roots[1] = OBJECT_TO_JSVAL(listobj); + tvr.count++; + list = (JSXML *) JS_GetPrivate(cx, listobj); + attributes = (OBJ_GET_CLASS(cx, nameqn->object) == + &js_AttributeNameClass); + + if (xml->xml_class == JSXML_CLASS_LIST) { XMLArrayCursorInit(&cursor, &xml->xml_kids); while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - if (kid->xml_class == JSXML_CLASS_ELEMENT) { - kidobj = js_GetXMLObject(cx, kid); - if (!kidobj) { - ok = JS_FALSE; - break; - } - ok = GetProperty(cx, kidobj, id, &kidval); - if (!ok) - break; - kidobj = JSVAL_TO_OBJECT(kidval); - kid = (JSXML *) JS_GetPrivate(cx, kidobj); - if (JSXML_LENGTH(kid) > 0) { - ok = Append(cx, list, kid); - if (!ok) - break; - } + if (kid->xml_class == JSXML_CLASS_ELEMENT && + !GetNamedProperty(cx, kid, nameqn, attributes, list)) { + listobj = NULL; + break; } } XMLArrayCursorFinish(&cursor); + } else { + if (!GetNamedProperty(cx, xml, nameqn, attributes, list)) + listobj = NULL; } - } else { - /* ECMA-357 9.1.1.1 starts here. */ - if (js_IdIsIndex(id, &index)) { - obj = ToXMLList(cx, OBJECT_TO_JSVAL(obj)); - if (!obj) - return JS_FALSE; - xml = (JSXML *) JS_GetPrivate(cx, obj); - goto retry; - } - - nameqn = ToXMLName(cx, id, &funid); - if (!nameqn) - return JS_FALSE; - if (funid) - return GetFunction(cx, obj, xml, funid, vp); - nameobj = nameqn->object; /* - * Recursion through GetProperty may allocate more list objects, so - * we make use of local root scopes here. Each new allocation will - * push the newborn onto the local root stack. + * Erratum: ECMA-357 9.1.1.1 misses that [[Append]] sets the given + * list's [[TargetProperty]] to the property that is being appended. + * This means that any use of the internal [[Get]] property returns + * a list which, when used by e.g. [[Insert]] duplicates the last + * element matched by id. + * See bug 336921. */ - ok = js_EnterLocalRootScope(cx); - if (!ok) - return JS_FALSE; - - listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST); - if (!listobj) { - ok = JS_FALSE; - } else { - list = (JSXML *) JS_GetPrivate(cx, listobj); - list->xml_target = xml; - - if (JSXML_HAS_KIDS(xml)) { - if (OBJ_GET_CLASS(cx, nameobj) == &js_AttributeNameClass) { - array = &xml->xml_attrs; - matcher = MatchAttrName; - } else { - array = &xml->xml_kids; - matcher = MatchElemName; - } - XMLArrayCursorInit(&cursor, array); - while ((kid = (JSXML *) XMLArrayCursorNext(&cursor)) != NULL) { - if (matcher(nameqn, kid)) { - if (array == &xml->xml_kids && - kid->xml_class == JSXML_CLASS_ELEMENT) { - ok = SyncInScopeNamespaces(cx, kid); - if (!ok) - break; - } - ok = Append(cx, list, kid); - if (!ok) - break; - } - } - XMLArrayCursorFinish(&cursor); - } - } + list->xml_target = xml; + list->xml_targetprop = nameqn; + *vp = OBJECT_TO_JSVAL(listobj); } - /* Common tail code for list and non-list cases. */ - js_LeaveLocalRootScopeWithResult(cx, (jsval) listobj); - if (!ok) - return JS_FALSE; - - /* - * Erratum: ECMA-357 9.1.1.1 misses that [[Append]] sets the given list's - * [[TargetProperty]] to the property that is being appended. This means - * that any use of the internal [[Get]] property returns a list which, - * when used by e.g. [[Insert]] duplicates the last element matched by id. - * See bug 336921. - */ - list->xml_targetprop = nameqn; - *vp = OBJECT_TO_JSVAL(listobj); - return JS_TRUE; + JS_POP_TEMP_ROOT(cx, &tvr); + return listobj != NULL; } static JSXML *