PageRenderTime 10ms CodeModel.GetById 19ms app.highlight 71ms RepoModel.GetById 0ms app.codeStats 1ms

/js/lib/Socket.IO-node/support/expresso/deps/jscoverage/js/jsxml.cpp

http://github.com/onedayitwillmake/RealtimeMultiplayerNodeJs
C++ | 2138 lines | 1652 code | 255 blank | 231 comment | 438 complexity | 1bad5945bfae6a690cea885d2ba4c3d7 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause
   1/* -*- 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
  91static 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 */
 108const char js_isXMLName_str[]     = "isXMLName";
 109const char js_XMLList_str[]       = "XMLList";
 110const char js_localName_str[]     = "localName";
 111const char js_xml_parent_str[]    = "parent";
 112const char js_prefix_str[]        = "prefix";
 113const char js_toXMLString_str[]   = "toXMLString";
 114const char js_uri_str[]           = "uri";
 115
 116const char js_amp_entity_str[]    = "&amp;";
 117const char js_gt_entity_str[]     = "&gt;";
 118const char js_lt_entity_str[]     = "&lt;";
 119const 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. */
 124const uint32 JSSLOT_PREFIX      = JSSLOT_PRIVATE;
 125const uint32 JSSLOT_URI         = JSSLOT_PRIVATE + 1;
 126
 127/* Namespace-specific slot. */
 128const uint32 JSSLOT_DECLARED    = JSSLOT_PRIVATE + 2;
 129
 130/* QName-specific slot. */
 131const uint32 JSSLOT_LOCAL_NAME  = JSSLOT_PRIVATE + 2;
 132
 133const uint32 NAMESPACE_RESERVED_SLOTS = 3;
 134const uint32 QNAME_RESERVED_SLOTS = 3;
 135
 136static JSBool
 137IsQNameClass(JSClass *clasp)
 138{
 139    return clasp == &js_QNameClass.base ||
 140           clasp == &js_AttributeNameClass ||
 141           clasp == &js_AnyNameClass;
 142}
 143
 144static JSString *
 145GetSlotString(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
 164static JS_INLINE JSString *
 165GetPrefix(const JSObject *obj)
 166{
 167    return GetSlotString(obj, JSSLOT_PREFIX);
 168}
 169
 170static JSString *
 171GetURI(const JSObject *obj)
 172{
 173    return GetSlotString(obj, JSSLOT_URI);
 174}
 175
 176static JSString *
 177GetLocalName(const JSObject *obj)
 178{
 179    return GetSlotString(obj, JSSLOT_LOCAL_NAME);
 180}
 181
 182static JSBool
 183IsDeclared(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
 193static JSBool
 194xml_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 */
 204enum namespace_tinyid {
 205    NAMESPACE_PREFIX = -1,
 206    NAMESPACE_URI = -2
 207};
 208
 209static JSBool
 210namespace_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
 229static void
 230namespace_finalize(JSContext *cx, JSObject *obj)
 231{
 232    if (cx->runtime->functionNamespaceObject == obj)
 233        cx->runtime->functionNamespaceObject = NULL;
 234}
 235
 236static JSBool
 237namespace_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
 249JS_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
 265static 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
 271static JSBool
 272namespace_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
 283static JSFunctionSpec namespace_methods[] = {
 284    JS_FN(js_toString_str,  namespace_toString,        0,0),
 285    JS_FS_END
 286};
 287
 288static JSObject *
 289NewXMLNamespace(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 */
 312enum qname_tinyid {
 313    QNAME_URI = -1,
 314    QNAME_LOCALNAME = -2
 315};
 316
 317static JSBool
 318qname_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
 339static void
 340anyname_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
 347static JSBool
 348qname_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
 360static JSBool
 361qname_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
 373JS_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 */
 392JS_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
 403JS_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
 417static 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
 423static JSBool
 424qname_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
 478static JSFunctionSpec qname_methods[] = {
 479    JS_FN(js_toString_str,  qname_toString,    0,0),
 480    JS_FS_END
 481};
 482
 483
 484static void
 485InitXMLQName(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
 499static JSObject *
 500NewXMLQName(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
 514JSObject *
 515js_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
 534static JSBool
 535IsXMLName(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
 552JSBool
 553js_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 */
 584static JSBool
 585NamespaceHelper(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
 679static JSBool
 680Namespace(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 */
 691static JSBool
 692QNameHelper(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
 809out:
 810    InitXMLQName(obj, uri, prefix, name);
 811    return JS_TRUE;
 812}
 813
 814static JSBool
 815QName(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
 821static JSBool
 822AttributeName(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 */
 832static JSBool
 833namespace_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
 850static JSBool
 851attr_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
 859static void
 860XMLArrayCursorInit(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
 874static void
 875XMLArrayCursorFinish(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
 888static void *
 889XMLArrayCursorNext(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
 899static void *
 900XMLArrayCursorItem(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
 910static void
 911XMLArrayCursorTrace(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. */
 926static JSBool
 927XMLArraySetCapacity(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
 953static void
 954XMLArrayTrim(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
 962static JSBool
 963XMLArrayInit(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
 971static void
 972XMLArrayFinish(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
 988static uint32
 989XMLArrayFindMember(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
1018static JSBool
1019XMLArrayAddMember(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
1056static JSBool
1057XMLArrayInsert(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
1081static void *
1082XMLArrayDelete(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
1110static void
1111XMLArrayTruncate(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 */
1156static const char js_ignoreComments_str[]   = "ignoreComments";
1157static const char js_ignoreProcessingInstructions_str[]
1158                                            = "ignoreProcessingInstructions";
1159static const char js_ignoreWhitespace_str[] = "ignoreWhitespace";
1160static const char js_prettyPrinting_str[]   = "prettyPrinting";
1161static 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 */
1169enum 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
1177static JSBool
1178xml_setting_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
1179{
1180    return JS_TRUE;
1181}
1182
1183static JSBool
1184xml_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
1198static 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
1258static const char xml_namespace_str[] = "http://www.w3.org/XML/1998/namespace";
1259static const char xmlns_namespace_str[] = "http://www.w3.org/2000/xmlns/";
1260
1261static JSObject *
1262ParseNodeToQName(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
1355static JSString *
1356ChompXMLWhitespace(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
1381static JSXML *
1382ParseNodeToXML(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
1728skip_child:
1729    js_LeaveLocalRootScope(cx);
1730    return PN2X_SKIP_CHILD;
1731
1732#undef PN2X_SKIP_CHILD
1733
1734syntax:
1735    js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn, JSREPORT_ERROR,
1736                                JSMSG_BAD_XML_MARKUP);
1737fail:
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 */
1746static JSBool
1747GetXMLSetting(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
1760static JSBool
1761FillSettingsCache(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
1782static JSBool
1783GetBooleanXMLSetting(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
1800static JSBool
1801GetUint32XMLSetting(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
1808static JSBool
1809GetXMLSettingFlags(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
1820static JSXML *
1821ParseXMLSource(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    uri = js_EscapeAttributeValue(cx, uri, JS_FALSE);
1848
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            lineno = js_FramePCToLineNumber(cx, fp);
1885            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
1904out:
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 */
1930static JSXML *
1931OrphanXMLChild(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
1948static JSObject *
1949ToXML(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
2020bad:
2021    js_ReportValueError(cx, JSMSG_BAD_XML_CONVERSION,
2022                        JSDVG_IGNORE_STACK, v, NULL);
2023    return NULL;
2024}
2025
2026static JSBool
2027Append(JSContext *cx, JSXML *list, JSXML *kid);
2028
2029static JSObject *
2030ToXMLList(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
2102bad:
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 */
2117static JSString *
2118MakeXMLSpecialString(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_r