/[jscoverage]/trunk/js/jsxml.cpp
ViewVC logotype

Annotation of /trunk/js/jsxml.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 399 - (hide annotations)
Tue Dec 9 03:37:47 2008 UTC (11 years, 9 months ago) by siliconforks
File size: 251230 byte(s)
Use SpiderMonkey from Firefox 3.1b2.

1 siliconforks 332 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2     * vim: set ts=4 sw=4 et tw=78:
3     *
4     * ***** BEGIN LICENSE BLOCK *****
5     * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6     *
7     * The contents of this file are subject to the Mozilla Public License Version
8     * 1.1 (the "License"); you may not use this file except in compliance with
9     * the License. You may obtain a copy of the License at
10     * http://www.mozilla.org/MPL/
11     *
12     * Software distributed under the License is distributed on an "AS IS" basis,
13     * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
14     * for the specific language governing rights and limitations under the
15     * License.
16     *
17     * The Original Code is SpiderMonkey E4X code, released August, 2004.
18     *
19     * The Initial Developer of the Original Code is
20     * Netscape Communications Corporation.
21     * Portions created by the Initial Developer are Copyright (C) 1998
22     * the Initial Developer. All Rights Reserved.
23     *
24     * Contributor(s):
25     *
26     * Alternatively, the contents of this file may be used under the terms of
27     * either of the GNU General Public License Version 2 or later (the "GPL"),
28     * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29     * in which case the provisions of the GPL or the LGPL are applicable instead
30     * of those above. If you wish to allow use of your version of this file only
31     * under the terms of either the GPL or the LGPL, and not to allow others to
32     * use your version of this file under the terms of the MPL, indicate your
33     * decision by deleting the provisions above and replace them with the notice
34     * and other provisions required by the GPL or the LGPL. If you do not delete
35     * the provisions above, a recipient may use your version of this file under
36     * the terms of any one of the MPL, the GPL or the LGPL.
37     *
38     * ***** END LICENSE BLOCK ***** */
39    
40     #include "jsstddef.h"
41     #include "jsversion.h"
42    
43     #if JS_HAS_XML_SUPPORT
44    
45     #include <math.h>
46     #include <stdlib.h>
47     #include <string.h>
48     #include "jstypes.h"
49     #include "jsbit.h"
50     #include "jsprf.h"
51     #include "jsutil.h"
52     #include "jsapi.h"
53     #include "jsarray.h"
54     #include "jsatom.h"
55     #include "jsbool.h"
56     #include "jscntxt.h"
57     #include "jsfun.h"
58     #include "jsgc.h"
59     #include "jsinterp.h"
60     #include "jslock.h"
61     #include "jsnum.h"
62     #include "jsobj.h"
63     #include "jsopcode.h"
64     #include "jsparse.h"
65     #include "jsscan.h"
66     #include "jsscope.h"
67     #include "jsscript.h"
68     #include "jsstr.h"
69     #include "jsxml.h"
70     #include "jsstaticcheck.h"
71    
72     #ifdef DEBUG
73     #include <string.h> /* for #ifdef DEBUG memset calls */
74     #endif
75    
76     /*
77     * NOTES
78     * - in the js shell, you must use the -x command line option, or call
79     * options('xml') before compiling anything that uses XML literals
80     *
81     * TODO
82     * - XXXbe patrol
83     * - Fuse objects and their JSXML* private data into single GC-things
84     * - fix function::foo vs. x.(foo == 42) collision using proper namespacing
85     * - fix the !TCF_HAS_DEFXMLNS optimization in js_FoldConstants
86     * - JSCLASS_DOCUMENT_OBSERVER support -- live two-way binding to Gecko's DOM!
87     * - JS_TypeOfValue sure could use a cleaner interface to "types"
88     */
89    
90     #ifdef XML_METERING
91     static struct {
92     jsrefcount qname;
93     jsrefcount xmlnamespace;
94     jsrefcount xml;
95     jsrefcount xmlobj;
96     } xml_stats;
97    
98     #define METER(x) JS_ATOMIC_INCREMENT(&(x))
99     #define UNMETER(x) JS_ATOMIC_DECREMENT(&(x))
100     #else
101     #define METER(x) /* nothing */
102     #define UNMETER(x) /* nothing */
103     #endif
104    
105     /*
106     * Random utilities and global functions.
107     */
108     const char js_isXMLName_str[] = "isXMLName";
109     const char js_XMLList_str[] = "XMLList";
110     const char js_localName_str[] = "localName";
111     const char js_xml_parent_str[] = "parent";
112     const char js_prefix_str[] = "prefix";
113     const char js_toXMLString_str[] = "toXMLString";
114     const char js_uri_str[] = "uri";
115    
116     const char js_amp_entity_str[] = "&amp;";
117     const char js_gt_entity_str[] = "&gt;";
118     const char js_lt_entity_str[] = "&lt;";
119     const char js_quot_entity_str[] = "&quot;";
120    
121     #define IS_EMPTY(str) (JSSTRING_LENGTH(str) == 0)
122     #define IS_STAR(str) (JSSTRING_LENGTH(str) == 1 && *JSSTRING_CHARS(str) == '*')
123     /* Slot indexes shared between Namespace and QName objects. */
124     const uint32 JSSLOT_PREFIX = JSSLOT_PRIVATE;
125     const uint32 JSSLOT_URI = JSSLOT_PRIVATE + 1;
126    
127     /* Namespace-specific slot. */
128     const uint32 JSSLOT_DECLARED = JSSLOT_PRIVATE + 2;
129    
130     /* QName-specific slot. */
131     const uint32 JSSLOT_LOCAL_NAME = JSSLOT_PRIVATE + 2;
132    
133     const uint32 NAMESPACE_RESERVED_SLOTS = 3;
134     const uint32 QNAME_RESERVED_SLOTS = 3;
135    
136     static JSBool
137     IsQNameClass(JSClass *clasp)
138     {
139     return clasp == &js_QNameClass.base ||
140     clasp == &js_AttributeNameClass ||
141     clasp == &js_AnyNameClass;
142     }
143    
144     static JSString *
145     GetSlotString(const JSObject *obj, uint32 slot)
146     {
147     jsval v;
148    
149     JS_ASSERT(slot == JSSLOT_PREFIX ||
150     slot == JSSLOT_URI ||
151     slot == JSSLOT_LOCAL_NAME);
152     JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_NamespaceClass.base ||
153     IsQNameClass(STOBJ_GET_CLASS(obj)));
154     JS_ASSERT_IF(STOBJ_GET_CLASS(obj) == &js_NamespaceClass.base,
155     slot != JSSLOT_LOCAL_NAME);
156    
157     v = obj->fslots[slot];
158     if (JSVAL_IS_VOID(v))
159     return NULL;
160     JS_ASSERT(JSVAL_IS_STRING(v));
161     return JSVAL_TO_STRING(v);
162     }
163    
164     static JS_INLINE JSString *
165     GetPrefix(const JSObject *obj)
166     {
167     return GetSlotString(obj, JSSLOT_PREFIX);
168     }
169    
170     static JSString *
171     GetURI(const JSObject *obj)
172     {
173     return GetSlotString(obj, JSSLOT_URI);
174     }
175    
176     static JSString *
177     GetLocalName(const JSObject *obj)
178     {
179     return GetSlotString(obj, JSSLOT_LOCAL_NAME);
180     }
181    
182     static JSBool
183     IsDeclared(const JSObject *obj)
184     {
185     jsval v;
186    
187     JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_NamespaceClass.base);
188     v = obj->fslots[JSSLOT_DECLARED];
189     JS_ASSERT(JSVAL_IS_VOID(v) || v == JSVAL_TRUE);
190     return v == JSVAL_TRUE;
191     }
192    
193     static JSBool
194     xml_isXMLName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
195     jsval *rval)
196     {
197     *rval = BOOLEAN_TO_JSVAL(js_IsXMLName(cx, argv[0]));
198     return JS_TRUE;
199     }
200    
201     /*
202     * Namespace class and library functions.
203     */
204     enum namespace_tinyid {
205     NAMESPACE_PREFIX = -1,
206     NAMESPACE_URI = -2
207     };
208    
209     static JSBool
210     namespace_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
211     {
212     if (!JSVAL_IS_INT(id))
213     return JS_TRUE;
214    
215     if (STOBJ_GET_CLASS(obj) != &js_NamespaceClass.base)
216     return JS_TRUE;
217    
218     switch (JSVAL_TO_INT(id)) {
219     case NAMESPACE_PREFIX:
220     *vp = obj->fslots[JSSLOT_PREFIX];
221     break;
222     case NAMESPACE_URI:
223     *vp = obj->fslots[JSSLOT_URI];
224     break;
225     }
226     return JS_TRUE;
227     }
228    
229     static void
230     namespace_finalize(JSContext *cx, JSObject *obj)
231     {
232     if (cx->runtime->functionNamespaceObject == obj)
233     cx->runtime->functionNamespaceObject = NULL;
234     }
235    
236     static JSBool
237     namespace_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
238     {
239     JSObject *obj2;
240    
241     JS_ASSERT(JSVAL_IS_OBJECT(v));
242     obj2 = JSVAL_TO_OBJECT(v);
243     *bp = (!obj2 || OBJ_GET_CLASS(cx, obj2) != &js_NamespaceClass.base)
244     ? JS_FALSE
245     : js_EqualStrings(GetURI(obj), GetURI(obj2));
246     return JS_TRUE;
247     }
248    
249     JS_FRIEND_DATA(JSExtendedClass) js_NamespaceClass = {
250     { "Namespace",
251     JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED |
252     JSCLASS_HAS_RESERVED_SLOTS(NAMESPACE_RESERVED_SLOTS) |
253     JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Namespace),
254     JS_PropertyStub, JS_PropertyStub, namespace_getProperty, NULL,
255     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, namespace_finalize,
256     NULL, NULL, NULL, NULL,
257     NULL, NULL, NULL, NULL },
258     namespace_equality,NULL, NULL, NULL,
259     NULL, NULL, NULL, NULL
260     };
261    
262     #define NAMESPACE_ATTRS \
263     (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
264    
265     static JSPropertySpec namespace_props[] = {
266     {js_prefix_str, NAMESPACE_PREFIX, NAMESPACE_ATTRS, 0, 0},
267     {js_uri_str, NAMESPACE_URI, NAMESPACE_ATTRS, 0, 0},
268     {0,0,0,0,0}
269     };
270    
271     static JSBool
272     namespace_toString(JSContext *cx, uintN argc, jsval *vp)
273     {
274     JSObject *obj;
275    
276     obj = JS_THIS_OBJECT(cx, vp);
277     if (!JS_InstanceOf(cx, obj, &js_NamespaceClass.base, vp))
278     return JS_FALSE;
279     *vp = obj->fslots[JSSLOT_URI];
280     return JS_TRUE;
281     }
282    
283     static JSFunctionSpec namespace_methods[] = {
284     JS_FN(js_toString_str, namespace_toString, 0,0),
285     JS_FS_END
286     };
287    
288     static JSObject *
289     NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri, JSBool declared)
290     {
291     JSObject *obj;
292    
293     obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL, 0);
294     if (!obj)
295     return JS_FALSE;
296     JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_PREFIX]));
297     JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_URI]));
298     JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_DECLARED]));
299     if (prefix)
300     obj->fslots[JSSLOT_PREFIX] = STRING_TO_JSVAL(prefix);
301     if (uri)
302     obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(uri);
303     if (declared)
304     obj->fslots[JSSLOT_DECLARED] = JSVAL_TRUE;
305     METER(xml_stats.xmlnamespace);
306     return obj;
307     }
308    
309     /*
310     * QName class and library functions.
311     */
312     enum qname_tinyid {
313     QNAME_URI = -1,
314     QNAME_LOCALNAME = -2
315     };
316    
317     static JSBool
318     qname_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
319     {
320     if (!JSVAL_IS_INT(id))
321     return JS_TRUE;
322    
323     if (STOBJ_GET_CLASS(obj) != &js_QNameClass.base)
324     return JS_TRUE;
325    
326     switch (JSVAL_TO_INT(id)) {
327     case QNAME_URI:
328     *vp = obj->fslots[JSSLOT_URI];
329     if (*vp == JSVAL_VOID)
330     *vp = JSVAL_NULL;
331     break;
332     case QNAME_LOCALNAME:
333     *vp = obj->fslots[JSSLOT_LOCAL_NAME];
334     break;
335     }
336     return JS_TRUE;
337     }
338    
339     static void
340     anyname_finalize(JSContext* cx, JSObject* obj)
341     {
342     /* Make sure the next call to js_GetAnyName doesn't try to use obj. */
343     if (cx->runtime->anynameObject == obj)
344     cx->runtime->anynameObject = NULL;
345     }
346    
347     static JSBool
348     qname_identity(JSObject *qna, JSObject *qnb)
349     {
350     JSString *uri1 = GetURI(qna);
351     JSString *uri2 = GetURI(qnb);
352    
353     if (!uri1 ^ !uri2)
354     return JS_FALSE;
355     if (uri1 && !js_EqualStrings(uri1, uri2))
356     return JS_FALSE;
357     return js_EqualStrings(GetLocalName(qna), GetLocalName(qnb));
358     }
359    
360     static JSBool
361     qname_equality(JSContext *cx, JSObject *qn, jsval v, JSBool *bp)
362     {
363     JSObject *obj2;
364    
365     JS_ASSERT(JSVAL_IS_OBJECT(v));
366     obj2 = JSVAL_TO_OBJECT(v);
367     *bp = (!obj2 || OBJ_GET_CLASS(cx, obj2) != &js_QNameClass.base)
368     ? JS_FALSE
369     : qname_identity(qn, obj2);
370     return JS_TRUE;
371     }
372    
373     JS_FRIEND_DATA(JSExtendedClass) js_QNameClass = {
374     { "QName",
375     JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED |
376     JSCLASS_HAS_RESERVED_SLOTS(QNAME_RESERVED_SLOTS) |
377     JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_QName),
378     JS_PropertyStub, JS_PropertyStub, qname_getProperty, NULL,
379     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
380     NULL, NULL, NULL, NULL,
381     NULL, NULL, NULL, NULL },
382     qname_equality, NULL, NULL, NULL,
383     NULL, NULL, NULL, NULL
384     };
385    
386     /*
387     * Classes for the ECMA-357-internal types AttributeName and AnyName, which
388     * are like QName, except that they have no property getters. They share the
389     * qname_toString method, and therefore are exposed as constructable objects
390     * in this implementation.
391     */
392     JS_FRIEND_DATA(JSClass) js_AttributeNameClass = {
393     js_AttributeName_str,
394     JSCLASS_CONSTRUCT_PROTOTYPE |
395     JSCLASS_HAS_RESERVED_SLOTS(QNAME_RESERVED_SLOTS) |
396     JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_AttributeName),
397     JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
398     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
399     NULL, NULL, NULL, NULL,
400     NULL, NULL, NULL, NULL
401     };
402    
403     JS_FRIEND_DATA(JSClass) js_AnyNameClass = {
404     js_AnyName_str,
405     JSCLASS_CONSTRUCT_PROTOTYPE |
406     JSCLASS_HAS_RESERVED_SLOTS(QNAME_RESERVED_SLOTS) |
407     JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_AnyName),
408     JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
409     JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, anyname_finalize,
410     NULL, NULL, NULL, NULL,
411     NULL, NULL, NULL, NULL
412     };
413    
414     #define QNAME_ATTRS \
415     (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
416    
417     static JSPropertySpec qname_props[] = {
418     {js_uri_str, QNAME_URI, QNAME_ATTRS, 0, 0},
419     {js_localName_str, QNAME_LOCALNAME, QNAME_ATTRS, 0, 0},
420     {0,0,0,0,0}
421     };
422    
423     static JSBool
424     qname_toString(JSContext *cx, uintN argc, jsval *vp)
425     {
426     JSObject *obj;
427     JSClass *clasp;
428     JSString *uri, *str, *qualstr;
429     size_t length;
430     jschar *chars;
431    
432     obj = JS_THIS_OBJECT(cx, vp);
433     if (!obj)
434     return JS_FALSE;
435     clasp = OBJ_GET_CLASS(cx, obj);
436     if (clasp != &js_AttributeNameClass &&
437     clasp != &js_AnyNameClass &&
438     !JS_InstanceOf(cx, obj, &js_QNameClass.base, vp + 2)) {
439     return JS_FALSE;
440     }
441    
442     uri = GetURI(obj);
443     if (!uri) {
444     /* No uri means wildcard qualifier. */
445     str = ATOM_TO_STRING(cx->runtime->atomState.starQualifierAtom);
446     } else if (IS_EMPTY(uri)) {
447     /* Empty string for uri means localName is in no namespace. */
448     str = cx->runtime->emptyString;
449     } else {
450     qualstr = ATOM_TO_STRING(cx->runtime->atomState.qualifierAtom);
451     str = js_ConcatStrings(cx, uri, qualstr);
452     if (!str)
453     return JS_FALSE;
454     }
455     str = js_ConcatStrings(cx, str, GetLocalName(obj));
456     if (!str)
457     return JS_FALSE;
458    
459     if (str && clasp == &js_AttributeNameClass) {
460     length = JSSTRING_LENGTH(str);
461     chars = (jschar *) JS_malloc(cx, (length + 2) * sizeof(jschar));
462     if (!chars)
463     return JS_FALSE;
464     *chars = '@';
465     js_strncpy(chars + 1, JSSTRING_CHARS(str), length);
466     chars[++length] = 0;
467     str = js_NewString(cx, chars, length);
468     if (!str) {
469     JS_free(cx, chars);
470     return JS_FALSE;
471     }
472     }
473    
474     *vp = STRING_TO_JSVAL(str);
475     return JS_TRUE;
476     }
477    
478     static JSFunctionSpec qname_methods[] = {
479     JS_FN(js_toString_str, qname_toString, 0,0),
480     JS_FS_END
481     };
482    
483    
484     static void
485     InitXMLQName(JSObject *obj, JSString *uri, JSString *prefix,
486     JSString *localName)
487     {
488     JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_PREFIX]));
489     JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_URI]));
490     JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_LOCAL_NAME]));
491     if (uri)
492     obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(uri);
493     if (prefix)
494     obj->fslots[JSSLOT_PREFIX] = STRING_TO_JSVAL(prefix);
495     if (localName)
496     obj->fslots[JSSLOT_LOCAL_NAME] = STRING_TO_JSVAL(localName);
497     }
498    
499     static JSObject *
500     NewXMLQName(JSContext *cx, JSString *uri, JSString *prefix, JSString *localName,
501     JSClass *clasp = &js_QNameClass.base)
502     {
503     JSObject *obj;
504    
505     JS_ASSERT(IsQNameClass(clasp));
506     obj = js_NewObject(cx, clasp, NULL, NULL, 0);
507     if (!obj)
508     return NULL;
509     InitXMLQName(obj, uri, prefix, localName);
510     METER(xml_stats.qname);
511     return obj;
512     }
513    
514     JSObject *
515     js_ConstructXMLQNameObject(JSContext *cx, jsval nsval, jsval lnval)
516     {
517     jsval argv[2];
518    
519     /*
520     * ECMA-357 11.1.2,
521     * The _QualifiedIdentifier : PropertySelector :: PropertySelector_
522     * production, step 2.
523     */
524     if (!JSVAL_IS_PRIMITIVE(nsval) &&
525     OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nsval)) == &js_AnyNameClass) {
526     nsval = JSVAL_NULL;
527     }
528    
529     argv[0] = nsval;
530     argv[1] = lnval;
531     return js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 2, argv);
532     }
533    
534     static JSBool
535     IsXMLName(const jschar *cp, size_t n)
536     {
537     JSBool rv;
538     jschar c;
539    
540     rv = JS_FALSE;
541     if (n != 0 && JS_ISXMLNSSTART(*cp)) {
542     while (--n != 0) {
543     c = *++cp;
544     if (!JS_ISXMLNS(c))
545     return rv;
546     }
547     rv = JS_TRUE;
548     }
549     return rv;
550     }
551    
552     JSBool
553     js_IsXMLName(JSContext *cx, jsval v)
554     {
555     JSString *name;
556     JSErrorReporter older;
557    
558     /*
559     * Inline specialization of the QName constructor called with v passed as
560     * the only argument, to compute the localName for the constructed qname,
561     * without actually allocating the object or computing its uri and prefix.
562     * See ECMA-357 13.1.2.1 step 1 and 13.3.2.
563     */
564     if (!JSVAL_IS_PRIMITIVE(v) &&
565     IsQNameClass(OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)))) {
566     name = GetLocalName(JSVAL_TO_OBJECT(v));
567     } else {
568     older = JS_SetErrorReporter(cx, NULL);
569     name = js_ValueToString(cx, v);
570     JS_SetErrorReporter(cx, older);
571     if (!name) {
572     JS_ClearPendingException(cx);
573     return JS_FALSE;
574     }
575     }
576    
577     return IsXMLName(JSSTRING_CHARS(name), JSSTRING_LENGTH(name));
578     }
579    
580     /*
581     * When argc is -1, it indicates argv is empty but the code should behave as
582     * if argc is 1 and argv[0] is JSVAL_VOID.
583     */
584     static JSBool
585     NamespaceHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv,
586     jsval *rval)
587     {
588     jsval urival, prefixval;
589     JSObject *uriobj;
590     JSBool isNamespace, isQName;
591     JSClass *clasp;
592     JSString *empty, *uri, *prefix;
593    
594     isNamespace = isQName = JS_FALSE;
595     #ifdef __GNUC__ /* suppress bogus gcc warnings */
596     uriobj = NULL;
597     #endif
598     if (argc <= 0) {
599     urival = JSVAL_VOID;
600     } else {
601     urival = argv[argc > 1];
602     if (!JSVAL_IS_PRIMITIVE(urival)) {
603     uriobj = JSVAL_TO_OBJECT(urival);
604     clasp = OBJ_GET_CLASS(cx, uriobj);
605     isNamespace = (clasp == &js_NamespaceClass.base);
606     isQName = (clasp == &js_QNameClass.base);
607     }
608     }
609    
610     if (!obj) {
611     /* Namespace called as function. */
612     if (argc == 1 && isNamespace) {
613     /* Namespace called with one Namespace argument is identity. */
614     *rval = urival;
615     return JS_TRUE;
616     }
617    
618     obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL, 0);
619     if (!obj)
620     return JS_FALSE;
621     *rval = OBJECT_TO_JSVAL(obj);
622     }
623     METER(xml_stats.xmlnamespace);
624    
625     empty = cx->runtime->emptyString;
626     obj->fslots[JSSLOT_PREFIX] = STRING_TO_JSVAL(empty);
627     obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(empty);
628    
629     if (argc == 1 || argc == -1) {
630     if (isNamespace) {
631     obj->fslots[JSSLOT_URI] = uriobj->fslots[JSSLOT_URI];
632     obj->fslots[JSSLOT_PREFIX] = uriobj->fslots[JSSLOT_PREFIX];
633     } else if (isQName && (uri = GetURI(uriobj))) {
634     obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(uri);
635     obj->fslots[JSSLOT_PREFIX] = uriobj->fslots[JSSLOT_PREFIX];
636     } else {
637     uri = js_ValueToString(cx, urival);
638     if (!uri)
639     return JS_FALSE;
640     obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(uri);
641     if (!IS_EMPTY(uri))
642     obj->fslots[JSSLOT_PREFIX] = JSVAL_VOID;
643     }
644     } else if (argc == 2) {
645     if (!isQName || !(uri = GetURI(uriobj))) {
646     uri = js_ValueToString(cx, urival);
647     if (!uri)
648     return JS_FALSE;
649     }
650     obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(uri);
651    
652     prefixval = argv[0];
653     if (IS_EMPTY(uri)) {
654     if (!JSVAL_IS_VOID(prefixval)) {
655     prefix = js_ValueToString(cx, prefixval);
656     if (!prefix)
657     return JS_FALSE;
658     if (!IS_EMPTY(prefix)) {
659     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
660     JSMSG_BAD_XML_NAMESPACE,
661     js_ValueToPrintableString(cx,
662     STRING_TO_JSVAL(prefix)));
663     return JS_FALSE;
664     }
665     }
666     } else if (JSVAL_IS_VOID(prefixval) || !js_IsXMLName(cx, prefixval)) {
667     obj->fslots[JSSLOT_PREFIX] = JSVAL_VOID;
668     } else {
669     prefix = js_ValueToString(cx, prefixval);
670     if (!prefix)
671     return JS_FALSE;
672     obj->fslots[JSSLOT_PREFIX] = STRING_TO_JSVAL(prefix);
673     }
674     }
675    
676     return JS_TRUE;
677     }
678    
679     static JSBool
680     Namespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
681     {
682     return NamespaceHelper(cx,
683     (cx->fp->flags & JSFRAME_CONSTRUCTING) ? obj : NULL,
684     argc, argv, rval);
685     }
686    
687     /*
688     * When argc is -1, it indicates argv is empty but the code should behave as
689     * if argc is 1 and argv[0] is JSVAL_VOID.
690     */
691     static JSBool
692     QNameHelper(JSContext *cx, JSObject *obj, JSClass *clasp, intN argc,
693     jsval *argv, jsval *rval)
694     {
695     jsval nameval, nsval;
696     JSBool isQName, isNamespace;
697     JSObject *qn;
698     JSString *uri, *prefix, *name;
699     JSObject *obj2;
700    
701     JS_ASSERT(clasp == &js_QNameClass.base ||
702     clasp == &js_AttributeNameClass);
703     if (argc <= 0) {
704     nameval = JSVAL_VOID;
705     isQName = JS_FALSE;
706     } else {
707     nameval = argv[argc > 1];
708     isQName =
709     !JSVAL_IS_PRIMITIVE(nameval) &&
710     OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nameval)) == &js_QNameClass.base;
711     }
712    
713     if (!obj) {
714     /* QName called as function. */
715     if (argc == 1 && isQName) {
716     /* QName called with one QName argument is identity. */
717     *rval = nameval;
718     return JS_TRUE;
719     }
720    
721     /*
722     * Create and return a new QName or AttributeName object exactly as if
723     * constructed.
724     */
725     obj = js_NewObject(cx, clasp, NULL, NULL, 0);
726     if (!obj)
727     return JS_FALSE;
728     *rval = OBJECT_TO_JSVAL(obj);
729     }
730     METER(xml_stats.qname);
731    
732     if (isQName) {
733     /* If namespace is not specified and name is a QName, clone it. */
734     qn = JSVAL_TO_OBJECT(nameval);
735     if (argc == 1) {
736     uri = GetURI(qn);
737     prefix = GetPrefix(qn);
738     name = GetLocalName(qn);
739     goto out;
740     }
741    
742     /* Namespace and qname were passed -- use the qname's localName. */
743     nameval = qn->fslots[JSSLOT_LOCAL_NAME];
744     }
745    
746     if (argc == 0) {
747     name = cx->runtime->emptyString;
748     } else if (argc < 0) {
749     name = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
750     } else {
751     name = js_ValueToString(cx, nameval);
752     if (!name)
753     return JS_FALSE;
754     argv[argc > 1] = STRING_TO_JSVAL(name);
755     }
756    
757     if (argc > 1 && !JSVAL_IS_VOID(argv[0])) {
758     nsval = argv[0];
759     } else if (IS_STAR(name)) {
760     nsval = JSVAL_NULL;
761     } else {
762     if (!js_GetDefaultXMLNamespace(cx, &nsval))
763     return JS_FALSE;
764     JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval));
765     JS_ASSERT(OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nsval)) ==
766     &js_NamespaceClass.base);
767     }
768    
769     if (JSVAL_IS_NULL(nsval)) {
770     /* NULL prefix represents *undefined* in ECMA-357 13.3.2 5(a). */
771     uri = prefix = NULL;
772     } else {
773     /*
774     * Inline specialization of the Namespace constructor called with
775     * nsval passed as the only argument, to compute the uri and prefix
776     * for the constructed namespace, without actually allocating the
777     * object or computing other members. See ECMA-357 13.3.2 6(a) and
778     * 13.2.2.
779     */
780     isNamespace = isQName = JS_FALSE;
781     if (!JSVAL_IS_PRIMITIVE(nsval)) {
782     obj2 = JSVAL_TO_OBJECT(nsval);
783     clasp = OBJ_GET_CLASS(cx, obj2);
784     isNamespace = (clasp == &js_NamespaceClass.base);
785     isQName = (clasp == &js_QNameClass.base);
786     }
787     #ifdef __GNUC__ /* suppress bogus gcc warnings */
788     else obj2 = NULL;
789     #endif
790    
791     if (isNamespace) {
792     uri = GetURI(obj2);
793     prefix = GetPrefix(obj2);
794     } else if (isQName && (uri = GetURI(obj2))) {
795     JS_ASSERT(argc > 1);
796     prefix = GetPrefix(obj2);
797     } else {
798     JS_ASSERT(argc > 1);
799     uri = js_ValueToString(cx, nsval);
800     if (!uri)
801     return JS_FALSE;
802     argv[0] = STRING_TO_JSVAL(uri); /* local root */
803    
804     /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */
805     prefix = IS_EMPTY(uri) ? cx->runtime->emptyString : NULL;
806     }
807     }
808    
809     out:
810     InitXMLQName(obj, uri, prefix, name);
811     return JS_TRUE;
812     }
813    
814     static JSBool
815     QName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
816     {
817     return QNameHelper(cx, (cx->fp->flags & JSFRAME_CONSTRUCTING) ? obj : NULL,
818     &js_QNameClass.base, argc, argv, rval);
819     }
820    
821     static JSBool
822     AttributeName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
823     jsval *rval)
824     {
825     return QNameHelper(cx, (cx->fp->flags & JSFRAME_CONSTRUCTING) ? obj : NULL,
826     &js_AttributeNameClass, argc, argv, rval);
827     }
828    
829     /*
830     * XMLArray library functions.
831     */
832     static JSBool
833     namespace_identity(const void *a, const void *b)
834     {
835     const JSObject *nsa = (const JSObject *) a;
836     const JSObject *nsb = (const JSObject *) b;
837     JSString *prefixa = GetPrefix(nsa);
838     JSString *prefixb = GetPrefix(nsb);
839    
840     if (prefixa && prefixb) {
841     if (!js_EqualStrings(prefixa, prefixb))
842     return JS_FALSE;
843     } else {
844     if (prefixa || prefixb)
845     return JS_FALSE;
846     }
847     return js_EqualStrings(GetURI(nsa), GetURI(nsb));
848     }
849    
850     static JSBool
851     attr_identity(const void *a, const void *b)
852     {
853     const JSXML *xmla = (const JSXML *) a;
854     const JSXML *xmlb = (const JSXML *) b;
855    
856     return qname_identity(xmla->name, xmlb->name);
857     }
858    
859     static void
860     XMLArrayCursorInit(JSXMLArrayCursor *cursor, JSXMLArray *array)
861     {
862     JSXMLArrayCursor *next;
863    
864     cursor->array = array;
865     cursor->index = 0;
866     next = cursor->next = array->cursors;
867     if (next)
868     next->prevp = &cursor->next;
869     cursor->prevp = &array->cursors;
870     array->cursors = cursor;
871     cursor->root = NULL;
872     }
873    
874     static void
875     XMLArrayCursorFinish(JSXMLArrayCursor *cursor)
876     {
877     JSXMLArrayCursor *next;
878    
879     if (!cursor->array)
880     return;
881     next = cursor->next;
882     if (next)
883     next->prevp = cursor->prevp;
884     *cursor->prevp = next;
885     cursor->array = NULL;
886     }
887    
888     static void *
889     XMLArrayCursorNext(JSXMLArrayCursor *cursor)
890     {
891     JSXMLArray *array;
892    
893     array = cursor->array;
894     if (!array || cursor->index >= array->length)
895     return NULL;
896     return cursor->root = array->vector[cursor->index++];
897     }
898    
899     static void *
900     XMLArrayCursorItem(JSXMLArrayCursor *cursor)
901     {
902     JSXMLArray *array;
903    
904     array = cursor->array;
905     if (!array || cursor->index >= array->length)
906     return NULL;
907     return cursor->root = array->vector[cursor->index];
908     }
909    
910     static void
911     XMLArrayCursorTrace(JSTracer *trc, JSXMLArrayCursor *cursor)
912     {
913     void *root;
914     #ifdef DEBUG
915     size_t index = 0;
916     #endif
917    
918     for (; cursor; cursor = cursor->next) {
919     root = cursor->root;
920     JS_SET_TRACING_INDEX(trc, "cursor_root", index++);
921     js_CallValueTracerIfGCThing(trc, (jsval)root);
922     }
923     }
924    
925     /* NB: called with null cx from the GC, via xml_trace => XMLArrayTrim. */
926     static JSBool
927     XMLArraySetCapacity(JSContext *cx, JSXMLArray *array, uint32 capacity)
928     {
929     void **vector;
930    
931     if (capacity == 0) {
932     /* We could let realloc(p, 0) free this, but purify gets confused. */
933     if (array->vector)
934     free(array->vector);
935     vector = NULL;
936     } else {
937     if (
938     #if JS_BITS_PER_WORD == 32
939     (size_t)capacity > ~(size_t)0 / sizeof(void *) ||
940     #endif
941     !(vector = (void **)
942     realloc(array->vector, capacity * sizeof(void *)))) {
943     if (cx)
944     JS_ReportOutOfMemory(cx);
945     return JS_FALSE;
946     }
947     }
948     array->capacity = JSXML_PRESET_CAPACITY | capacity;
949     array->vector = vector;
950     return JS_TRUE;
951     }
952    
953     static void
954     XMLArrayTrim(JSXMLArray *array)
955     {
956     if (array->capacity & JSXML_PRESET_CAPACITY)
957     return;
958     if (array->length < array->capacity)
959     XMLArraySetCapacity(NULL, array, array->length);
960     }
961    
962     static JSBool
963     XMLArrayInit(JSContext *cx, JSXMLArray *array, uint32 capacity)
964     {
965     array->length = array->capacity = 0;
966     array->vector = NULL;
967     array->cursors = NULL;
968     return capacity == 0 || XMLArraySetCapacity(cx, array, capacity);
969     }
970    
971     static void
972     XMLArrayFinish(JSContext *cx, JSXMLArray *array)
973     {
974     JSXMLArrayCursor *cursor;
975    
976     JS_free(cx, array->vector);
977    
978     while ((cursor = array->cursors) != NULL)
979     XMLArrayCursorFinish(cursor);
980    
981     #ifdef DEBUG
982     memset(array, 0xd5, sizeof *array);
983     #endif
984     }
985    
986     #define XML_NOT_FOUND ((uint32) -1)
987    
988     static uint32
989     XMLArrayFindMember(const JSXMLArray *array, void *elt, JSIdentityOp identity)
990     {
991     void **vector;
992     uint32 i, n;
993    
994     /* The identity op must not reallocate array->vector. */
995     vector = array->vector;
996     if (identity) {
997     for (i = 0, n = array->length; i < n; i++) {
998     if (identity(vector[i], elt))
999     return i;
1000     }
1001     } else {
1002     for (i = 0, n = array->length; i < n; i++) {
1003     if (vector[i] == elt)
1004     return i;
1005     }
1006     }
1007     return XML_NOT_FOUND;
1008     }
1009    
1010     /*
1011     * Grow array vector capacity by powers of two to LINEAR_THRESHOLD, and after
1012     * that, grow by LINEAR_INCREMENT. Both must be powers of two, and threshold
1013     * should be greater than increment.
1014     */
1015     #define LINEAR_THRESHOLD 256
1016     #define LINEAR_INCREMENT 32
1017    
1018     static JSBool
1019     XMLArrayAddMember(JSContext *cx, JSXMLArray *array, uint32 index, void *elt)
1020     {
1021     uint32 capacity, i;
1022     int log2;
1023     void **vector;
1024    
1025     if (index >= array->length) {
1026     if (index >= JSXML_CAPACITY(array)) {
1027     /* Arrange to clear JSXML_PRESET_CAPACITY from array->capacity. */
1028     capacity = index + 1;
1029     if (index >= LINEAR_THRESHOLD) {
1030     capacity = JS_ROUNDUP(capacity, LINEAR_INCREMENT);
1031     } else {
1032     JS_CEILING_LOG2(log2, capacity);
1033     capacity = JS_BIT(log2);
1034     }
1035     if (
1036     #if JS_BITS_PER_WORD == 32
1037     (size_t)capacity > ~(size_t)0 / sizeof(void *) ||
1038     #endif
1039     !(vector = (void **)
1040     realloc(array->vector, capacity * sizeof(void *)))) {
1041     JS_ReportOutOfMemory(cx);
1042     return JS_FALSE;
1043     }
1044     array->capacity = capacity;
1045     array->vector = vector;
1046     for (i = array->length; i < index; i++)
1047     vector[i] = NULL;
1048     }
1049     array->length = index + 1;
1050     }
1051    
1052     array->vector[index] = elt;
1053     return JS_TRUE;
1054     }
1055    
1056     static JSBool
1057     XMLArrayInsert(JSContext *cx, JSXMLArray *array, uint32 i, uint32 n)
1058     {
1059     uint32 j;
1060     JSXMLArrayCursor *cursor;
1061    
1062     j = array->length;
1063     JS_ASSERT(i <= j);
1064     if (!XMLArraySetCapacity(cx, array, j + n))
1065     return JS_FALSE;
1066    
1067     array->length = j + n;
1068     JS_ASSERT(n != (uint32)-1);
1069     while (j != i) {
1070     --j;
1071     array->vector[j + n] = array->vector[j];
1072     }
1073    
1074     for (cursor = array->cursors; cursor; cursor = cursor->next) {
1075     if (cursor->index > i)
1076     cursor->index += n;
1077     }
1078     return JS_TRUE;
1079     }
1080    
1081     static void *
1082     XMLArrayDelete(JSContext *cx, JSXMLArray *array, uint32 index, JSBool compress)
1083     {
1084     uint32 length;
1085     void **vector, *elt;
1086     JSXMLArrayCursor *cursor;
1087    
1088     length = array->length;
1089     if (index >= length)
1090     return NULL;
1091    
1092     vector = array->vector;
1093     elt = vector[index];
1094     if (compress) {
1095     while (++index < length)
1096     vector[index-1] = vector[index];
1097     array->length = length - 1;
1098     array->capacity = JSXML_CAPACITY(array);
1099     } else {
1100     vector[index] = NULL;
1101     }
1102    
1103     for (cursor = array->cursors; cursor; cursor = cursor->next) {
1104     if (cursor->index > index)
1105     --cursor->index;
1106     }
1107     return elt;
1108     }
1109    
1110     static void
1111     XMLArrayTruncate(JSContext *cx, JSXMLArray *array, uint32 length)
1112     {
1113     void **vector;
1114    
1115     JS_ASSERT(!array->cursors);
1116     if (length >= array->length)
1117     return;
1118    
1119     if (length == 0) {
1120     if (array->vector)
1121     free(array->vector);
1122     vector = NULL;
1123     } else {
1124     vector = (void **) realloc(array->vector, length * sizeof(void *));
1125     if (!vector)
1126     return;
1127     }
1128    
1129     if (array->length > length)
1130     array->length = length;
1131     array->capacity = length;
1132     array->vector = vector;
1133     }
1134    
1135     #define XMLARRAY_FIND_MEMBER(a,e,f) XMLArrayFindMember(a, (void *)(e), f)
1136     #define XMLARRAY_HAS_MEMBER(a,e,f) (XMLArrayFindMember(a, (void *)(e), f) != \
1137     XML_NOT_FOUND)
1138     #define XMLARRAY_MEMBER(a,i,t) (((i) < (a)->length) \
1139     ? (t *) (a)->vector[i] \
1140     : NULL)
1141     #define XMLARRAY_SET_MEMBER(a,i,e) JS_BEGIN_MACRO \
1142     if ((a)->length <= (i)) \
1143     (a)->length = (i) + 1; \
1144     ((a)->vector[i] = (void *)(e)); \
1145     JS_END_MACRO
1146     #define XMLARRAY_ADD_MEMBER(x,a,i,e)XMLArrayAddMember(x, a, i, (void *)(e))
1147     #define XMLARRAY_INSERT(x,a,i,n) XMLArrayInsert(x, a, i, n)
1148     #define XMLARRAY_APPEND(x,a,e) XMLARRAY_ADD_MEMBER(x, a, (a)->length, (e))
1149     #define XMLARRAY_DELETE(x,a,i,c,t) ((t *) XMLArrayDelete(x, a, i, c))
1150     #define XMLARRAY_TRUNCATE(x,a,n) XMLArrayTruncate(x, a, n)
1151    
1152     /*
1153     * Define XML setting property strings and constants early, so everyone can
1154     * use the same names and their magic numbers (tinyids, flags).
1155     */
1156     static const char js_ignoreComments_str[] = "ignoreComments";
1157     static const char js_ignoreProcessingInstructions_str[]
1158     = "ignoreProcessingInstructions";
1159     static const char js_ignoreWhitespace_str[] = "ignoreWhitespace";
1160     static const char js_prettyPrinting_str[] = "prettyPrinting";
1161     static const char js_prettyIndent_str[] = "prettyIndent";
1162    
1163     /*
1164     * NB: These XML static property tinyids must
1165     * (a) not collide with the generic negative tinyids at the top of jsfun.c;
1166     * (b) index their corresponding xml_static_props array elements.
1167     * Don't change 'em!
1168     */
1169     enum xml_static_tinyid {
1170     XML_IGNORE_COMMENTS,
1171     XML_IGNORE_PROCESSING_INSTRUCTIONS,
1172     XML_IGNORE_WHITESPACE,
1173     XML_PRETTY_PRINTING,
1174     XML_PRETTY_INDENT
1175     };
1176    
1177     static JSBool
1178     xml_setting_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
1179     {
1180     return JS_TRUE;
1181     }
1182    
1183     static JSBool
1184     xml_setting_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
1185     {
1186     uint8 flag;
1187    
1188     JS_ASSERT(JSVAL_IS_INT(id));
1189    
1190     flag = JS_BIT(JSVAL_TO_INT(id));
1191     if (js_ValueToBoolean(*vp))
1192     cx->xmlSettingFlags |= flag;
1193     else
1194     cx->xmlSettingFlags &= ~flag;
1195     return JS_TRUE;
1196     }
1197    
1198     static JSPropertySpec xml_static_props[] = {
1199     {js_ignoreComments_str, XML_IGNORE_COMMENTS, JSPROP_PERMANENT,
1200     xml_setting_getter, xml_setting_setter},
1201     {js_ignoreProcessingInstructions_str,
1202     XML_IGNORE_PROCESSING_INSTRUCTIONS, JSPROP_PERMANENT,
1203     xml_setting_getter, xml_setting_setter},
1204     {js_ignoreWhitespace_str, XML_IGNORE_WHITESPACE, JSPROP_PERMANENT,
1205     xml_setting_getter, xml_setting_setter},
1206     {js_prettyPrinting_str, XML_PRETTY_PRINTING, JSPROP_PERMANENT,
1207     xml_setting_getter, xml_setting_setter},
1208     {js_prettyIndent_str, XML_PRETTY_INDENT, JSPROP_PERMANENT,
1209     xml_setting_getter, NULL},
1210     {0,0,0,0,0}
1211     };
1212    
1213     /* Derive cx->xmlSettingFlags bits from xml_static_props tinyids. */
1214     #define XSF_IGNORE_COMMENTS JS_BIT(XML_IGNORE_COMMENTS)
1215     #define XSF_IGNORE_PROCESSING_INSTRUCTIONS \
1216     JS_BIT(XML_IGNORE_PROCESSING_INSTRUCTIONS)
1217     #define XSF_IGNORE_WHITESPACE JS_BIT(XML_IGNORE_WHITESPACE)
1218     #define XSF_PRETTY_PRINTING JS_BIT(XML_PRETTY_PRINTING)
1219     #define XSF_CACHE_VALID JS_BIT(XML_PRETTY_INDENT)
1220    
1221     /*
1222     * Extra, unrelated but necessarily disjoint flag used by ParseNodeToXML.
1223     * This flag means a couple of things:
1224     *
1225     * - The top JSXML created for a parse tree must have an object owning it.
1226     *
1227     * - That the default namespace normally inherited from the temporary
1228     * <parent xmlns='...'> tag that wraps a runtime-concatenated XML source
1229     * string must, in the case of a precompiled XML object tree, inherit via
1230     * ad-hoc code in ParseNodeToXML.
1231     *
1232     * Because of the second purpose, we name this flag XSF_PRECOMPILED_ROOT.
1233     */
1234     #define XSF_PRECOMPILED_ROOT (XSF_CACHE_VALID << 1)
1235    
1236     /* Macros for special-casing xml:, xmlns= and xmlns:foo= in ParseNodeToQName. */
1237     #define IS_XML(str) \
1238     (JSSTRING_LENGTH(str) == 3 && IS_XML_CHARS(JSSTRING_CHARS(str)))
1239    
1240     #define IS_XMLNS(str) \
1241     (JSSTRING_LENGTH(str) == 5 && IS_XMLNS_CHARS(JSSTRING_CHARS(str)))
1242    
1243     #define IS_XML_CHARS(chars) \
1244     (JS_TOLOWER((chars)[0]) == 'x' && \
1245     JS_TOLOWER((chars)[1]) == 'm' && \
1246     JS_TOLOWER((chars)[2]) == 'l')
1247    
1248     #define HAS_NS_AFTER_XML(chars) \
1249     (JS_TOLOWER((chars)[3]) == 'n' && \
1250     JS_TOLOWER((chars)[4]) == 's')
1251    
1252     #define IS_XMLNS_CHARS(chars) \
1253     (IS_XML_CHARS(chars) && HAS_NS_AFTER_XML(chars))
1254    
1255     #define STARTS_WITH_XML(chars,length) \
1256     (length >= 3 && IS_XML_CHARS(chars))
1257    
1258     static const char xml_namespace_str[] = "http://www.w3.org/XML/1998/namespace";
1259     static const char xmlns_namespace_str[] = "http://www.w3.org/2000/xmlns/";
1260    
1261     static JSObject *
1262     ParseNodeToQName(JSContext *cx, JSParseContext *pc, JSParseNode *pn,
1263     JSXMLArray *inScopeNSes, JSBool isAttributeName)
1264     {
1265     JSString *str, *uri, *prefix, *localName;
1266     size_t length, offset;
1267     const jschar *start, *limit, *colon;
1268     uint32 n;
1269     JSObject *ns;
1270     JSString *nsprefix;
1271    
1272     JS_ASSERT(pn->pn_arity == PN_NULLARY);
1273     str = ATOM_TO_STRING(pn->pn_atom);
1274     JSSTRING_CHARS_AND_LENGTH(str, start, length);
1275     JS_ASSERT(length != 0 && *start != '@');
1276     JS_ASSERT(length != 1 || *start != '*');
1277    
1278     uri = cx->runtime->emptyString;
1279     limit = start + length;
1280     colon = js_strchr_limit(start, ':', limit);
1281     if (colon) {
1282     offset = PTRDIFF(colon, start, jschar);
1283     prefix = js_NewDependentString(cx, str, 0, offset);
1284     if (!prefix)
1285     return NULL;
1286    
1287     if (STARTS_WITH_XML(start, offset)) {
1288     if (offset == 3) {
1289     uri = JS_InternString(cx, xml_namespace_str);
1290     if (!uri)
1291     return NULL;
1292     } else if (offset == 5 && HAS_NS_AFTER_XML(start)) {
1293     uri = JS_InternString(cx, xmlns_namespace_str);
1294     if (!uri)
1295     return NULL;
1296     } else {
1297     uri = NULL;
1298     }
1299     } else {
1300     uri = NULL;
1301     n = inScopeNSes->length;
1302     while (n != 0) {
1303     --n;
1304     ns = XMLARRAY_MEMBER(inScopeNSes, n, JSObject);
1305     nsprefix = GetPrefix(ns);
1306     if (nsprefix && js_EqualStrings(nsprefix, prefix)) {
1307     uri = GetURI(ns);
1308     break;
1309     }
1310     }
1311     }
1312    
1313     if (!uri) {
1314     js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn,
1315     JSREPORT_ERROR,
1316     JSMSG_BAD_XML_NAMESPACE,
1317     js_ValueToPrintableString(cx,
1318     STRING_TO_JSVAL(prefix)));
1319     return NULL;
1320     }
1321    
1322     localName = js_NewStringCopyN(cx, colon + 1, length - (offset + 1));
1323     if (!localName)
1324     return NULL;
1325     } else {
1326     if (isAttributeName) {
1327     /*
1328     * An unprefixed attribute is not in any namespace, so set prefix
1329     * as well as uri to the empty string.
1330     */
1331     prefix = uri;
1332     } else {
1333     /*
1334     * Loop from back to front looking for the closest declared default
1335     * namespace.
1336     */
1337     n = inScopeNSes->length;
1338     while (n != 0) {
1339     --n;
1340     ns = XMLARRAY_MEMBER(inScopeNSes, n, JSObject);
1341     nsprefix = GetPrefix(ns);
1342     if (!nsprefix || IS_EMPTY(nsprefix)) {
1343     uri = GetURI(ns);
1344     break;
1345     }
1346     }
1347     prefix = IS_EMPTY(uri) ? cx->runtime->emptyString : NULL;
1348     }
1349     localName = str;
1350     }
1351    
1352     return NewXMLQName(cx, uri, prefix, localName);
1353     }
1354    
1355     static JSString *
1356     ChompXMLWhitespace(JSContext *cx, JSString *str)
1357     {
1358     size_t length, newlength, offset;
1359     const jschar *cp, *start, *end;
1360     jschar c;
1361    
1362     JSSTRING_CHARS_AND_LENGTH(str, start, length);
1363     for (cp = start, end = cp + length; cp < end; cp++) {
1364     c = *cp;
1365     if (!JS_ISXMLSPACE(c))
1366     break;
1367     }
1368     while (end > cp) {
1369     c = end[-1];
1370     if (!JS_ISXMLSPACE(c))
1371     break;
1372     --end;
1373     }
1374     newlength = PTRDIFF(end, cp, jschar);
1375     if (newlength == length)
1376     return str;
1377     offset = PTRDIFF(cp, start, jschar);
1378     return js_NewDependentString(cx, str, offset, newlength);
1379     }
1380    
1381     static JSXML *
1382     ParseNodeToXML(JSContext *cx, JSParseContext *pc, JSParseNode *pn,
1383     JSXMLArray *inScopeNSes, uintN flags)
1384     {
1385     JSXML *xml, *kid, *attr, *attrj;
1386     JSString *str;
1387     uint32 length, n, i, j;
1388     JSParseNode *pn2, *pn3, *head, **pnp;
1389     JSObject *ns;
1390     JSObject *qn, *attrjqn;
1391     JSXMLClass xml_class;
1392     int stackDummy;
1393    
1394     if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
1395     js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn, JSREPORT_ERROR,
1396     JSMSG_OVER_RECURSED);
1397     return NULL;
1398     }
1399    
1400     #define PN2X_SKIP_CHILD ((JSXML *) 1)
1401    
1402     /*
1403     * Cases return early to avoid common code that gets an outermost xml's
1404     * object, which protects GC-things owned by xml and its descendants from
1405     * garbage collection.
1406     */
1407     xml = NULL;
1408     if (!js_EnterLocalRootScope(cx))
1409     return NULL;
1410     switch (pn->pn_type) {
1411     case TOK_XMLELEM:
1412     length = inScopeNSes->length;
1413     pn2 = pn->pn_head;
1414     xml = ParseNodeToXML(cx, pc, pn2, inScopeNSes, flags);
1415     if (!xml)
1416     goto fail;
1417    
1418     flags &= ~XSF_PRECOMPILED_ROOT;
1419     n = pn->pn_count;
1420     JS_ASSERT(n >= 2);
1421     n -= 2;
1422     if (!XMLArraySetCapacity(cx, &xml->xml_kids, n))
1423     goto fail;
1424    
1425     i = 0;
1426     while ((pn2 = pn2->pn_next) != NULL) {
1427     if (!pn2->pn_next) {
1428     /* Don't append the end tag! */
1429     JS_ASSERT(pn2->pn_type == TOK_XMLETAGO);
1430     break;
1431     }
1432    
1433     if ((flags & XSF_IGNORE_WHITESPACE) &&
1434     n > 1 && pn2->pn_type == TOK_XMLSPACE) {
1435     --n;
1436     continue;
1437     }
1438    
1439     kid = ParseNodeToXML(cx, pc, pn2, inScopeNSes, flags);
1440     if (kid == PN2X_SKIP_CHILD) {
1441     --n;
1442     continue;
1443     }
1444    
1445     if (!kid)
1446     goto fail;
1447    
1448     /* Store kid in xml right away, to protect it from GC. */
1449     XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid);
1450     kid->parent = xml;
1451     ++i;
1452    
1453     /* XXX where is this documented in an XML spec, or in E4X? */
1454     if ((flags & XSF_IGNORE_WHITESPACE) &&
1455     n > 1 && kid->xml_class == JSXML_CLASS_TEXT) {
1456     str = ChompXMLWhitespace(cx, kid->xml_value);
1457     if (!str)
1458     goto fail;
1459     kid->xml_value = str;
1460     }
1461     }
1462    
1463     JS_ASSERT(i == n);
1464     if (n < pn->pn_count - 2)
1465     XMLArrayTrim(&xml->xml_kids);
1466     XMLARRAY_TRUNCATE(cx, inScopeNSes, length);
1467     break;
1468    
1469     case TOK_XMLLIST:
1470     xml = js_NewXML(cx, JSXML_CLASS_LIST);
1471     if (!xml)
1472     goto fail;
1473    
1474     n = pn->pn_count;
1475     if (!XMLArraySetCapacity(cx, &xml->xml_kids, n))
1476     goto fail;
1477    
1478     i = 0;
1479     for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
1480     /*
1481     * Always ignore insignificant whitespace in lists -- we shouldn't
1482     * condition this on an XML.ignoreWhitespace setting when the list
1483     * constructor is XMLList (note XML/XMLList unification hazard).
1484     */
1485     if (pn2->pn_type == TOK_XMLSPACE) {
1486     --n;
1487     continue;
1488     }
1489    
1490     kid = ParseNodeToXML(cx, pc, pn2, inScopeNSes, flags);
1491     if (kid == PN2X_SKIP_CHILD) {
1492     --n;
1493     continue;
1494     }
1495    
1496     if (!kid)
1497     goto fail;
1498    
1499     XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid);
1500     ++i;
1501     }
1502    
1503     if (n < pn->pn_count)
1504     XMLArrayTrim(&xml->xml_kids);
1505     break;
1506    
1507     case TOK_XMLSTAGO:
1508     case TOK_XMLPTAGC:
1509     length = inScopeNSes->length;
1510     pn2 = pn->pn_head;
1511     JS_ASSERT(pn2->pn_type == TOK_XMLNAME);
1512     if (pn2->pn_arity == PN_LIST)
1513     goto syntax;
1514    
1515     xml = js_NewXML(cx, JSXML_CLASS_ELEMENT);
1516     if (!xml)
1517     goto fail;
1518    
1519     /* First pass: check syntax and process namespace declarations. */
1520     JS_ASSERT(pn->pn_count >= 1);
1521     n = pn->pn_count - 1;
1522     pnp = &pn2->pn_next;
1523     head = *pnp;
1524     while ((pn2 = *pnp) != NULL) {
1525     size_t length;
1526     const jschar *chars;
1527    
1528     if (pn2->pn_type != TOK_XMLNAME || pn2->pn_arity != PN_NULLARY)
1529     goto syntax;
1530    
1531     /* Enforce "Well-formedness constraint: Unique Att Spec". */
1532     for (pn3 = head; pn3 != pn2; pn3 = pn3->pn_next->pn_next) {
1533     if (pn3->pn_atom == pn2->pn_atom) {
1534     js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn2,
1535     JSREPORT_ERROR,
1536     JSMSG_DUPLICATE_XML_ATTR,
1537     js_ValueToPrintableString(cx,
1538     ATOM_KEY(pn2->pn_atom)));
1539     goto fail;
1540     }
1541     }
1542    
1543     str = ATOM_TO_STRING(pn2->pn_atom);
1544     pn2 = pn2->pn_next;
1545     JS_ASSERT(pn2);
1546     if (pn2->pn_type != TOK_XMLATTR)
1547     goto syntax;
1548    
1549     JSSTRING_CHARS_AND_LENGTH(str, chars, length);
1550     if (length >= 5 &&
1551     IS_XMLNS_CHARS(chars) &&
1552     (length == 5 || chars[5] == ':')) {
1553     JSString *uri, *prefix;
1554    
1555     uri = ATOM_TO_STRING(pn2->pn_atom);
1556     if (length == 5) {
1557     /* 10.3.2.1. Step 6(h)(i)(1)(a). */
1558     prefix = cx->runtime->emptyString;
1559     } else {
1560     prefix = js_NewStringCopyN(cx, chars + 6, length - 6);
1561     if (!prefix)
1562     goto fail;
1563     }
1564    
1565     /*
1566     * Once the new ns is appended to xml->xml_namespaces, it is
1567     * protected from GC by the object that owns xml -- which is
1568     * either xml->object if outermost, or the object owning xml's
1569     * oldest ancestor if !outermost.
1570     */
1571     ns = NewXMLNamespace(cx, prefix, uri, JS_TRUE);
1572     if (!ns)
1573     goto fail;
1574    
1575     /*
1576     * Don't add a namespace that's already in scope. If someone
1577     * extracts a child property from its parent via [[Get]], then
1578     * we enforce the invariant, noted many times in ECMA-357, that
1579     * the child's namespaces form a possibly-improper superset of
1580     * its ancestors' namespaces.
1581     */
1582     if (!XMLARRAY_HAS_MEMBER(inScopeNSes, ns, namespace_identity)) {
1583     if (!XMLARRAY_APPEND(cx, inScopeNSes, ns) ||
1584     !XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) {
1585     goto fail;
1586     }
1587     }
1588    
1589     JS_ASSERT(n >= 2);
1590     n -= 2;
1591     *pnp = pn2->pn_next;
1592     /* XXXbe recycle pn2 */
1593     continue;
1594     }
1595    
1596     pnp = &pn2->pn_next;
1597     }
1598    
1599     /*
1600     * If called from js_ParseNodeToXMLObject, emulate the effect of the
1601     * <parent xmlns='%s'>...</parent> wrapping done by "ToXML Applied to
1602     * the String Type" (ECMA-357 10.3.1).
1603     */
1604     if (flags & XSF_PRECOMPILED_ROOT) {
1605     JS_ASSERT(length >= 1);
1606     ns = XMLARRAY_MEMBER(inScopeNSes, 0, JSObject);
1607     JS_ASSERT(!XMLARRAY_HAS_MEMBER(&xml->xml_namespaces, ns,
1608     namespace_identity));
1609     ns = NewXMLNamespace(cx, GetPrefix(ns), GetURI(ns), JS_FALSE);
1610     if (!ns)
1611     goto fail;
1612     if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
1613     goto fail;
1614     }
1615     XMLArrayTrim(&xml->xml_namespaces);
1616    
1617     /* Second pass: process tag name and attributes, using namespaces. */
1618     pn2 = pn->pn_head;
1619     qn = ParseNodeToQName(cx, pc, pn2, inScopeNSes, JS_FALSE);
1620     if (!qn)
1621     goto fail;
1622     xml->name = qn;
1623    
1624     JS_ASSERT((n & 1) == 0);
1625     n >>= 1;
1626     if (!XMLArraySetCapacity(cx, &xml->xml_attrs, n))
1627     goto fail;
1628    
1629     for (i = 0; (pn2 = pn2->pn_next) != NULL; i++) {
1630     qn = ParseNodeToQName(cx, pc, pn2, inScopeNSes, JS_TRUE);
1631     if (!qn) {
1632     xml->xml_attrs.length = i;
1633     goto fail;
1634     }
1635    
1636     /*
1637     * Enforce "Well-formedness constraint: Unique Att Spec", part 2:
1638     * this time checking local name and namespace URI.
1639     */
1640     for (j = 0; j < i; j++) {
1641     attrj = XMLARRAY_MEMBER(&xml->xml_attrs, j, JSXML);
1642     attrjqn = attrj->name;
1643     if (js_EqualStrings(GetURI(attrjqn), GetURI(qn)) &&
1644     js_EqualStrings(GetLocalName(attrjqn), GetLocalName(qn))) {
1645     js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn2,
1646     JSREPORT_ERROR,
1647     JSMSG_DUPLICATE_XML_ATTR,
1648     js_ValueToPrintableString(cx,
1649     ATOM_KEY(pn2->pn_atom)));
1650     goto fail;
1651     }
1652     }
1653    
1654     pn2 = pn2->pn_next;
1655     JS_ASSERT(pn2);
1656     JS_ASSERT(pn2->pn_type == TOK_XMLATTR);
1657    
1658     attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE);
1659     if (!attr)
1660     goto fail;
1661    
1662     XMLARRAY_SET_MEMBER(&xml->xml_attrs, i, attr);
1663     attr->parent = xml;
1664     attr->name = qn;
1665     attr->xml_value = ATOM_TO_STRING(pn2->pn_atom);
1666     }
1667    
1668     /* Point tag closes its own namespace scope. */
1669     if (pn->pn_type == TOK_XMLPTAGC)
1670     XMLARRAY_TRUNCATE(cx, inScopeNSes, length);
1671     break;
1672    
1673     case TOK_XMLSPACE:
1674     case TOK_XMLTEXT:
1675     case TOK_XMLCDATA:
1676     case TOK_XMLCOMMENT:
1677     case TOK_XMLPI:
1678     str = ATOM_TO_STRING(pn->pn_atom);
1679     qn = NULL;
1680     if (pn->pn_type == TOK_XMLCOMMENT) {
1681     if (flags & XSF_IGNORE_COMMENTS)
1682     goto skip_child;
1683     xml_class = JSXML_CLASS_COMMENT;
1684     } else if (pn->pn_type == TOK_XMLPI) {
1685     if (IS_XML(str)) {
1686     js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn,
1687     JSREPORT_ERROR,
1688     JSMSG_RESERVED_ID,
1689     js_ValueToPrintableString(cx,
1690     STRING_TO_JSVAL(str)));
1691     goto fail;
1692     }
1693    
1694     if (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS)
1695     goto skip_child;
1696    
1697     qn = ParseNodeToQName(cx, pc, pn, inScopeNSes, JS_FALSE);
1698     if (!qn)
1699     goto fail;
1700    
1701     str = pn->pn_atom2
1702     ? ATOM_TO_STRING(pn->pn_atom2)
1703     : cx->runtime->emptyString;
1704     xml_class = JSXML_CLASS_PROCESSING_INSTRUCTION;
1705     } else {
1706     /* CDATA section content, or element text. */
1707     xml_class = JSXML_CLASS_TEXT;
1708     }
1709    
1710     xml = js_NewXML(cx, xml_class);
1711     if (!xml)
1712     goto fail;
1713     xml->name = qn;
1714     if (pn->pn_type == TOK_XMLSPACE)
1715     xml->xml_flags |= XMLF_WHITESPACE_TEXT;
1716     xml->xml_value = str;
1717     break;
1718    
1719     default:
1720     goto syntax;
1721     }
1722    
1723     js_LeaveLocalRootScopeWithResult(cx, (jsval) xml);
1724     if ((flags & XSF_PRECOMPILED_ROOT) && !js_GetXMLObject(cx, xml))
1725     return NULL;
1726     return xml;
1727    
1728     skip_child:
1729     js_LeaveLocalRootScope(cx);
1730     return PN2X_SKIP_CHILD;
1731    
1732     #undef PN2X_SKIP_CHILD
1733    
1734     syntax:
1735     js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn, JSREPORT_ERROR,
1736     JSMSG_BAD_XML_MARKUP);
1737     fail:
1738     js_LeaveLocalRootScope(cx);
1739     return NULL;
1740     }
1741    
1742     /*
1743     * XML helper, object-ops, and library functions. We start with the helpers,
1744     * in ECMA-357 order, but merging XML (9.1) and XMLList (9.2) helpers.
1745     */
1746     static JSBool
1747     GetXMLSetting(JSContext *cx, const char *name, jsval *vp)
1748     {
1749     jsval v;
1750    
1751     if (!js_FindClassObject(cx, NULL, INT_TO_JSID(JSProto_XML), &v))
1752     return JS_FALSE;
1753     if (!VALUE_IS_FUNCTION(cx, v)) {
1754     *vp = JSVAL_VOID;
1755     return JS_TRUE;
1756     }
1757     return JS_GetProperty(cx, JSVAL_TO_OBJECT(v), name, vp);
1758     }
1759    
1760     static JSBool
1761     FillSettingsCache(JSContext *cx)
1762     {
1763     int i;
1764     const char *name;
1765     jsval v;
1766    
1767     /* Note: XML_PRETTY_INDENT is not a boolean setting. */
1768     for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) {
1769     name = xml_static_props[i].name;
1770     if (!GetXMLSetting(cx, name, &v))
1771     return JS_FALSE;
1772     if (js_ValueToBoolean(v))
1773     cx->xmlSettingFlags |= JS_BIT(i);
1774     else
1775     cx->xmlSettingFlags &= ~JS_BIT(i);
1776     }
1777    
1778     cx->xmlSettingFlags |= XSF_CACHE_VALID;
1779     return JS_TRUE;
1780     }
1781    
1782     static JSBool
1783     GetBooleanXMLSetting(JSContext *cx, const char *name, JSBool *bp)
1784     {
1785     int i;
1786    
1787     if (!(cx->xmlSettingFlags & XSF_CACHE_VALID) && !FillSettingsCache(cx))
1788     return JS_FALSE;
1789    
1790     for (i = 0; xml_static_props[i].name; i++) {
1791     if (!strcmp(xml_static_props[i].name, name)) {
1792     *bp = (cx->xmlSettingFlags & JS_BIT(i)) != 0;
1793     return JS_TRUE;
1794     }
1795     }
1796     *bp = JS_FALSE;
1797     return JS_TRUE;
1798     }
1799    
1800     static JSBool
1801     GetUint32XMLSetting(JSContext *cx, const char *name, uint32 *uip)
1802     {
1803     jsval v;
1804    
1805     return GetXMLSetting(cx, name, &v) && JS_ValueToECMAUint32(cx, v, uip);
1806     }
1807    
1808     static JSBool
1809     GetXMLSettingFlags(JSContext *cx, uintN *flagsp)
1810     {
1811     JSBool flag;
1812    
1813     /* Just get the first flag to validate the setting flags cache. */
1814     if (!GetBooleanXMLSetting(cx, js_ignoreComments_str, &flag))
1815     return JS_FALSE;
1816     *flagsp = cx->xmlSettingFlags;
1817     return JS_TRUE;
1818     }
1819    
1820     static JSXML *
1821     ParseXMLSource(JSContext *cx, JSString *src)
1822     {
1823     jsval nsval;
1824     JSString *uri;
1825     size_t urilen, srclen, length, offset, dstlen;
1826     jschar *chars;
1827     const jschar *srcp, *endp;
1828     JSXML *xml;
1829     JSParseContext pc;
1830     const char *filename;
1831     uintN lineno;
1832     JSStackFrame *fp;
1833     JSOp op;
1834     JSParseNode *pn;
1835     JSXMLArray nsarray;
1836     uintN flags;
1837    
1838 siliconforks 399 static const char prefix[] = "<parent xmlns=\"";
1839     static const char middle[] = "\">";
1840 siliconforks 332 static const char suffix[] = "</parent>";
1841    
1842     #define constrlen(constr) (sizeof(constr) - 1)
1843    
1844     if (!js_GetDefaultXMLNamespace(cx, &nsval))
1845     return NULL;
1846     uri = GetURI(JSVAL_TO_OBJECT(nsval));
1847 siliconforks 399 uri = js_EscapeAttributeValue(cx, uri, JS_FALSE);
1848 siliconforks 332
1849     urilen = JSSTRING_LENGTH(uri);
1850     srclen = JSSTRING_LENGTH(src);
1851     length = constrlen(prefix) + urilen + constrlen(middle) + srclen +
1852     constrlen(suffix);
1853    
1854     chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
1855     if (!chars)
1856     return NULL;
1857    
1858     dstlen = length;
1859     js_InflateStringToBuffer(cx, prefix, constrlen(prefix), chars, &dstlen);
1860     offset = dstlen;
1861     js_strncpy(chars + offset, JSSTRING_CHARS(uri), urilen);
1862     offset += urilen;
1863     dstlen = length - offset + 1;
1864     js_InflateStringToBuffer(cx, middle, constrlen(middle), chars + offset,
1865     &dstlen);
1866     offset += dstlen;
1867     srcp = JSSTRING_CHARS(src);
1868     js_strncpy(chars + offset, srcp, srclen);
1869     offset += srclen;
1870     dstlen = length - offset + 1;
1871     js_InflateStringToBuffer(cx, suffix, constrlen(suffix), chars + offset,
1872     &dstlen);
1873     chars [offset + dstlen] = 0;
1874    
1875     xml = NULL;
1876     for (fp = cx->fp; fp && !fp->regs; fp = fp->down)
1877     JS_ASSERT(!fp->script);
1878     filename = NULL;
1879     lineno = 1;
1880     if (fp) {
1881     op = (JSOp) *fp->regs->pc;
1882     if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) {
1883     filename = fp->script->filename;
1884 siliconforks 399 lineno = js_FramePCToLineNumber(cx, fp);
1885 siliconforks 332 for (endp = srcp + srclen; srcp < endp; srcp++) {
1886     if (*srcp == '\n')
1887     --lineno;
1888     }
1889     }
1890     }
1891    
1892     if (!js_InitParseContext(cx, &pc, NULL, NULL, chars, length, NULL,
1893     filename, lineno))
1894     goto out;
1895     pn = js_ParseXMLText(cx, cx->fp->scopeChain, &pc, JS_FALSE);
1896     if (pn && XMLArrayInit(cx, &nsarray, 1)) {
1897     if (GetXMLSettingFlags(cx, &flags))
1898     xml = ParseNodeToXML(cx, &pc, pn, &nsarray, flags);
1899    
1900     XMLArrayFinish(cx, &nsarray);
1901     }
1902     js_FinishParseContext(cx, &pc);
1903    
1904     out:
1905     JS_free(cx, chars);
1906     return xml;
1907    
1908     #undef constrlen
1909     }
1910    
1911     /*
1912     * Errata in 10.3.1, 10.4.1, and 13.4.4.24 (at least).
1913     *
1914     * 10.3.1 Step 6(a) fails to NOTE that implementations that do not enforce
1915     * the constraint:
1916     *
1917     * for all x belonging to XML:
1918     * x.[[InScopeNamespaces]] >= x.[[Parent]].[[InScopeNamespaces]]
1919     *
1920     * must union x.[[InScopeNamespaces]] into x[0].[[InScopeNamespaces]] here
1921     * (in new sub-step 6(a), renumbering the others to (b) and (c)).
1922     *
1923     * Same goes for 10.4.1 Step 7(a).
1924     *
1925     * In order for XML.prototype.namespaceDeclarations() to work correctly, the
1926     * default namespace thereby unioned into x[0].[[InScopeNamespaces]] must be
1927     * flagged as not declared, so that 13.4.4.24 Step 8(a) can exclude all such
1928     * undeclared namespaces associated with x not belonging to ancestorNS.
1929     */
1930     static JSXML *
1931     OrphanXMLChild(JSContext *cx, JSXML *xml, uint32 i)
1932     {
1933     JSObject *ns;
1934    
1935     ns = XMLARRAY_MEMBER(&xml->xml_namespaces, 0, JSObject);
1936     xml = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
1937     if (!ns || !xml)
1938     return xml;
1939     if (xml->xml_class == JSXML_CLASS_ELEMENT) {
1940     if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
1941     return NULL;
1942     ns->fslots[JSSLOT_DECLARED] = JSVAL_VOID;
1943     }
1944     xml->parent = NULL;
1945     return xml;
1946     }
1947    
1948     static JSObject *
1949     ToXML(JSContext *cx, jsval v)
1950     {
1951     JSObject *obj;
1952     JSXML *xml;
1953     JSClass *clasp;
1954     JSString *str;
1955     uint32 length;
1956    
1957     if (JSVAL_IS_PRIMITIVE(v)) {
1958     if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
1959     goto bad;
1960     } else {
1961     obj = JSVAL_TO_OBJECT(v);
1962     if (OBJECT_IS_XML(cx, obj)) {
1963     xml = (JSXML *) JS_GetPrivate(cx, obj);
1964     if (xml->xml_class == JSXML_CLASS_LIST) {
1965     if (xml->xml_kids.length != 1)
1966     goto bad;
1967     xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
1968     if (xml) {
1969     JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST);
1970     return js_GetXMLObject(cx, xml);
1971     }
1972     }
1973     return obj;
1974     }
1975    
1976     clasp = OBJ_GET_CLASS(cx, obj);
1977     if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) {
1978     JS_ASSERT(0);
1979     }
1980    
1981     if (clasp != &js_StringClass &&
1982     clasp != &js_NumberClass &&
1983     clasp != &js_BooleanClass) {
1984     goto bad;
1985     }
1986     }
1987    
1988     str = js_ValueToString(cx, v);
1989     if (!str)
1990     return NULL;
1991     if (IS_EMPTY(str)) {
1992     length = 0;
1993     #ifdef __GNUC__ /* suppress bogus gcc warnings */
1994     xml = NULL;
1995     #endif
1996     } else {
1997     xml = ParseXMLSource(cx, str);
1998     if (!xml)
1999     return NULL;
2000     length = JSXML_LENGTH(xml);
2001     }
2002    
2003     if (length == 0) {
2004     obj = js_NewXMLObject(cx, JSXML_CLASS_TEXT);
2005     if (!obj)
2006     return NULL;
2007     } else if (length == 1) {
2008     xml = OrphanXMLChild(cx, xml, 0);
2009     if (!xml)
2010     return NULL;
2011     obj = js_GetXMLObject(cx, xml);
2012     if (!obj)
2013     return NULL;
2014     } else {
2015     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SYNTAX_ERROR);
2016     return NULL;
2017     }
2018     return obj;
2019    
2020     bad:
2021     js_ReportValueError(cx, JSMSG_BAD_XML_CONVERSION,
2022     JSDVG_IGNORE_STACK, v, NULL);
2023     return NULL;
2024     }
2025    
2026     static JSBool
2027     Append(JSContext *cx, JSXML *list, JSXML *kid);
2028    
2029     static JSObject *
2030     ToXMLList(JSContext *cx, jsval v)
2031     {
2032     JSObject *obj, *listobj;
2033     JSXML *xml, *list, *kid;
2034     JSClass *clasp;
2035     JSString *str;
2036     uint32 i, length;
2037    
2038     if (JSVAL_IS_PRIMITIVE(v)) {
2039     if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
2040     goto bad;
2041     } else {
2042     obj = JSVAL_TO_OBJECT(v);
2043     if (OBJECT_IS_XML(cx, obj)) {
2044     xml = (JSXML *) JS_GetPrivate(cx, obj);
2045     if (xml->xml_class != JSXML_CLASS_LIST) {
2046     listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
2047     if (!listobj)
2048     return NULL;
2049     list = (JSXML *) JS_GetPrivate(cx, listobj);
2050     if (!Append(cx, list, xml))
2051     return NULL;
2052     return listobj;
2053     }
2054     return obj;
2055     }
2056    
2057     clasp = OBJ_GET_CLASS(cx, obj);
2058     if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) {
2059     JS_ASSERT(0);
2060     }
2061    
2062     if (clasp != &js_StringClass &&
2063     clasp != &js_NumberClass &&
2064     clasp != &js_BooleanClass) {
2065     goto bad;
2066     }
2067     }
2068    
2069     str = js_ValueToString(cx, v);
2070     if (!str)
2071     return NULL;
2072     if (IS_EMPTY(str)) {
2073     xml = NULL;
2074     length = 0;
2075     } else {
2076     if (!js_EnterLocalRootScope(cx))
2077     return NULL;
2078     xml = ParseXMLSource(cx, str);
2079     if (!xml) {
2080     js_LeaveLocalRootScope(cx);
2081     return NULL;
2082     }
2083     length = JSXML_LENGTH(xml);
2084     }
2085    
2086     listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
2087     if (listobj) {
2088     list = (JSXML *) JS_GetPrivate(cx, listobj);
2089     for (i = 0; i < length; i++) {
2090     kid = OrphanXMLChild(cx, xml, i);
2091     if (!kid || !Append(cx, list, kid)) {
2092     listobj = NULL;
2093     break;
2094     }
2095     }
2096     }
2097    
2098     if (xml)
2099     js_LeaveLocalRootScopeWithResult(cx, (jsval) listobj);
2100     return listobj;
2101    
2102     bad:
2103     js_ReportValueError(cx, JSMSG_BAD_XMLLIST_CONVERSION,
2104     JSDVG_IGNORE_STACK, v, NULL);
2105     return NULL;
2106     }
2107    
2108     /*
2109     * ECMA-357 10.2.1 Steps 5-7 pulled out as common subroutines of XMLToXMLString
2110     * and their library-public js_* counterparts. The guts of MakeXMLCDataString,
2111     * MakeXMLCommentString, and MakeXMLPIString are further factored into a common
2112     * MakeXMLSpecialString subroutine.
2113     *
2114     * These functions take ownership of sb->base, if sb is non-null, in all cases
2115     * of success or failure.
2116     */
2117     static JSString *
2118     MakeXMLSpecialString(JSContext *cx, JSStringBuffer *sb,
2119     JSString *str, JSString *str2,
2120     const jschar *prefix, size_t prefixlength,
2121     const jschar *suffix, size_t suffixlength)
2122     {
2123     JSStringBuffer localSB;
2124     size_t length, length2, newlength;
2125     jschar *bp, *base;
2126    
2127     if (!sb) {
2128     sb = &localSB;
2129     js_InitStringBuffer(sb);
2130     }
2131    
2132     length = JSSTRING_LENGTH(str);
2133     length2 = str2 ? JSSTRING_LENGTH(str2) : 0;
2134     newlength = STRING_BUFFER_OFFSET(sb) +
2135     prefixlength + length + ((length2 != 0) ? 1 + length2 : 0) +
2136     suffixlength;
2137     bp = base = (jschar *)
2138     JS_realloc(cx, sb->base, (newlength + 1) * sizeof(jschar));
2139     if (!bp) {
2140     js_FinishStringBuffer(sb);
2141     return NULL;
2142     }
2143    
2144     bp += STRING_BUFFER_OFFSET(sb);
2145     js_strncpy(bp, prefix, prefixlength);
2146     bp += prefixlength;
2147     js_strncpy(bp, JSSTRING_CHARS(str), length);
2148     bp += length;
2149     if (length2 != 0) {
2150     *bp++ = (jschar) ' ';
2151     js_strncpy(bp, JSSTRING_CHARS(str2), length2);
2152     bp += length2;
2153     }
2154     js_strncpy(bp, suffix, suffixlength);
2155     bp[suffixlength] = 0;
2156    
2157     str = js_NewString(cx, base, newlength);
2158     if (!str)
2159     free(base);
2160     return str;
2161     }
2162    
2163     static JSString *
2164     MakeXMLCDATAString(JSContext *cx, JSStringBuffer *sb, JSString *str)
2165     {
2166     static const jschar cdata_prefix_ucNstr[] = {'<', '!', '[',
2167     'C', 'D', 'A', 'T', 'A',
2168     '['};
2169     static const jschar cdata_suffix_ucNstr[] = {']', ']', '>'};
2170    
2171     return MakeXMLSpecialString(cx, sb, str, NULL,
2172     cdata_prefix_ucNstr, 9,
2173     cdata_suffix_ucNstr, 3);
2174     }
2175    
2176     static JSString *
2177     MakeXMLCommentString(JSContext *cx, JSStringBuffer *sb, JSString *str)
2178     {
2179     static const jschar comment_prefix_ucNstr[] = {'<', '!', '-', '-'};
2180     static const jschar comment_suffix_ucNstr[] = {'-', '-', '>'};
2181    
2182     return MakeXMLSpecialString(cx, sb, str, NULL,
2183     comment_prefix_ucNstr, 4,
2184     comment_suffix_ucNstr, 3);
2185     }
2186    
2187     static JSString *
2188     MakeXMLPIString(JSContext *cx, JSStringBuffer *sb, JSString *name,
2189     JSString *value)
2190     {
2191     static const jschar pi_prefix_ucNstr[] = {'<', '?'};
2192     static const jschar pi_suffix_ucNstr[] = {'?', '>'};
2193    
2194     return MakeXMLSpecialString(cx, sb, name, value,
2195     pi_prefix_ucNstr, 2,
2196     pi_suffix_ucNstr, 2);
2197     }
2198    
2199     /*
2200     * ECMA-357 10.2.1 17(d-g) pulled out into a common subroutine that appends
2201     * equals, a double quote, an attribute value, and a closing double quote.
2202     */
2203     static void
2204     AppendAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *valstr)
2205     {
2206     js_AppendChar(sb, '=');
2207     valstr = js_EscapeAttributeValue(cx, valstr, JS_TRUE);
2208     if (!valstr) {
2209     if (STRING_BUFFER_OK(sb)) {
2210     free(sb->base);
2211     sb->base = STRING_BUFFER_ERROR_BASE;
2212     }
2213     return;
2214     }
2215     js_AppendJSString(sb, valstr);
2216     }
2217    
2218     /*
2219     * ECMA-357 10.2.1.1 EscapeElementValue helper method.
2220     *
2221     * This function takes ownership of sb->base, if sb is non-null, in all cases
2222     * of success or failure.
2223     */
2224     static JSString *
2225     EscapeElementValue(JSContext *cx, JSStringBuffer *sb, JSString *str)
2226     {
2227     size_t length, newlength;
2228     const jschar *cp, *start, *end;
2229     jschar c;
2230    
2231     JSSTRING_CHARS_AND_LENGTH(str, start, length);
2232     newlength = length;
2233     for (cp = start, end = cp + length; cp < end; cp++) {
2234     c = *cp;
2235     if (c == '<' || c == '>')
2236     newlength += 3;
2237     else if (c == '&')
2238     newlength += 4;
2239    
2240     if (newlength < length) {
2241     js_ReportAllocationOverflow(cx);
2242     return NULL;
2243     }
2244     }
2245     if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) {
2246     JSStringBuffer localSB;
2247     if (!sb) {
2248     sb = &localSB;
2249     js_InitStringBuffer(sb);
2250     }
2251     if (!sb->grow(sb, newlength)) {
2252     JS_ReportOutOfMemory(cx);
2253     return NULL;
2254     }
2255     for (cp = start; cp < end; cp++) {
2256     c = *cp;
2257     if (c == '<')
2258     js_AppendCString(sb, js_lt_entity_str);
2259     else if (c == '>')
2260     js_AppendCString(sb, js_gt_entity_str);
2261     else if (c == '&')
2262     js_AppendCString(sb, js_amp_entity_str);
2263     else
2264     js_AppendChar(sb, c);
2265     }
2266     JS_ASSERT(STRING_BUFFER_OK(sb));
2267     str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb));
2268     if (!str)
2269     js_FinishStringBuffer(sb);
2270     }
2271     return str;
2272     }
2273    
2274     /*
2275     * ECMA-357 10.2.1.2 EscapeAttributeValue helper method.
2276     * This function takes ownership of sb->base, if sb is non-null, in all cases.
2277     */
2278     static JSString *
2279     EscapeAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *str,
2280     JSBool quote)
2281     {
2282     size_t length, newlength;
2283     const jschar *cp, *start, *end;
2284     jschar c;
2285    
2286     JSSTRING_CHARS_AND_LENGTH(str, start, length);
2287     newlength = length + (quote ? 2 : 0);
2288     for (cp = start, end = cp + length; cp < end; cp++) {
2289     c = *cp;
2290     if (c == '"')
2291     newlength += 5;
2292     else if (c == '<')
2293     newlength += 3;
2294     else if (c == '&' || c == '\n' || c == '\r' || c == '\t')
2295     newlength += 4;
2296    
2297     if (newlength < length) {
2298     js_ReportAllocationOverflow(cx);
2299     return NULL;
2300     }
2301     }
2302     if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) {
2303     JSStringBuffer localSB;
2304     if (!sb) {
2305     sb = &localSB;
2306     js_InitStringBuffer(sb);
2307     }
2308     if (!sb->grow(sb, newlength)) {
2309     JS_ReportOutOfMemory(cx);
2310     return NULL;
2311     }
2312     if (quote)
2313     js_AppendChar(sb, '"');
2314     for (cp = start; cp < end; cp++) {
2315     c = *cp;
2316     if (c == '"')
2317     js_AppendCString(sb, js_quot_entity_str);
2318     else if (c == '<')
2319     js_AppendCString(sb, js_lt_entity_str);
2320     else if (c == '&')
2321     js_AppendCString(sb, js_amp_entity_str);
2322     else if (c == '\n')
2323     js_AppendCString(sb, "&#xA;");
2324     else if (c == '\r')
2325     js_AppendCString(sb, "&#xD;");
2326     else if (c == '\t')
2327     js_AppendCString(sb, "&#x9;");
2328     else
2329     js_AppendChar(sb, c);
2330     }
2331     if (quote)
2332     js_AppendChar(sb, '"');
2333     JS_ASSERT(STRING_BUFFER_OK(sb));
2334     str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb));
2335     if (!str)
2336     js_FinishStringBuffer(sb);
2337     }
2338     return str;
2339     }
2340    
2341     /* 13.3.5.4 [[GetNamespace]]([InScopeNamespaces]) */
2342     static JSObject *
2343     GetNamespace(JSContext *cx, JSObject *qn, const JSXMLArray *inScopeNSes)
2344     {
2345     JSString *uri, *prefix, *nsprefix;
2346     JSObject *match, *ns;
2347     uint32 i, n;
2348     jsval argv[2];
2349    
2350     uri = GetURI(qn);
2351     prefix = GetPrefix(qn);
2352     JS_ASSERT(uri);
2353     if (!uri) {
2354     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2355     JSMSG_BAD_XML_NAMESPACE,
2356     prefix
2357     ? js_ValueToPrintableString(cx,
2358     STRING_TO_JSVAL(prefix))
2359     : js_undefined_str);
2360     return NULL;
2361     }
2362    
2363     /* Look for a matching namespace in inScopeNSes, if provided. */
2364     match = NULL;
2365     if (inScopeNSes) {
2366     for (i = 0, n = inScopeNSes->length; i < n; i++) {
2367     ns = XMLARRAY_MEMBER(inScopeNSes, i, JSObject);
2368     if (!ns)
2369     continue;
2370    
2371     /*
2372     * Erratum, very tricky, and not specified in ECMA-357 13.3.5.4:
2373     * If we preserve prefixes, we must match null prefix against
2374     * an empty prefix of ns, in order to avoid generating redundant
2375     * prefixed and default namespaces for cases such as:
2376     *
2377     * x = <t xmlns="http://foo.com"/>
2378     * print(x.toXMLString());
2379     *
2380     * Per 10.3.2.1, the namespace attribute in t has an empty string
2381     * prefix (*not* a null prefix), per 10.3.2.1 Step 6(h)(i)(1):
2382     *
2383     * 1. If the [local name] property of a is "xmlns"
2384     * a. Map ns.prefix to the empty string
2385     *
2386     * But t's name has a null prefix in this implementation, meaning
2387     * *undefined*, per 10.3.2.1 Step 6(c)'s NOTE (which refers to
2388     * the http://www.w3.org/TR/xml-infoset/ spec, item 2.2.3, without
2389     * saying how "no value" maps to an ECMA-357 value -- but it must
2390     * map to the *undefined* prefix value).
2391     *
2392     * Since "" != undefined (or null, in the current implementation)
2393     * the ECMA-357 spec will fail to match in [[GetNamespace]] called
2394     * on t with argument {} U {(prefix="", uri="http://foo.com")}.
2395     * This spec bug leads to ToXMLString results that duplicate the
2396     * declared namespace.
2397     */
2398     if (js_EqualStrings(GetURI(ns), uri)) {
2399     nsprefix = GetPrefix(ns);
2400     if (nsprefix == prefix ||
2401     ((nsprefix && prefix)
2402     ? js_EqualStrings(nsprefix, prefix)
2403     : IS_EMPTY(nsprefix ? nsprefix : prefix))) {
2404     match = ns;
2405     break;
2406     }
2407     }
2408     }
2409     }
2410    
2411     /* If we didn't match, make a new namespace from qn. */
2412     if (!match) {
2413     argv[0] = prefix ? STRING_TO_JSVAL(prefix) : JSVAL_VOID;
2414     argv[1] = STRING_TO_JSVAL(uri);
2415     ns = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, NULL,
2416     2, argv);
2417     if (!ns)
2418     return NULL;
2419     match = ns;
2420     }
2421     return match;
2422     }
2423    
2424     static JSString *
2425     GeneratePrefix(JSContext *cx, JSString *uri, JSXMLArray *decls)
2426     {
2427     const jschar *cp, *start, *end;
2428     size_t length, newlength, offset;
2429     uint32 i, n, m, serial;
2430     jschar *bp, *dp;
2431     JSBool done;
2432     JSObject *ns;
2433     JSString *nsprefix, *prefix;
2434    
2435     JS_ASSERT(!IS_EMPTY(uri));
2436    
2437     /*
2438     * If there are no *declared* namespaces, skip all collision detection and
2439     * return a short prefix quickly; an example of such a situation:
2440     *
2441     * var x = <f/>;
2442     * var n = new Namespace("http://example.com/");
2443     * x.@n::att = "val";
2444     * x.toXMLString();
2445     *
2446     * This is necessary for various log10 uses below to be valid.
2447     */
2448     if (decls->length == 0)
2449     return JS_NewStringCopyZ(cx, "a");
2450    
2451     /*
2452     * Try peeling off the last filename suffix or pathname component till
2453     * we have a valid XML name. This heuristic will prefer "xul" given
2454     * ".../there.is.only.xul", "xbl" given ".../xbl", and "xbl2" given any
2455     * likely URI of the form ".../xbl2/2005".
2456     */
2457     JSSTRING_CHARS_AND_END(uri, start, end);
2458     cp = end;
2459     while (--cp > start) {
2460     if (*cp == '.' || *cp == '/' || *cp == ':') {
2461     ++cp;
2462     length = PTRDIFF(end, cp, jschar);
2463     if (IsXMLName(cp, length) && !STARTS_WITH_XML(cp, length))
2464     break;
2465     end = --cp;
2466     }
2467     }
2468     length = PTRDIFF(end, cp, jschar);
2469    
2470     /*
2471     * If the namespace consisted only of non-XML names or names that begin
2472     * case-insensitively with "xml", arbitrarily create a prefix consisting
2473     * of 'a's of size length (allowing dp-calculating code to work with or
2474     * without this branch executing) plus the space for storing a hyphen and
2475     * the serial number (avoiding reallocation if a collision happens).
2476     */
2477     bp = (jschar *) cp;
2478     newlength = length;
2479     if (STARTS_WITH_XML(cp, length) || !IsXMLName(cp, length)) {
2480     newlength = length + 2 + (size_t) log10((double) decls->length);
2481     bp = (jschar *)
2482     JS_malloc(cx, (newlength + 1) * sizeof(jschar));
2483     if (!bp)
2484     return NULL;
2485    
2486     bp[newlength] = 0;
2487     for (i = 0; i < newlength; i++)
2488     bp[i] = 'a';
2489     }
2490    
2491     /*
2492     * Now search through decls looking for a collision. If we collide with
2493     * an existing prefix, start tacking on a hyphen and a serial number.
2494     */
2495     serial = 0;
2496     do {
2497     done = JS_TRUE;
2498     for (i = 0, n = decls->length; i < n; i++) {
2499     ns = XMLARRAY_MEMBER(decls, i, JSObject);
2500     if (ns && (nsprefix = GetPrefix(ns)) &&
2501     JSSTRING_LENGTH(nsprefix) == newlength &&
2502     !memcmp(JSSTRING_CHARS(nsprefix), bp,
2503     newlength * sizeof(jschar))) {
2504     if (bp == cp) {
2505     newlength = length + 2 + (size_t) log10((double) n);
2506     bp = (jschar *)
2507     JS_malloc(cx, (newlength + 1) * sizeof(jschar));
2508     if (!bp)
2509     return NULL;
2510     js_strncpy(bp, cp, length);
2511     }
2512    
2513     ++serial;
2514     JS_ASSERT(serial <= n);
2515     dp = bp + length + 2 + (size_t) log10((double) serial);
2516     *dp = 0;
2517     for (m = serial; m != 0; m /= 10)
2518     *--dp = (jschar)('0' + m % 10);
2519     *--dp = '-';
2520     JS_ASSERT(dp == bp + length);
2521    
2522     done = JS_FALSE;
2523     break;
2524     }
2525     }
2526     } while (!done);
2527    
2528     if (bp == cp) {
2529     offset = PTRDIFF(cp, start, jschar);
2530     prefix = js_NewDependentString(cx, uri, offset, length);
2531     } else {
2532     prefix = js_NewString(cx, bp, newlength);
2533     if (!prefix)
2534     JS_free(cx, bp);
2535     }
2536     return prefix;
2537     }
2538    
2539     static JSBool
2540     namespace_match(const void *a, const void *b)
2541     {
2542     const JSObject *nsa = (const JSObject *) a;
2543     const JSObject *nsb = (const JSObject *) b;
2544     JSString *prefixa, *prefixb = GetPrefix(nsb);
2545    
2546     if (prefixb) {
2547     prefixa = GetPrefix(nsa);
2548     return prefixa && js_EqualStrings(prefixa, prefixb);
2549     }
2550     return js_EqualStrings(GetURI(nsa), GetURI(nsb));
2551     }
2552    
2553     /* ECMA-357 10.2.1 and 10.2.2 */
2554     #define TO_SOURCE_FLAG 0x80000000
2555    
2556     static JSString *
2557     XMLToXMLString(JSContext *cx, JSXML *xml, const JSXMLArray *ancestorNSes,
2558     uint32 indentLevel)
2559     {
2560     JSBool pretty, indentKids;
2561     JSStringBuffer sb;
2562     JSString *str, *prefix, *kidstr, *nsuri;
2563     JSXMLArrayCursor cursor;
2564     uint32 i, n, nextIndentLevel;
2565     JSXMLArray empty, decls, ancdecls;
2566     JSObject *ns, *ns2;
2567     JSXML *attr, *kid;
2568    
2569     if (!GetBooleanXMLSetting(cx, js_prettyPrinting_str, &pretty))
2570     return NULL;
2571    
2572     js_InitStringBuffer(&sb);
2573     if (pretty) {
2574     js_RepeatChar(&sb, ' ', indentLevel & ~TO_SOURCE_FLAG);
2575    
2576     if (!STRING_BUFFER_OK(&sb)) {<