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

Annotation of /trunk/js/jsxml.cpp

Parent Directory Parent Directory | Revision Log Revision Log


Revision 332 - (hide annotations)
Thu Oct 23 19:03:33 2008 UTC (11 years, 11 months ago) by siliconforks
File size: 251191 byte(s)
Add SpiderMonkey from Firefox 3.1b1.

The following directories and files were removed:
correct/, correct.js
liveconnect/
nanojit/
t/
v8/
vprof/
xpconnect/
all JavaScript files (Y.js, call.js, if.js, math-partial-sums.js, md5.js, perfect.js, trace-test.js, trace.js)


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     static const char prefix[] = "<parent xmlns='";
1839     static const char middle[] = "'>";
1840     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    
1848     urilen = JSSTRING_LENGTH(uri);
1849     srclen = JSSTRING_LENGTH(src);
1850     length = constrlen(prefix) + urilen + constrlen(middle) + srclen +
1851     constrlen(suffix);
1852    
1853     chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
1854     if (!chars)
1855     return NULL;
1856    
1857     dstlen = length;
1858     js_InflateStringToBuffer(cx, prefix, constrlen(prefix), chars, &dstlen);
1859     offset = dstlen;
1860     js_strncpy(chars + offset, JSSTRING_CHARS(uri), urilen);
1861     offset += urilen;
1862     dstlen = length - offset + 1;
1863     js_InflateStringToBuffer(cx, middle, constrlen(middle), chars + offset,
1864     &dstlen);
1865     offset += dstlen;
1866     srcp = JSSTRING_CHARS(src);
1867     js_strncpy(chars + offset, srcp, srclen);
1868     offset += srclen;
1869     dstlen = length - offset + 1;
1870     js_InflateStringToBuffer(cx, suffix, constrlen(suffix), chars + offset,
1871     &dstlen);
1872     chars [offset + dstlen] = 0;
1873    
1874     xml = NULL;
1875     for (fp = cx->fp; fp && !fp->regs; fp = fp->down)
1876     JS_ASSERT(!fp->script);
1877     filename = NULL;
1878     lineno = 1;
1879     if (fp) {
1880     op = (JSOp) *fp->regs->pc;
1881     if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) {
1882     filename = fp->script->filename;
1883     lineno = js_PCToLineNumber(cx, fp->script, fp->regs->pc);
1884     for (endp = srcp + srclen; srcp < endp; srcp++) {
1885     if (*srcp == '\n')
1886     --lineno;
1887     }
1888     }
1889     }
1890    
1891     if (!js_InitParseContext(cx, &pc, NULL, NULL, chars, length, NULL,
1892     filename, lineno))
1893     goto out;
1894     pn = js_ParseXMLText(cx, cx->fp->scopeChain, &pc, JS_FALSE);
1895     if (pn && XMLArrayInit(cx, &nsarray, 1)) {
1896     if (GetXMLSettingFlags(cx, &flags))
1897     xml = ParseNodeToXML(cx, &pc, pn, &nsarray, flags);
1898    
1899     XMLArrayFinish(cx, &nsarray);
1900     }
1901     js_FinishParseContext(cx, &pc);
1902    
1903     out:
1904     JS_free(cx, chars);
1905     return xml;
1906    
1907     #undef constrlen
1908     }
1909    
1910     /*
1911     * Errata in 10.3.1, 10.4.1, and 13.4.4.24 (at least).
1912     *
1913     * 10.3.1 Step 6(a) fails to NOTE that implementations that do not enforce
1914     * the constraint:
1915     *
1916     * for all x belonging to XML:
1917     * x.[[InScopeNamespaces]] >= x.[[Parent]].[[InScopeNamespaces]]
1918     *
1919     * must union x.[[InScopeNamespaces]] into x[0].[[InScopeNamespaces]] here
1920     * (in new sub-step 6(a), renumbering the others to (b) and (c)).
1921     *
1922     * Same goes for 10.4.1 Step 7(a).
1923     *
1924     * In order for XML.prototype.namespaceDeclarations() to work correctly, the
1925     * default namespace thereby unioned into x[0].[[InScopeNamespaces]] must be
1926     * flagged as not declared, so that 13.4.4.24 Step 8(a) can exclude all such
1927     * undeclared namespaces associated with x not belonging to ancestorNS.
1928     */
1929     static JSXML *
1930     OrphanXMLChild(JSContext *cx, JSXML *xml, uint32 i)
1931     {
1932     JSObject *ns;
1933    
1934     ns = XMLARRAY_MEMBER(&xml->xml_namespaces, 0, JSObject);
1935     xml = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
1936     if (!ns || !xml)
1937     return xml;
1938     if (xml->xml_class == JSXML_CLASS_ELEMENT) {
1939     if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
1940     return NULL;
1941     ns->fslots[JSSLOT_DECLARED] = JSVAL_VOID;
1942     }
1943     xml->parent = NULL;
1944     return xml;
1945     }
1946    
1947     static JSObject *
1948     ToXML(JSContext *cx, jsval v)
1949     {
1950     JSObject *obj;
1951     JSXML *xml;
1952     JSClass *clasp;
1953     JSString *str;
1954     uint32 length;
1955    
1956     if (JSVAL_IS_PRIMITIVE(v)) {
1957     if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
1958     goto bad;
1959     } else {
1960     obj = JSVAL_TO_OBJECT(v);
1961     if (OBJECT_IS_XML(cx, obj)) {
1962     xml = (JSXML *) JS_GetPrivate(cx, obj);
1963     if (xml->xml_class == JSXML_CLASS_LIST) {
1964     if (xml->xml_kids.length != 1)
1965     goto bad;
1966     xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
1967     if (xml) {
1968     JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST);
1969     return js_GetXMLObject(cx, xml);
1970     }
1971     }
1972     return obj;
1973     }
1974    
1975     clasp = OBJ_GET_CLASS(cx, obj);
1976     if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) {
1977     JS_ASSERT(0);
1978     }
1979    
1980     if (clasp != &js_StringClass &&
1981     clasp != &js_NumberClass &&
1982     clasp != &js_BooleanClass) {
1983     goto bad;
1984     }
1985     }
1986    
1987     str = js_ValueToString(cx, v);
1988     if (!str)
1989     return NULL;
1990     if (IS_EMPTY(str)) {
1991     length = 0;
1992     #ifdef __GNUC__ /* suppress bogus gcc warnings */
1993     xml = NULL;
1994     #endif
1995     } else {
1996     xml = ParseXMLSource(cx, str);
1997     if (!xml)
1998     return NULL;
1999     length = JSXML_LENGTH(xml);
2000     }
2001    
2002     if (length == 0) {
2003     obj = js_NewXMLObject(cx, JSXML_CLASS_TEXT);
2004     if (!obj)
2005     return NULL;
2006     } else if (length == 1) {
2007     xml = OrphanXMLChild(cx, xml, 0);
2008     if (!xml)
2009     return NULL;
2010     obj = js_GetXMLObject(cx, xml);
2011     if (!obj)
2012     return NULL;
2013     } else {
2014     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SYNTAX_ERROR);
2015     return NULL;
2016     }
2017     return obj;
2018    
2019     bad:
2020     js_ReportValueError(cx, JSMSG_BAD_XML_CONVERSION,
2021     JSDVG_IGNORE_STACK, v, NULL);
2022     return NULL;
2023     }
2024    
2025     static JSBool
2026     Append(JSContext *cx, JSXML *list, JSXML *kid);
2027    
2028     static JSObject *
2029     ToXMLList(JSContext *cx, jsval v)
2030     {
2031     JSObject *obj, *listobj;
2032     JSXML *xml, *list, *kid;
2033     JSClass *clasp;
2034     JSString *str;
2035     uint32 i, length;
2036    
2037     if (JSVAL_IS_PRIMITIVE(v)) {
2038     if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
2039     goto bad;
2040     } else {
2041     obj = JSVAL_TO_OBJECT(v);
2042     if (OBJECT_IS_XML(cx, obj)) {
2043     xml = (JSXML *) JS_GetPrivate(cx, obj);
2044     if (xml->xml_class != JSXML_CLASS_LIST) {
2045     listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
2046     if (!listobj)
2047     return NULL;
2048     list = (JSXML *) JS_GetPrivate(cx, listobj);
2049     if (!Append(cx, list, xml))
2050     return NULL;
2051     return listobj;
2052     }
2053     return obj;
2054     }
2055    
2056     clasp = OBJ_GET_CLASS(cx, obj);
2057     if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) {
2058     JS_ASSERT(0);
2059     }
2060    
2061     if (clasp != &js_StringClass &&
2062     clasp != &js_NumberClass &&
2063     clasp != &js_BooleanClass) {
2064     goto bad;
2065     }
2066     }
2067    
2068     str = js_ValueToString(cx, v);
2069     if (!str)
2070     return NULL;
2071     if (IS_EMPTY(str)) {
2072     xml = NULL;
2073     length = 0;
2074     } else {
2075     if (!js_EnterLocalRootScope(cx))
2076     return NULL;
2077     xml = ParseXMLSource(cx, str);
2078     if (!xml) {
2079     js_LeaveLocalRootScope(cx);
2080     return NULL;
2081     }
2082     length = JSXML_LENGTH(xml);
2083     }
2084    
2085     listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
2086     if (listobj) {
2087     list = (JSXML *) JS_GetPrivate(cx, listobj);
2088     for (i = 0; i < length; i++) {
2089     kid = OrphanXMLChild(cx, xml, i);
2090     if (!kid || !Append(cx, list, kid)) {
2091     listobj = NULL;
2092     break;
2093     }
2094     }
2095     }
2096    
2097     if (xml)
2098     js_LeaveLocalRootScopeWithResult(cx, (jsval) listobj);
2099     return listobj;
2100    
2101     bad:
2102     js_ReportValueError(cx, JSMSG_BAD_XMLLIST_CONVERSION,
2103     JSDVG_IGNORE_STACK, v, NULL);
2104     return NULL;
2105     }
2106    
2107     /*
2108     * ECMA-357 10.2.1 Steps 5-7 pulled out as common subroutines of XMLToXMLString
2109     * and their library-public js_* counterparts. The guts of MakeXMLCDataString,
2110     * MakeXMLCommentString, and MakeXMLPIString are further factored into a common
2111     * MakeXMLSpecialString subroutine.
2112     *
2113     * These functions take ownership of sb->base, if sb is non-null, in all cases
2114     * of success or failure.
2115     */
2116     static JSString *
2117     MakeXMLSpecialString(JSContext *cx, JSStringBuffer *sb,
2118     JSString *str, JSString *str2,
2119     const jschar *prefix, size_t prefixlength,
2120     const jschar *suffix, size_t suffixlength)
2121     {
2122     JSStringBuffer localSB;
2123     size_t length, length2, newlength;
2124     jschar *bp, *base;
2125    
2126     if (!sb) {
2127     sb = &localSB;
2128     js_InitStringBuffer(sb);
2129     }
2130    
2131     length = JSSTRING_LENGTH(str);
2132     length2 = str2 ? JSSTRING_LENGTH(str2) : 0;
2133     newlength = STRING_BUFFER_OFFSET(sb) +
2134     prefixlength + length + ((length2 != 0) ? 1 + length2 : 0) +
2135     suffixlength;
2136     bp = base = (jschar *)
2137     JS_realloc(cx, sb->base, (newlength + 1) * sizeof(jschar));
2138     if (!bp) {
2139     js_FinishStringBuffer(sb);
2140     return NULL;
2141     }
2142    
2143     bp += STRING_BUFFER_OFFSET(sb);
2144     js_strncpy(bp, prefix, prefixlength);
2145     bp += prefixlength;
2146     js_strncpy(bp, JSSTRING_CHARS(str), length);
2147     bp += length;
2148     if (length2 != 0) {
2149     *bp++ = (jschar) ' ';
2150     js_strncpy(bp, JSSTRING_CHARS(str2), length2);
2151     bp += length2;
2152     }
2153     js_strncpy(bp, suffix, suffixlength);
2154     bp[suffixlength] = 0;
2155    
2156     str = js_NewString(cx, base, newlength);
2157     if (!str)
2158     free(base);
2159     return str;
2160     }
2161    
2162     static JSString *
2163     MakeXMLCDATAString(JSContext *cx, JSStringBuffer *sb, JSString *str)
2164     {
2165     static const jschar cdata_prefix_ucNstr[] = {'<', '!', '[',
2166     'C', 'D', 'A', 'T', 'A',
2167     '['};
2168     static const jschar cdata_suffix_ucNstr[] = {']', ']', '>'};
2169    
2170     return MakeXMLSpecialString(cx, sb, str, NULL,
2171     cdata_prefix_ucNstr, 9,
2172     cdata_suffix_ucNstr, 3);
2173     }
2174    
2175     static JSString *
2176     MakeXMLCommentString(JSContext *cx, JSStringBuffer *sb, JSString *str)
2177     {
2178     static const jschar comment_prefix_ucNstr[] = {'<', '!', '-', '-'};
2179     static const jschar comment_suffix_ucNstr[] = {'-', '-', '>'};
2180    
2181     return MakeXMLSpecialString(cx, sb, str, NULL,
2182     comment_prefix_ucNstr, 4,
2183     comment_suffix_ucNstr, 3);
2184     }
2185    
2186     static JSString *
2187     MakeXMLPIString(JSContext *cx, JSStringBuffer *sb, JSString *name,
2188     JSString *value)
2189     {
2190     static const jschar pi_prefix_ucNstr[] = {'<', '?'};
2191     static const jschar pi_suffix_ucNstr[] = {'?', '>'};
2192    
2193     return MakeXMLSpecialString(cx, sb, name, value,
2194     pi_prefix_ucNstr, 2,
2195     pi_suffix_ucNstr, 2);
2196     }
2197    
2198     /*
2199     * ECMA-357 10.2.1 17(d-g) pulled out into a common subroutine that appends
2200     * equals, a double quote, an attribute value, and a closing double quote.
2201     */
2202     static void
2203     AppendAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *valstr)
2204     {
2205     js_AppendChar(sb, '=');
2206     valstr = js_EscapeAttributeValue(cx, valstr, JS_TRUE);
2207     if (!valstr) {
2208     if (STRING_BUFFER_OK(sb)) {
2209     free(sb->base);
2210     sb->base = STRING_BUFFER_ERROR_BASE;
2211     }
2212     return;
2213     }
2214     js_AppendJSString(sb, valstr);
2215     }
2216    
2217     /*
2218     * ECMA-357 10.2.1.1 EscapeElementValue helper method.
2219     *
2220     * This function takes ownership of sb->base, if sb is non-null, in all cases
2221     * of success or failure.
2222     */
2223     static JSString *
2224     EscapeElementValue(JSContext *cx, JSStringBuffer *sb, JSString *str)
2225     {
2226     size_t length, newlength;
2227     const jschar *cp, *start, *end;
2228     jschar c;
2229    
2230     JSSTRING_CHARS_AND_LENGTH(str, start, length);
2231     newlength = length;
2232     for (cp = start, end = cp + length; cp < end; cp++) {
2233     c = *cp;
2234     if (c == '<' || c == '>')
2235     newlength += 3;
2236     else if (c == '&')
2237     newlength += 4;
2238    
2239     if (newlength < length) {
2240     js_ReportAllocationOverflow(cx);
2241     return NULL;
2242     }
2243     }
2244     if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) {
2245     JSStringBuffer localSB;
2246     if (!sb) {
2247     sb = &localSB;
2248     js_InitStringBuffer(sb);
2249     }
2250     if (!sb->grow(sb, newlength)) {
2251     JS_ReportOutOfMemory(cx);
2252     return NULL;
2253     }
2254     for (cp = start; cp < end; cp++) {
2255     c = *cp;
2256     if (c == '<')
2257     js_AppendCString(sb, js_lt_entity_str);
2258     else if (c == '>')
2259     js_AppendCString(sb, js_gt_entity_str);
2260     else if (c == '&')
2261     js_AppendCString(sb, js_amp_entity_str);
2262     else
2263     js_AppendChar(sb, c);
2264     }
2265     JS_ASSERT(STRING_BUFFER_OK(sb));
2266     str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb));
2267     if (!str)
2268     js_FinishStringBuffer(sb);
2269     }
2270     return str;
2271     }
2272    
2273     /*
2274     * ECMA-357 10.2.1.2 EscapeAttributeValue helper method.
2275     * This function takes ownership of sb->base, if sb is non-null, in all cases.
2276     */
2277     static JSString *
2278     EscapeAttributeValue(JSContext *cx, JSStringBuffer *sb, JSString *str,
2279     JSBool quote)
2280     {
2281     size_t length, newlength;
2282     const jschar *cp, *start, *end;
2283     jschar c;
2284    
2285     JSSTRING_CHARS_AND_LENGTH(str, start, length);
2286     newlength = length + (quote ? 2 : 0);
2287     for (cp = start, end = cp + length; cp < end; cp++) {
2288     c = *cp;
2289     if (c == '"')
2290     newlength += 5;
2291     else if (c == '<')
2292     newlength += 3;
2293     else if (c == '&' || c == '\n' || c == '\r' || c == '\t')
2294     newlength += 4;
2295    
2296     if (newlength < length) {
2297     js_ReportAllocationOverflow(cx);
2298     return NULL;
2299     }
2300     }
2301     if ((sb && STRING_BUFFER_OFFSET(sb) != 0) || newlength > length) {
2302     JSStringBuffer localSB;
2303     if (!sb) {
2304     sb = &localSB;
2305     js_InitStringBuffer(sb);
2306     }
2307     if (!sb->grow(sb, newlength)) {
2308     JS_ReportOutOfMemory(cx);
2309     return NULL;
2310     }
2311     if (quote)
2312     js_AppendChar(sb, '"');
2313     for (cp = start; cp < end; cp++) {
2314     c = *cp;
2315     if (c == '"')
2316     js_AppendCString(sb, js_quot_entity_str);
2317     else if (c == '<')
2318     js_AppendCString(sb, js_lt_entity_str);
2319     else if (c == '&')
2320     js_AppendCString(sb, js_amp_entity_str);
2321     else if (c == '\n')
2322     js_AppendCString(sb, "&#xA;");
2323     else if (c == '\r')
2324     js_AppendCString(sb, "&#xD;");
2325     else if (c == '\t')
2326     js_AppendCString(sb, "&#x9;");
2327     else
2328     js_AppendChar(sb, c);
2329     }
2330     if (quote)
2331     js_AppendChar(sb, '"');
2332     JS_ASSERT(STRING_BUFFER_OK(sb));
2333     str = js_NewString(cx, sb->base, STRING_BUFFER_OFFSET(sb));
2334     if (!str)
2335     js_FinishStringBuffer(sb);
2336     }
2337     return str;
2338     }
2339    
2340     /* 13.3.5.4 [[GetNamespace]]([InScopeNamespaces]) */
2341     static JSObject *
2342     GetNamespace(JSContext *cx, JSObject *qn, const JSXMLArray *inScopeNSes)
2343     {
2344     JSString *uri, *prefix, *nsprefix;
2345     JSObject *match, *ns;
2346     uint32 i, n;
2347     jsval argv[2];
2348    
2349     uri = GetURI(qn);
2350     prefix = GetPrefix(qn);
2351     JS_ASSERT(uri);
2352     if (!uri) {
2353     JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
2354     JSMSG_BAD_XML_NAMESPACE,
2355     prefix
2356     ? js_ValueToPrintableString(cx,
2357     STRING_TO_JSVAL(prefix))
2358     : js_undefined_str);
2359     return NULL;
2360     }
2361    
2362     /* Look for a matching namespace in inScopeNSes, if provided. */
2363     match = NULL;
2364     if (inScopeNSes) {
2365     for (i = 0, n = inScopeNSes->length; i < n; i++) {
2366     ns = XMLARRAY_MEMBER(inScopeNSes, i, JSObject);
2367     if (!ns)
2368     continue;
2369    
2370     /*
2371     * Erratum, very tricky, and not specified in ECMA-357 13.3.5.4:
2372     * If we preserve prefixes, we must match null prefix against
2373     * an empty prefix of ns, in order to avoid generating redundant
2374     * prefixed and default namespaces for cases such as:
2375     *
2376     * x = <t xmlns="http://foo.com"/>
2377     * print(x.toXMLString());
2378     *
2379     * Per 10.3.2.1, the namespace attribute in t has an empty string
2380     * prefix (*not* a null prefix), per 10.3.2.1 Step 6(h)(i)(1):
2381     *
2382     * 1. If the [local name] property of a is "xmlns"
2383     * a. Map ns.prefix to the empty string
2384     *
2385     * But t's name has a null prefix in this implementation, meaning
2386     * *undefined*, per 10.3.2.1 Step 6(c)'s NOTE (which refers to
2387     * the http://www.w3.org/TR/xml-infoset/ spec, item 2.2.3, without
2388     * saying how "no value" maps to an ECMA-357 value -- but it must
2389     * map to the *undefined* prefix value).
2390     *
2391     * Since "" != undefined (or null, in the current implementation)
2392     * the ECMA-357 spec will fail to match in [[GetNamespace]] called
2393     * on t with argument {} U {(prefix="", uri="http://foo.com")}.
2394     * This spec bug leads to ToXMLString results that duplicate the
2395     * declared namespace.
2396     */
2397     if (js_EqualStrings(GetURI(ns), uri)) {
2398     nsprefix = GetPrefix(ns);
2399     if (nsprefix == prefix ||
2400     ((nsprefix && prefix)
2401     ? js_EqualStrings(nsprefix, prefix)
2402     : IS_EMPTY(nsprefix ? nsprefix : prefix))) {
2403     match = ns;
2404     break;
2405     }
2406     }
2407     }
2408     }
2409    
2410     /* If we didn't match, make a new namespace from qn. */
2411     if (!match) {
2412     argv[0] = prefix ? STRING_TO_JSVAL(prefix) : JSVAL_VOID;
2413     argv[1] = STRING_TO_JSVAL(uri);
2414     ns = js_ConstructObject(cx, &js_NamespaceClass.base, NULL, NULL,
2415     2, argv);
2416     if (!ns)
2417     return NULL;
2418     match = ns;
2419     }
2420     return match;
2421     }
2422    
2423     static JSString *
2424     GeneratePrefix(JSContext *cx, JSString *uri, JSXMLArray *decls)
2425     {
2426     const jschar *cp, *start, *end;
2427     size_t length, newlength, offset;
2428     uint32 i, n, m, serial;
2429     jschar *bp, *dp;
2430     JSBool done;
2431     JSObject *ns;
2432     JSString *nsprefix, *prefix;
2433    
2434     JS_ASSERT(!IS_EMPTY(uri));
2435    
2436     /*
2437     * If there are no *declared* namespaces, skip all collision detection and
2438     * return a short prefix quickly; an example of such a situation:
2439     *
2440     * var x = <f/>;
2441     * var n = new Namespace("http://example.com/");
2442     * x.@n::att = "val";
2443     * x.toXMLString();
2444     *
2445     * This is necessary for various log10 uses below to be valid.
2446     */
2447     if (decls->length == 0)
2448     return JS_NewStringCopyZ(cx, "a");
2449    
2450     /*
2451     * Try peeling off the last filename suffix or pathname component till
2452     * we have a valid XML name. This heuristic will prefer "xul" given
2453     * ".../there.is.only.xul", "xbl" given ".../xbl", and "xbl2" given any
2454     * likely URI of the form ".../xbl2/2005".
2455     */
2456     JSSTRING_CHARS_AND_END(uri, start, end);
2457     cp = end;
2458     while (--cp > start) {
2459     if (*cp == '.' || *cp == '/' || *cp == ':') {
2460     ++cp;
2461     length = PTRDIFF(end, cp, jschar);
2462     if (IsXMLName(cp, length) && !STARTS_WITH_XML(cp, length))
2463     break;
2464     end = --cp;
2465     }
2466     }
2467     length = PTRDIFF(end, cp, jschar);
2468    
2469     /*
2470     * If the namespace consisted only of non-XML names or names that begin
2471     * case-insensitively with "xml", arbitrarily create a prefix consisting
2472     * of 'a's of size length (allowing dp-calculating code to work with or
2473     * without this branch executing) plus the space for storing a hyphen and
2474     * the serial number (avoiding reallocation if a collision happens).
2475     */
2476     bp = (jschar *) cp;
2477     newlength = length;
2478     if (STARTS_WITH_XML(cp, length) || !IsXMLName(cp, length)) {
2479     newlength = length + 2 + (size_t) log10((double) decls->length);
2480     bp = (jschar *)
2481     JS_malloc(cx, (newlength + 1) * sizeof(jschar));
2482     if (!bp)
2483     return NULL;
2484    
2485     bp[newlength] = 0;
2486     for (i = 0; i < newlength; i++)
2487     bp[i] = 'a';
2488     }
2489    
2490     /*
2491     * Now search through decls looking for a collision. If we collide with
2492     * an existing prefix, start tacking on a hyphen and a serial number.
2493     */
2494     serial = 0;
2495     do {
2496     done = JS_TRUE;
2497     for (i = 0, n = decls->length; i < n; i++) {
2498     ns = XMLARRAY_MEMBER(decls, i, JSObject);
2499     if (ns && (nsprefix = GetPrefix(ns)) &&
2500     JSSTRING_LENGTH(nsprefix) == newlength &&
2501     !memcmp(JSSTRING_CHARS(nsprefix), bp,
2502     newlength * sizeof(jschar))) {
2503     if (bp == cp) {
2504     newlength = length + 2 + (size_t) log10((double) n);
2505     bp = (jschar *)
2506     JS_malloc(cx, (newlength + 1) * sizeof(jschar));
2507     if (!bp)
2508     return NULL;
2509     js_strncpy(bp, cp, length);
2510     }
2511    
2512     ++serial;
2513     JS_ASSERT(serial <= n);
2514     dp = bp + length + 2 + (size_t) log10((double) serial);
2515     *dp = 0;
2516     for (m = serial; m != 0; m /= 10)
2517     *--dp = (jschar)('0' + m % 10);
2518     *--dp = '-';
2519     JS_ASSERT(dp == bp + length);
2520    
2521     done = JS_FALSE;
2522     break;
2523     }
2524     }
2525     } while (!done);
2526    
2527     if (bp == cp) {
2528     offset = PTRDIFF(cp, start, jschar);
2529     prefix = js_NewDependentString(cx, uri, offset, length);
2530     } else {
2531     prefix = js_NewString(cx, bp, newlength);
2532     if (!prefix)
2533     JS_free(cx, bp);
2534     }
2535     return prefix;
2536     }
2537    
2538     static JSBool
2539     namespace_match(const void *a, const void *b)
2540     {
2541     const JSObject *nsa = (const JSObject *) a;
2542     const JSObject *nsb = (const JSObject *) b;
2543     JSString *prefixa, *prefixb = GetPrefix(nsb);
2544    
2545     if (prefixb) {
2546     prefixa = GetPrefix(nsa);
2547     return prefixa && js_EqualStrings(prefixa, prefixb);
2548     }
2549     return js_EqualStrings(GetURI(nsa), GetURI(nsb));
2550     }
2551    
2552     /* ECMA-357 10.2.1 and 10.2.2 */
2553     #define TO_SOURCE_FLAG 0x80000000
2554    
2555     static JSString *
2