PageRenderTime 151ms CodeModel.GetById 32ms app.highlight 96ms RepoModel.GetById 1ms app.codeStats 2ms

/js/src/jsxml.cpp

http://github.com/zpao/v8monkey
C++ | 8109 lines | 6438 code | 936 blank | 735 comment | 1808 complexity | e16b88c7531ad81e8cde99e3c49fc788 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   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 "jsversion.h"
  41
  42#if JS_HAS_XML_SUPPORT
  43
  44#include <math.h>
  45#include <stdlib.h>
  46#include <string.h>
  47
  48#include "mozilla/Util.h"
  49
  50#include "jstypes.h"
  51#include "jsprf.h"
  52#include "jsutil.h"
  53#include "jsapi.h"
  54#include "jsarray.h"
  55#include "jsatom.h"
  56#include "jsbool.h"
  57#include "jscntxt.h"
  58#include "jsfun.h"
  59#include "jsgc.h"
  60#include "jsgcmark.h"
  61#include "jslock.h"
  62#include "jsnum.h"
  63#include "jsobj.h"
  64#include "jsopcode.h"
  65#include "jsscope.h"
  66#include "jsscript.h"
  67#include "jsstr.h"
  68#include "jsxml.h"
  69
  70#include "frontend/Parser.h"
  71#include "frontend/TokenStream.h"
  72#include "vm/GlobalObject.h"
  73
  74#include "jsatominlines.h"
  75#include "jsinferinlines.h"
  76#include "jsobjinlines.h"
  77#include "jsstrinlines.h"
  78
  79#include "vm/Stack-inl.h"
  80#include "vm/String-inl.h"
  81
  82#ifdef DEBUG
  83#include <string.h>     /* for #ifdef DEBUG memset calls */
  84#endif
  85
  86using namespace mozilla;
  87using namespace js;
  88using namespace js::gc;
  89using namespace js::types;
  90
  91template<class T, class U>
  92struct IdentityOp
  93{
  94    typedef JSBool (* compare)(const T *a, const U *b);
  95};
  96
  97template<class T>
  98static JSBool
  99pointer_match(const T *a, const T *b)
 100{
 101    return a == b;
 102}
 103
 104/*
 105 * NOTES
 106 * - in the js shell, you must use the -x command line option, or call
 107 *   options('xml') before compiling anything that uses XML literals
 108 *
 109 * TODO
 110 * - XXXbe patrol
 111 * - Fuse objects and their JSXML* private data into single GC-things
 112 * - fix function::foo vs. x.(foo == 42) collision using proper namespacing
 113 * - JSCLASS_DOCUMENT_OBSERVER support -- live two-way binding to Gecko's DOM!
 114 */
 115
 116static inline bool
 117js_EnterLocalRootScope(JSContext *cx)
 118{
 119    return true;
 120}
 121
 122static inline void
 123js_LeaveLocalRootScope(JSContext *cx)
 124{
 125}
 126
 127static inline void
 128js_LeaveLocalRootScopeWithResult(JSContext *cx, Value rval)
 129{
 130}
 131
 132static inline void
 133js_LeaveLocalRootScopeWithResult(JSContext *cx, void *rval)
 134{
 135}
 136
 137/*
 138 * Random utilities and global functions.
 139 */
 140const char js_AttributeName_str[] = "AttributeName";
 141const char js_isXMLName_str[]     = "isXMLName";
 142const char js_XMLList_str[]       = "XMLList";
 143const char js_localName_str[]     = "localName";
 144const char js_xml_parent_str[]    = "parent";
 145const char js_prefix_str[]        = "prefix";
 146const char js_toXMLString_str[]   = "toXMLString";
 147const char js_uri_str[]           = "uri";
 148
 149const char js_amp_entity_str[]    = "&amp;";
 150const char js_gt_entity_str[]     = "&gt;";
 151const char js_lt_entity_str[]     = "&lt;";
 152const char js_quot_entity_str[]   = "&quot;";
 153const char js_leftcurly_entity_str[]   = "&#123;";
 154
 155#define IS_STAR(str)  ((str)->length() == 1 && *(str)->chars() == '*')
 156
 157static JSBool
 158GetXMLFunction(JSContext *cx, JSObject *obj, jsid id, jsval *vp);
 159
 160static JSBool
 161IsDeclared(const JSObject *obj)
 162{
 163    jsval v;
 164
 165    JS_ASSERT(obj->getClass() == &NamespaceClass);
 166    v = obj->getNamespaceDeclared();
 167    JS_ASSERT(JSVAL_IS_VOID(v) || v == JSVAL_TRUE);
 168    return v == JSVAL_TRUE;
 169}
 170
 171static JSBool
 172xml_isXMLName(JSContext *cx, uintN argc, jsval *vp)
 173{
 174    *vp = BOOLEAN_TO_JSVAL(js_IsXMLName(cx, argc ? vp[2] : JSVAL_VOID));
 175    return JS_TRUE;
 176}
 177
 178size_t sE4XObjectsCreated = 0;
 179
 180/*
 181 * This wrapper is needed because NewBuiltinClassInstance doesn't
 182 * call the constructor, and we need a place to set the
 183 * HAS_EQUALITY bit.
 184 */
 185static inline JSObject *
 186NewBuiltinClassInstanceXML(JSContext *cx, Class *clasp)
 187{
 188    if (!cx->runningWithTrustedPrincipals())
 189        ++sE4XObjectsCreated;
 190
 191    return NewBuiltinClassInstance(cx, clasp);
 192}
 193
 194#define DEFINE_GETTER(name,code)                                               \
 195    static JSBool                                                              \
 196    name(JSContext *cx, JSObject *obj, jsid id, jsval *vp)                     \
 197    {                                                                          \
 198        code;                                                                  \
 199        return true;                                                           \
 200    }
 201
 202/*
 203 * Namespace class and library functions.
 204 */
 205DEFINE_GETTER(NamePrefix_getter,
 206              if (obj->getClass() == &NamespaceClass) *vp = obj->getNamePrefixVal())
 207DEFINE_GETTER(NameURI_getter,
 208              if (obj->getClass() == &NamespaceClass) *vp = obj->getNameURIVal())
 209
 210static JSBool
 211namespace_equality(JSContext *cx, JSObject *obj, const Value *v, JSBool *bp)
 212{
 213    JSObject *obj2;
 214
 215    JS_ASSERT(v->isObjectOrNull());
 216    obj2 = v->toObjectOrNull();
 217    *bp = (!obj2 || obj2->getClass() != &NamespaceClass)
 218          ? JS_FALSE
 219          : EqualStrings(obj->getNameURI(), obj2->getNameURI());
 220    return JS_TRUE;
 221}
 222
 223JS_FRIEND_DATA(Class) js::NamespaceClass = {
 224    "Namespace",
 225    JSCLASS_HAS_RESERVED_SLOTS(JSObject::NAMESPACE_CLASS_RESERVED_SLOTS) |
 226    JSCLASS_HAS_CACHED_PROTO(JSProto_Namespace),
 227    JS_PropertyStub,         /* addProperty */
 228    JS_PropertyStub,         /* delProperty */
 229    JS_PropertyStub,         /* getProperty */
 230    JS_StrictPropertyStub,   /* setProperty */
 231    JS_EnumerateStub,
 232    JS_ResolveStub,
 233    JS_ConvertStub,
 234    JS_FinalizeStub,
 235    NULL,                    /* reserved0   */
 236    NULL,                    /* checkAccess */
 237    NULL,                    /* call        */
 238    NULL,                    /* construct   */
 239    NULL,                    /* xdrObject   */
 240    NULL,                    /* hasInstance */
 241    NULL,                    /* mark        */
 242    {
 243        namespace_equality,
 244        NULL,                /* outerObject    */
 245        NULL,                /* innerObject    */
 246        NULL,                /* iteratorObject */
 247        NULL,                /* wrappedObject  */
 248    }
 249};
 250
 251#define NAMESPACE_ATTRS                                                       \
 252    (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
 253
 254static JSPropertySpec namespace_props[] = {
 255    {js_prefix_str, 0, NAMESPACE_ATTRS, NamePrefix_getter, 0},
 256    {js_uri_str,    0, NAMESPACE_ATTRS, NameURI_getter,    0},
 257    {0,0,0,0,0}
 258};
 259
 260static JSBool
 261namespace_toString(JSContext *cx, uintN argc, Value *vp)
 262{
 263    JSObject *obj = ToObject(cx, &vp[1]);
 264    if (!obj)
 265        return JS_FALSE;
 266    if (!obj->isNamespace()) {
 267        ReportIncompatibleMethod(cx, CallReceiverFromVp(vp), &NamespaceClass);
 268        return JS_FALSE;
 269    }
 270    *vp = obj->getNameURIVal();
 271    return JS_TRUE;
 272}
 273
 274static JSFunctionSpec namespace_methods[] = {
 275    JS_FN(js_toString_str,  namespace_toString,        0,0),
 276    JS_FS_END
 277};
 278
 279static JSObject *
 280NewXMLNamespace(JSContext *cx, JSLinearString *prefix, JSLinearString *uri, JSBool declared)
 281{
 282    JSObject *obj;
 283
 284    obj = NewBuiltinClassInstanceXML(cx, &NamespaceClass);
 285    if (!obj)
 286        return NULL;
 287    JS_ASSERT(JSVAL_IS_VOID(obj->getNamePrefixVal()));
 288    JS_ASSERT(JSVAL_IS_VOID(obj->getNameURIVal()));
 289    JS_ASSERT(JSVAL_IS_VOID(obj->getNamespaceDeclared()));
 290
 291    /* Per ECMA-357, 13.2.5, these properties must be "own". */
 292    if (!JS_DefineProperties(cx, obj, namespace_props))
 293        return NULL;
 294
 295    if (prefix)
 296        obj->setNamePrefix(prefix);
 297    if (uri)
 298        obj->setNameURI(uri);
 299    if (declared)
 300        obj->setNamespaceDeclared(JSVAL_TRUE);
 301    return obj;
 302}
 303
 304/*
 305 * QName class and library functions.
 306 */
 307DEFINE_GETTER(QNameNameURI_getter,
 308              if (obj->getClass() == &QNameClass)
 309                  *vp = JSVAL_IS_VOID(obj->getNameURIVal()) ? JSVAL_NULL : obj->getNameURIVal())
 310DEFINE_GETTER(QNameLocalName_getter,
 311              if (obj->getClass() == &QNameClass)
 312                  *vp = obj->getQNameLocalNameVal())
 313
 314static JSBool
 315qname_identity(JSObject *qna, const JSObject *qnb)
 316{
 317    JSLinearString *uri1 = qna->getNameURI();
 318    JSLinearString *uri2 = qnb->getNameURI();
 319
 320    if (!uri1 ^ !uri2)
 321        return JS_FALSE;
 322    if (uri1 && !EqualStrings(uri1, uri2))
 323        return JS_FALSE;
 324    return EqualStrings(qna->getQNameLocalName(), qnb->getQNameLocalName());
 325}
 326
 327static JSBool
 328qname_equality(JSContext *cx, JSObject *qn, const Value *v, JSBool *bp)
 329{
 330    JSObject *obj2;
 331
 332    obj2 = v->toObjectOrNull();
 333    *bp = (!obj2 || obj2->getClass() != &QNameClass)
 334          ? JS_FALSE
 335          : qname_identity(qn, obj2);
 336    return JS_TRUE;
 337}
 338
 339JS_FRIEND_DATA(Class) js::QNameClass = {
 340    "QName",
 341    JSCLASS_HAS_RESERVED_SLOTS(JSObject::QNAME_CLASS_RESERVED_SLOTS) |
 342    JSCLASS_HAS_CACHED_PROTO(JSProto_QName),
 343    JS_PropertyStub,         /* addProperty */
 344    JS_PropertyStub,         /* delProperty */
 345    JS_PropertyStub,         /* getProperty */
 346    JS_StrictPropertyStub,   /* setProperty */
 347    JS_EnumerateStub,
 348    JS_ResolveStub,
 349    JS_ConvertStub,
 350    JS_FinalizeStub,
 351    NULL,                    /* reserved0   */
 352    NULL,                    /* checkAccess */
 353    NULL,                    /* call        */
 354    NULL,                    /* construct   */
 355    NULL,                    /* xdrObject   */
 356    NULL,                    /* hasInstance */
 357    NULL,                    /* mark        */
 358    {
 359        qname_equality,
 360        NULL,                /* outerObject    */
 361        NULL,                /* innerObject    */
 362        NULL,                /* iteratorObject */
 363        NULL,                /* wrappedObject  */
 364    }
 365};
 366
 367/*
 368 * Classes for the ECMA-357-internal types AttributeName and AnyName, which
 369 * are like QName, except that they have no property getters.  They share the
 370 * qname_toString method, and therefore are exposed as constructable objects
 371 * in this implementation.
 372 */
 373JS_FRIEND_DATA(Class) js::AttributeNameClass = {
 374    js_AttributeName_str,
 375    JSCLASS_HAS_RESERVED_SLOTS(JSObject::QNAME_CLASS_RESERVED_SLOTS) |
 376    JSCLASS_IS_ANONYMOUS,
 377    JS_PropertyStub,         /* addProperty */
 378    JS_PropertyStub,         /* delProperty */
 379    JS_PropertyStub,         /* getProperty */
 380    JS_StrictPropertyStub,   /* setProperty */
 381    JS_EnumerateStub,
 382    JS_ResolveStub,
 383    JS_ConvertStub,
 384    JS_FinalizeStub
 385};
 386
 387JS_FRIEND_DATA(Class) js::AnyNameClass = {
 388    js_AnyName_str,
 389    JSCLASS_HAS_RESERVED_SLOTS(JSObject::QNAME_CLASS_RESERVED_SLOTS) |
 390    JSCLASS_IS_ANONYMOUS,
 391    JS_PropertyStub,         /* addProperty */
 392    JS_PropertyStub,         /* delProperty */
 393    JS_PropertyStub,         /* getProperty */
 394    JS_StrictPropertyStub,   /* setProperty */
 395    JS_EnumerateStub,
 396    JS_ResolveStub,
 397    JS_ConvertStub,
 398    JS_FinalizeStub
 399};
 400
 401#define QNAME_ATTRS (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
 402
 403static JSPropertySpec qname_props[] = {
 404    {js_uri_str,       0, QNAME_ATTRS, QNameNameURI_getter,   0},
 405    {js_localName_str, 0, QNAME_ATTRS, QNameLocalName_getter, 0},
 406    {0,0,0,0,0}
 407};
 408
 409static JSString *
 410ConvertQNameToString(JSContext *cx, JSObject *obj)
 411{
 412    JS_ASSERT(obj->isQName());
 413    JSString *uri = obj->getNameURI();
 414    JSString *str;
 415    if (!uri) {
 416        /* No uri means wildcard qualifier. */
 417        str = cx->runtime->atomState.starQualifierAtom;
 418    } else if (uri->empty()) {
 419        /* Empty string for uri means localName is in no namespace. */
 420        str = cx->runtime->emptyString;
 421    } else {
 422        JSString *qualstr = cx->runtime->atomState.qualifierAtom;
 423        str = js_ConcatStrings(cx, uri, qualstr);
 424        if (!str)
 425            return NULL;
 426    }
 427    str = js_ConcatStrings(cx, str, obj->getQNameLocalName());
 428    if (!str)
 429        return NULL;
 430
 431    if (obj->getClass() == &AttributeNameClass) {
 432        JS::Anchor<JSString *> anchor(str);
 433        size_t length = str->length();
 434        jschar *chars = (jschar *) cx->malloc_((length + 2) * sizeof(jschar));
 435        if (!chars)
 436            return JS_FALSE;
 437        *chars = '@';
 438        const jschar *strChars = str->getChars(cx);
 439        if (!strChars) {
 440            cx->free_(chars);
 441            return NULL;
 442        }
 443        js_strncpy(chars + 1, strChars, length);
 444        chars[++length] = 0;
 445        str = js_NewString(cx, chars, length);
 446        if (!str) {
 447            cx->free_(chars);
 448            return NULL;
 449        }
 450    }
 451    return str;
 452}
 453
 454static JSBool
 455qname_toString(JSContext *cx, uintN argc, Value *vp)
 456{
 457    JSObject *obj = ToObject(cx, &vp[1]);
 458    if (!obj)
 459        return false;
 460
 461    if (!obj->isQName()) {
 462        ReportIncompatibleMethod(cx, CallReceiverFromVp(vp), &QNameClass);
 463        return false;
 464    }
 465
 466    JSString *str = ConvertQNameToString(cx, obj);
 467    if (!str)
 468        return false;
 469
 470    vp->setString(str);
 471    return true;
 472}
 473
 474static JSFunctionSpec qname_methods[] = {
 475    JS_FN(js_toString_str,  qname_toString,    0,0),
 476    JS_FS_END
 477};
 478
 479
 480static bool
 481InitXMLQName(JSContext *cx, JSObject *obj, JSLinearString *uri, JSLinearString *prefix,
 482             JSAtom *localName)
 483{
 484    JS_ASSERT(obj->isQName());
 485    JS_ASSERT(JSVAL_IS_VOID(obj->getNamePrefixVal()));
 486    JS_ASSERT(JSVAL_IS_VOID(obj->getNameURIVal()));
 487    JS_ASSERT(JSVAL_IS_VOID(obj->getQNameLocalNameVal()));
 488
 489    /* Per ECMA-357, 13.3.5, these properties must be "own". */
 490    if (!JS_DefineProperties(cx, obj, qname_props))
 491        return false;
 492
 493    if (uri)
 494        obj->setNameURI(uri);
 495    if (prefix)
 496        obj->setNamePrefix(prefix);
 497    if (localName)
 498        obj->setQNameLocalName(localName);
 499    return true;
 500}
 501
 502static JSObject *
 503NewXMLQName(JSContext *cx, JSLinearString *uri, JSLinearString *prefix,
 504            JSAtom *localName)
 505{
 506    JSObject *obj = NewBuiltinClassInstanceXML(cx, &QNameClass);
 507    if (!obj)
 508        return NULL;
 509    if (!InitXMLQName(cx, obj, uri, prefix, localName))
 510        return NULL;
 511    return obj;
 512}
 513
 514static JSObject *
 515NewXMLAttributeName(JSContext *cx, JSLinearString *uri, JSLinearString *prefix,
 516                    JSAtom *localName)
 517{
 518    /*
 519     * AttributeName is an internal anonymous class which instances are not
 520     * exposed to scripts.
 521     */
 522    JSObject *parent = GetGlobalForScopeChain(cx);
 523    JSObject *obj = NewObjectWithGivenProto(cx, &AttributeNameClass, NULL, parent);
 524    if (!obj)
 525        return NULL;
 526    JS_ASSERT(obj->isQName());
 527    if (!InitXMLQName(cx, obj, uri, prefix, localName))
 528        return NULL;
 529    return obj;
 530}
 531
 532JSObject *
 533js_ConstructXMLQNameObject(JSContext *cx, const Value &nsval, const Value &lnval)
 534{
 535    Value argv[2];
 536
 537    /*
 538     * ECMA-357 11.1.2,
 539     * The _QualifiedIdentifier : PropertySelector :: PropertySelector_
 540     * production, step 2.
 541     */
 542    if (nsval.isObject() &&
 543        nsval.toObject().getClass() == &AnyNameClass) {
 544        argv[0].setNull();
 545    } else {
 546        argv[0] = nsval;
 547    }
 548    argv[1] = lnval;
 549    return JS_ConstructObjectWithArguments(cx, Jsvalify(&QNameClass), NULL, 2, argv);
 550}
 551
 552static JSBool
 553IsXMLName(const jschar *cp, size_t n)
 554{
 555    JSBool rv;
 556    jschar c;
 557
 558    rv = JS_FALSE;
 559    if (n != 0 && unicode::IsXMLNamespaceStart(*cp)) {
 560        while (--n != 0) {
 561            c = *++cp;
 562            if (!unicode::IsXMLNamespacePart(c))
 563                return rv;
 564        }
 565        rv = JS_TRUE;
 566    }
 567    return rv;
 568}
 569
 570JSBool
 571js_IsXMLName(JSContext *cx, jsval v)
 572{
 573    JSLinearString *name = NULL;
 574    JSErrorReporter older;
 575
 576    /*
 577     * Inline specialization of the QName constructor called with v passed as
 578     * the only argument, to compute the localName for the constructed qname,
 579     * without actually allocating the object or computing its uri and prefix.
 580     * See ECMA-357 13.1.2.1 step 1 and 13.3.2.
 581     */
 582    if (!JSVAL_IS_PRIMITIVE(v) &&
 583        JSVAL_TO_OBJECT(v)->isQName()) {
 584        name = JSVAL_TO_OBJECT(v)->getQNameLocalName();
 585    } else {
 586        older = JS_SetErrorReporter(cx, NULL);
 587        JSString *str = ToString(cx, v);
 588        if (str)
 589            name = str->ensureLinear(cx);
 590        JS_SetErrorReporter(cx, older);
 591        if (!name) {
 592            JS_ClearPendingException(cx);
 593            return JS_FALSE;
 594        }
 595    }
 596
 597    return IsXMLName(name->chars(), name->length());
 598}
 599
 600/*
 601 * When argc is -1, it indicates argv is empty but the code should behave as
 602 * if argc is 1 and argv[0] is JSVAL_VOID.
 603 */
 604static JSBool
 605NamespaceHelper(JSContext *cx, intN argc, jsval *argv, jsval *rval)
 606{
 607    jsval urival, prefixval;
 608    JSObject *uriobj;
 609    JSBool isNamespace, isQName;
 610    Class *clasp;
 611    JSLinearString *empty, *prefix, *uri;
 612
 613    isNamespace = isQName = JS_FALSE;
 614#ifdef __GNUC__         /* suppress bogus gcc warnings */
 615    uriobj = NULL;
 616#endif
 617    if (argc <= 0) {
 618        urival = JSVAL_VOID;
 619    } else {
 620        urival = argv[argc > 1];
 621        if (!JSVAL_IS_PRIMITIVE(urival)) {
 622            uriobj = JSVAL_TO_OBJECT(urival);
 623            clasp = uriobj->getClass();
 624            isNamespace = (clasp == &NamespaceClass);
 625            isQName = (clasp == &QNameClass);
 626        }
 627    }
 628
 629    /* Namespace called as function. */
 630    if (argc == 1 && isNamespace) {
 631        /* Namespace called with one Namespace argument is identity. */
 632        *rval = urival;
 633        return JS_TRUE;
 634    }
 635
 636    JSObject *obj = NewBuiltinClassInstanceXML(cx, &NamespaceClass);
 637    if (!obj)
 638        return JS_FALSE;
 639
 640    /* Per ECMA-357, 13.2.5, these properties must be "own". */
 641    if (!JS_DefineProperties(cx, obj, namespace_props))
 642        return JS_FALSE;
 643
 644    *rval = OBJECT_TO_JSVAL(obj);
 645
 646    empty = cx->runtime->emptyString;
 647    obj->setNamePrefix(empty);
 648    obj->setNameURI(empty);
 649
 650    if (argc == 1 || argc == -1) {
 651        if (isNamespace) {
 652            obj->setNameURI(uriobj->getNameURI());
 653            obj->setNamePrefix(uriobj->getNamePrefix());
 654        } else if (isQName && (uri = uriobj->getNameURI())) {
 655            obj->setNameURI(uri);
 656            obj->setNamePrefix(uriobj->getNamePrefix());
 657        } else {
 658            JSString *str = ToString(cx, urival);
 659            if (!str)
 660                return JS_FALSE;
 661            uri = str->ensureLinear(cx);
 662            if (!uri)
 663                return JS_FALSE;
 664            obj->setNameURI(uri);
 665            if (!uri->empty())
 666                obj->clearNamePrefix();
 667        }
 668    } else if (argc == 2) {
 669        if (!isQName || !(uri = uriobj->getNameURI())) {
 670            JSString *str = ToString(cx, urival);
 671            if (!str)
 672                return JS_FALSE;
 673            uri = str->ensureLinear(cx);
 674            if (!uri)
 675                return JS_FALSE;
 676        }
 677        obj->setNameURI(uri);
 678
 679        prefixval = argv[0];
 680        if (uri->empty()) {
 681            if (!JSVAL_IS_VOID(prefixval)) {
 682                JSString *str = ToString(cx, prefixval);
 683                if (!str)
 684                    return JS_FALSE;
 685                if (!str->empty()) {
 686                    JSAutoByteString bytes;
 687                    if (js_ValueToPrintable(cx, StringValue(str), &bytes)) {
 688                        JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
 689                                             JSMSG_BAD_XML_NAMESPACE, bytes.ptr());
 690                    }
 691                    return JS_FALSE;
 692                }
 693            }
 694        } else if (JSVAL_IS_VOID(prefixval) || !js_IsXMLName(cx, prefixval)) {
 695            obj->clearNamePrefix();
 696        } else {
 697            JSString *str = ToString(cx, prefixval);
 698            if (!str)
 699                return JS_FALSE;
 700            prefix = str->ensureLinear(cx);
 701            if (!prefix)
 702                return JS_FALSE;
 703            obj->setNamePrefix(prefix);
 704        }
 705    }
 706    return JS_TRUE;
 707}
 708
 709static JSBool
 710Namespace(JSContext *cx, uintN argc, Value *vp)
 711{
 712    return NamespaceHelper(cx, argc, vp + 2, vp);
 713}
 714
 715/*
 716 * When argc is -1, it indicates argv is empty but the code should behave as
 717 * if argc is 1 and argv[0] is JSVAL_VOID.
 718 */
 719static JSBool
 720QNameHelper(JSContext *cx, intN argc, jsval *argv, jsval *rval)
 721{
 722    jsval nameval, nsval;
 723    JSBool isQName, isNamespace;
 724    JSObject *qn;
 725    JSLinearString *uri, *prefix;
 726    JSObject *obj2;
 727
 728    JSAtom *name;
 729    if (argc <= 0) {
 730        nameval = JSVAL_VOID;
 731        isQName = JS_FALSE;
 732    } else {
 733        nameval = argv[argc > 1];
 734        isQName =
 735            !JSVAL_IS_PRIMITIVE(nameval) &&
 736            JSVAL_TO_OBJECT(nameval)->getClass() == &QNameClass;
 737    }
 738
 739    /* QName called as function. */
 740    if (argc == 1 && isQName) {
 741        /* QName called with one QName argument is identity. */
 742        *rval = nameval;
 743        return JS_TRUE;
 744    }
 745
 746        /* Create and return a new QName object exactly as if constructed. */
 747    JSObject *obj = NewBuiltinClassInstanceXML(cx, &QNameClass);
 748    if (!obj)
 749        return JS_FALSE;
 750    *rval = OBJECT_TO_JSVAL(obj);
 751
 752    if (isQName) {
 753        /* If namespace is not specified and name is a QName, clone it. */
 754        qn = JSVAL_TO_OBJECT(nameval);
 755        if (argc == 1) {
 756            uri = qn->getNameURI();
 757            prefix = qn->getNamePrefix();
 758            name = qn->getQNameLocalName();
 759            goto out;
 760        }
 761
 762        /* Namespace and qname were passed -- use the qname's localName. */
 763        nameval = qn->getQNameLocalNameVal();
 764    }
 765
 766    if (argc == 0) {
 767        name = cx->runtime->emptyString;
 768    } else if (argc < 0) {
 769        name = cx->runtime->atomState.typeAtoms[JSTYPE_VOID];
 770    } else {
 771        if (!js_ValueToAtom(cx, nameval, &name))
 772            return false;
 773    }
 774
 775    if (argc > 1 && !JSVAL_IS_VOID(argv[0])) {
 776        nsval = argv[0];
 777    } else if (IS_STAR(name)) {
 778        nsval = JSVAL_NULL;
 779    } else {
 780        if (!js_GetDefaultXMLNamespace(cx, &nsval))
 781            return JS_FALSE;
 782        JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval));
 783        JS_ASSERT(JSVAL_TO_OBJECT(nsval)->getClass() ==
 784                  &NamespaceClass);
 785    }
 786
 787    if (JSVAL_IS_NULL(nsval)) {
 788        /* NULL prefix represents *undefined* in ECMA-357 13.3.2 5(a). */
 789        prefix = uri = NULL;
 790    } else {
 791        /*
 792         * Inline specialization of the Namespace constructor called with
 793         * nsval passed as the only argument, to compute the uri and prefix
 794         * for the constructed namespace, without actually allocating the
 795         * object or computing other members.  See ECMA-357 13.3.2 6(a) and
 796         * 13.2.2.
 797         */
 798        isNamespace = isQName = JS_FALSE;
 799        if (!JSVAL_IS_PRIMITIVE(nsval)) {
 800            obj2 = JSVAL_TO_OBJECT(nsval);
 801            isNamespace = (obj2->getClass() == &NamespaceClass);
 802            isQName = (obj2->getClass() == &QNameClass);
 803        }
 804#ifdef __GNUC__         /* suppress bogus gcc warnings */
 805        else obj2 = NULL;
 806#endif
 807
 808        if (isNamespace) {
 809            uri = obj2->getNameURI();
 810            prefix = obj2->getNamePrefix();
 811        } else if (isQName && (uri = obj2->getNameURI())) {
 812            JS_ASSERT(argc > 1);
 813            prefix = obj2->getNamePrefix();
 814        } else {
 815            JS_ASSERT(argc > 1);
 816            JSString *str = ToString(cx, nsval);
 817            if (!str)
 818                return JS_FALSE;
 819            uri = str->ensureLinear(cx);
 820            if (!uri)
 821                return JS_FALSE;
 822            argv[0] = STRING_TO_JSVAL(uri);     /* local root */
 823
 824            /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */
 825            prefix = uri->empty() ? cx->runtime->emptyString : NULL;
 826        }
 827    }
 828
 829out:
 830    return InitXMLQName(cx, obj, uri, prefix, name);
 831}
 832
 833static JSBool
 834QName(JSContext *cx, uintN argc, Value *vp)
 835{
 836    return QNameHelper(cx, argc, vp + 2, vp);
 837}
 838
 839/*
 840 * XMLArray library functions.
 841 */
 842static JSBool
 843namespace_identity(const JSObject *nsa, const JSObject *nsb)
 844{
 845    JSLinearString *prefixa = nsa->getNamePrefix();
 846    JSLinearString *prefixb = nsb->getNamePrefix();
 847
 848    if (prefixa && prefixb) {
 849        if (!EqualStrings(prefixa, prefixb))
 850            return JS_FALSE;
 851    } else {
 852        if (prefixa || prefixb)
 853            return JS_FALSE;
 854    }
 855    return EqualStrings(nsa->getNameURI(), nsb->getNameURI());
 856}
 857
 858static JSBool
 859attr_identity(const JSXML *xmla, const JSXML *xmlb)
 860{
 861    return qname_identity(xmla->name, xmlb->name);
 862}
 863
 864void
 865js_XMLArrayCursorTrace(JSTracer *trc, JSXMLArrayCursor<JSXML> *cursor)
 866{
 867    for (; cursor; cursor = cursor->next) {
 868        if (cursor->root)
 869            MarkXML(trc, (const HeapPtr<JSXML> &)cursor->root, "cursor_root");
 870    }
 871}
 872
 873void
 874js_XMLArrayCursorTrace(JSTracer *trc, JSXMLArrayCursor<JSObject> *cursor)
 875{
 876    for (; cursor; cursor = cursor->next) {
 877        if (cursor->root)
 878            MarkObject(trc, (const HeapPtr<JSObject> &)cursor->root, "cursor_root");
 879    }
 880}
 881
 882template<class T>
 883static HeapPtr<T> *
 884ReallocateVector(HeapPtr<T> *vector, size_t count)
 885{
 886#if JS_BITS_PER_WORD == 32
 887    if (count > ~(size_t)0 / sizeof(HeapPtr<T>))
 888        return NULL;
 889#endif
 890
 891    size_t size = count * sizeof(HeapPtr<T>);
 892    return (HeapPtr<T> *) OffTheBooks::realloc_(vector, size);
 893}
 894
 895/* NB: called with null cx from the GC, via xml_trace => JSXMLArray::trim. */
 896template<class T>
 897bool
 898JSXMLArray<T>::setCapacity(JSContext *cx, uint32_t newCapacity)
 899{
 900    if (newCapacity == 0) {
 901        /* We could let realloc(p, 0) free this, but purify gets confused. */
 902        if (vector) {
 903            if (cx)
 904                cx->free_(vector);
 905            else
 906                Foreground::free_(vector);
 907        }
 908        vector = NULL;
 909    } else {
 910        HeapPtr<T> *tmp = ReallocateVector(vector, newCapacity);
 911        if (!tmp) {
 912            if (cx)
 913                JS_ReportOutOfMemory(cx);
 914            return false;
 915        }
 916        vector = tmp;
 917    }
 918    capacity = JSXML_PRESET_CAPACITY | newCapacity;
 919    return true;
 920}
 921
 922template<class T>
 923void
 924JSXMLArray<T>::trim()
 925{
 926    if (capacity & JSXML_PRESET_CAPACITY)
 927        return;
 928    if (length < capacity)
 929        setCapacity(NULL, length);
 930}
 931
 932template<class T>
 933void
 934JSXMLArray<T>::finish(JSContext *cx)
 935{
 936    if (!cx->runtime->gcRunning) {
 937        /* We need to clear these to trigger a write barrier. */
 938        for (uint32_t i = 0; i < length; i++)
 939            vector[i].~HeapPtr<T>();
 940    }
 941
 942    cx->free_(vector);
 943
 944    while (JSXMLArrayCursor<T> *cursor = cursors)
 945        cursor->disconnect();
 946
 947#ifdef DEBUG
 948    memset(this, 0xd5, sizeof *this);
 949#endif
 950}
 951
 952#define XML_NOT_FOUND   UINT32_MAX
 953
 954template<class T, class U>
 955static uint32_t
 956XMLArrayFindMember(const JSXMLArray<T> *array, U *elt, typename IdentityOp<T, U>::compare identity)
 957{
 958    HeapPtr<T> *vector;
 959    uint32_t i, n;
 960
 961    /* The identity op must not reallocate array->vector. */
 962    vector = array->vector;
 963    for (i = 0, n = array->length; i < n; i++) {
 964        if (identity(vector[i].get(), elt))
 965            return i;
 966    }
 967    return XML_NOT_FOUND;
 968}
 969
 970/*
 971 * Grow array vector capacity by powers of two to LINEAR_THRESHOLD, and after
 972 * that, grow by LINEAR_INCREMENT.  Both must be powers of two, and threshold
 973 * should be greater than increment.
 974 */
 975#define LINEAR_THRESHOLD        256
 976#define LINEAR_INCREMENT        32
 977
 978template<class T>
 979static JSBool
 980XMLArrayAddMember(JSContext *cx, JSXMLArray<T> *array, uint32_t index, T *elt)
 981{
 982    uint32_t capacity, i;
 983    int log2;
 984    HeapPtr<T> *vector;
 985
 986    if (index >= array->length) {
 987        if (index >= JSXML_CAPACITY(array)) {
 988            /* Arrange to clear JSXML_PRESET_CAPACITY from array->capacity. */
 989            capacity = index + 1;
 990            if (index >= LINEAR_THRESHOLD) {
 991                capacity = JS_ROUNDUP(capacity, LINEAR_INCREMENT);
 992            } else {
 993                JS_CEILING_LOG2(log2, capacity);
 994                capacity = JS_BIT(log2);
 995            }
 996            if (!(vector = ReallocateVector(array->vector, capacity))) {
 997                JS_ReportOutOfMemory(cx);
 998                return JS_FALSE;
 999            }
1000            array->capacity = capacity;
1001            array->vector = vector;
1002            for (i = array->length; i < index; i++)
1003                vector[i].init(NULL);
1004        }
1005        array->vector[index].init(NULL);
1006        array->length = index + 1;
1007    }
1008
1009    array->vector[index] = elt;
1010    return JS_TRUE;
1011}
1012
1013template<class T>
1014static JSBool
1015XMLArrayInsert(JSContext *cx, JSXMLArray<T> *array, uint32_t i, uint32_t n)
1016{
1017    uint32_t j, k;
1018    JSXMLArrayCursor<T> *cursor;
1019
1020    j = array->length;
1021    JS_ASSERT(i <= j);
1022    if (!array->setCapacity(cx, j + n))
1023        return JS_FALSE;
1024
1025    k = j;
1026    while (k != j + n) {
1027        array->vector[k].init(NULL);
1028        k++;
1029    }
1030
1031    array->length = j + n;
1032    JS_ASSERT(n != (uint32_t)-1);
1033    while (j != i) {
1034        --j;
1035        array->vector[j + n] = array->vector[j];
1036    }
1037
1038    for (cursor = array->cursors; cursor; cursor = cursor->next) {
1039        if (cursor->index > i)
1040            cursor->index += n;
1041    }
1042    return JS_TRUE;
1043}
1044
1045template<class T>
1046static T *
1047XMLArrayDelete(JSContext *cx, JSXMLArray<T> *array, uint32_t index, JSBool compress)
1048{
1049    uint32_t length;
1050    HeapPtr<T> *vector;
1051    T *elt;
1052    JSXMLArrayCursor<T> *cursor;
1053
1054    length = array->length;
1055    if (index >= length)
1056        return NULL;
1057
1058    vector = array->vector;
1059    elt = vector[index];
1060    if (compress) {
1061        vector[length - 1].~HeapPtr<T>();
1062        while (++index < length)
1063            vector[index-1] = vector[index];
1064        array->length = length - 1;
1065        array->capacity = JSXML_CAPACITY(array);
1066    } else {
1067        vector[index] = NULL;
1068    }
1069
1070    for (cursor = array->cursors; cursor; cursor = cursor->next) {
1071        if (cursor->index > index)
1072            --cursor->index;
1073    }
1074    return elt;
1075}
1076
1077template<class T>
1078static void
1079XMLArrayTruncate(JSContext *cx, JSXMLArray<T> *array, uint32_t length)
1080{
1081    HeapPtr<T> *vector;
1082
1083    JS_ASSERT(!array->cursors);
1084    if (length >= array->length)
1085        return;
1086
1087    for (uint32_t i = length; i < array->length; i++)
1088        array->vector[i].~HeapPtr<T>();
1089
1090    if (length == 0) {
1091        if (array->vector)
1092            cx->free_(array->vector);
1093        vector = NULL;
1094    } else {
1095        vector = ReallocateVector(array->vector, length);
1096        if (!vector)
1097            return;
1098    }
1099
1100    if (array->length > length)
1101        array->length = length;
1102    array->capacity = length;
1103    array->vector = vector;
1104}
1105
1106#define XMLARRAY_FIND_MEMBER(a,e,f) XMLArrayFindMember(a, e, f)
1107#define XMLARRAY_HAS_MEMBER(a,e,f)  (XMLArrayFindMember(a, e, f) !=           \
1108                                     XML_NOT_FOUND)
1109#define XMLARRAY_MEMBER(a,i,t)      (((i) < (a)->length)                      \
1110                                     ? (a)->vector[i].get()                   \
1111                                     : NULL)
1112#define XMLARRAY_SET_MEMBER(a,i,e)  JS_BEGIN_MACRO                            \
1113                                        if ((a)->length <= (i)) {             \
1114                                            (a)->length = (i) + 1;            \
1115                                            ((a)->vector[i].init(e));         \
1116                                        } else {                              \
1117                                            ((a)->vector[i] = e);             \
1118                                        }                                     \
1119                                    JS_END_MACRO
1120#define XMLARRAY_ADD_MEMBER(x,a,i,e)XMLArrayAddMember(x, a, i, e)
1121#define XMLARRAY_INSERT(x,a,i,n)    XMLArrayInsert(x, a, i, n)
1122#define XMLARRAY_APPEND(x,a,e)      XMLARRAY_ADD_MEMBER(x, a, (a)->length, (e))
1123#define XMLARRAY_DELETE(x,a,i,c,t)  (XMLArrayDelete<t>(x, a, i, c))
1124#define XMLARRAY_TRUNCATE(x,a,n)    XMLArrayTruncate(x, a, n)
1125
1126/*
1127 * Define XML setting property strings and constants early, so everyone can
1128 * use the same names.
1129 */
1130static const char js_ignoreComments_str[]   = "ignoreComments";
1131static const char js_ignoreProcessingInstructions_str[]
1132                                            = "ignoreProcessingInstructions";
1133static const char js_ignoreWhitespace_str[] = "ignoreWhitespace";
1134static const char js_prettyPrinting_str[]   = "prettyPrinting";
1135static const char js_prettyIndent_str[]     = "prettyIndent";
1136
1137#define XSF_IGNORE_COMMENTS                JS_BIT(0)
1138#define XSF_IGNORE_PROCESSING_INSTRUCTIONS JS_BIT(1)
1139#define XSF_IGNORE_WHITESPACE              JS_BIT(2)
1140#define XSF_PRETTY_PRINTING                JS_BIT(3)
1141
1142static JSPropertySpec xml_static_props[] = {
1143    {js_ignoreComments_str, 0, JSPROP_PERMANENT, NULL, NULL},
1144    {js_ignoreProcessingInstructions_str, 0, JSPROP_PERMANENT, NULL, NULL},
1145    {js_ignoreWhitespace_str, 0, JSPROP_PERMANENT, NULL, NULL},
1146    {js_prettyPrinting_str, 0, JSPROP_PERMANENT, NULL, NULL},
1147    {js_prettyIndent_str, 0, JSPROP_PERMANENT, NULL, NULL},
1148    {0,0,0,0,0}
1149};
1150
1151/* Macros for special-casing xml:, xmlns= and xmlns:foo= in ParseNodeToQName. */
1152#define IS_XML(str)                                                           \
1153    (str->length() == 3 && IS_XML_CHARS(str->chars()))
1154
1155#define IS_XMLNS(str)                                                         \
1156    (str->length() == 5 && IS_XMLNS_CHARS(str->chars()))
1157
1158static inline bool
1159IS_XML_CHARS(const jschar *chars)
1160{
1161    return (chars[0] == 'x' || chars[0] == 'X') &&
1162           (chars[1] == 'm' || chars[1] == 'M') &&
1163           (chars[2] == 'l' || chars[2] == 'L');
1164}
1165
1166static inline bool
1167HAS_NS_AFTER_XML(const jschar *chars)
1168{
1169    return (chars[3] == 'n' || chars[3] == 'N') &&
1170           (chars[4] == 's' || chars[4] == 'S');
1171}
1172
1173#define IS_XMLNS_CHARS(chars)                                                 \
1174    (IS_XML_CHARS(chars) && HAS_NS_AFTER_XML(chars))
1175
1176#define STARTS_WITH_XML(chars,length)                                         \
1177    (length >= 3 && IS_XML_CHARS(chars))
1178
1179static const char xml_namespace_str[] = "http://www.w3.org/XML/1998/namespace";
1180static const char xmlns_namespace_str[] = "http://www.w3.org/2000/xmlns/";
1181
1182void
1183JSXML::finalize(JSContext *cx, bool builtin)
1184{
1185    if (JSXML_HAS_KIDS(this)) {
1186        xml_kids.finish(cx);
1187        if (xml_class == JSXML_CLASS_ELEMENT) {
1188            xml_namespaces.finish(cx);
1189            xml_attrs.finish(cx);
1190        }
1191    }
1192#ifdef DEBUG_notme
1193    JS_REMOVE_LINK(&links);
1194#endif
1195}
1196
1197static JSObject *
1198ParseNodeToQName(Parser *parser, ParseNode *pn,
1199                 JSXMLArray<JSObject> *inScopeNSes, JSBool isAttributeName)
1200{
1201    JSContext *cx = parser->context;
1202    JSLinearString *uri, *prefix;
1203    size_t length, offset;
1204    const jschar *start, *limit, *colon;
1205    uint32_t n;
1206    JSObject *ns;
1207    JSLinearString *nsprefix;
1208
1209    JS_ASSERT(pn->isArity(PN_NULLARY));
1210    JSAtom *str = pn->pn_atom;
1211    start = str->chars();
1212    length = str->length();
1213    JS_ASSERT(length != 0 && *start != '@');
1214    JS_ASSERT(length != 1 || *start != '*');
1215
1216    JSAtom *localName;
1217
1218    uri = cx->runtime->emptyString;
1219    limit = start + length;
1220    colon = js_strchr_limit(start, ':', limit);
1221    if (colon) {
1222        offset = colon - start;
1223        prefix = js_NewDependentString(cx, str, 0, offset);
1224        if (!prefix)
1225            return NULL;
1226
1227        if (STARTS_WITH_XML(start, offset)) {
1228            if (offset == 3) {
1229                uri = JS_ASSERT_STRING_IS_FLAT(JS_InternString(cx, xml_namespace_str));
1230                if (!uri)
1231                    return NULL;
1232            } else if (offset == 5 && HAS_NS_AFTER_XML(start)) {
1233                uri = JS_ASSERT_STRING_IS_FLAT(JS_InternString(cx, xmlns_namespace_str));
1234                if (!uri)
1235                    return NULL;
1236            } else {
1237                uri = NULL;
1238            }
1239        } else {
1240            uri = NULL;
1241            n = inScopeNSes->length;
1242            while (n != 0) {
1243                --n;
1244                ns = XMLARRAY_MEMBER(inScopeNSes, n, JSObject);
1245                nsprefix = ns->getNamePrefix();
1246                if (nsprefix && EqualStrings(nsprefix, prefix)) {
1247                    uri = ns->getNameURI();
1248                    break;
1249                }
1250            }
1251        }
1252
1253        if (!uri) {
1254            Value v = StringValue(prefix);
1255            JSAutoByteString bytes;
1256            if (js_ValueToPrintable(parser->context, v, &bytes)) {
1257                ReportCompileErrorNumber(parser->context, &parser->tokenStream, pn,
1258                                         JSREPORT_ERROR, JSMSG_BAD_XML_NAMESPACE, bytes.ptr());
1259            }
1260            return NULL;
1261        }
1262
1263        localName = js_AtomizeChars(parser->context, colon + 1, length - (offset + 1));
1264        if (!localName)
1265            return NULL;
1266    } else {
1267        if (isAttributeName) {
1268            /*
1269             * An unprefixed attribute is not in any namespace, so set prefix
1270             * as well as uri to the empty string.
1271             */
1272            prefix = uri;
1273        } else {
1274            /*
1275             * Loop from back to front looking for the closest declared default
1276             * namespace.
1277             */
1278            n = inScopeNSes->length;
1279            while (n != 0) {
1280                --n;
1281                ns = XMLARRAY_MEMBER(inScopeNSes, n, JSObject);
1282                nsprefix = ns->getNamePrefix();
1283                if (!nsprefix || nsprefix->empty()) {
1284                    uri = ns->getNameURI();
1285                    break;
1286                }
1287            }
1288            prefix = uri->empty() ? parser->context->runtime->emptyString : NULL;
1289        }
1290        localName = str;
1291    }
1292
1293    return NewXMLQName(parser->context, uri, prefix, localName);
1294}
1295
1296static JSString *
1297ChompXMLWhitespace(JSContext *cx, JSString *str)
1298{
1299    size_t length, newlength, offset;
1300    const jschar *cp, *start, *end;
1301    jschar c;
1302
1303    length = str->length();
1304    start = str->getChars(cx);
1305    if (!start)
1306        return NULL;
1307
1308    for (cp = start, end = cp + length; cp < end; cp++) {
1309        c = *cp;
1310        if (!unicode::IsXMLSpace(c))
1311            break;
1312    }
1313    while (end > cp) {
1314        c = end[-1];
1315        if (!unicode::IsXMLSpace(c))
1316            break;
1317        --end;
1318    }
1319    newlength = end - cp;
1320    if (newlength == length)
1321        return str;
1322    offset = cp - start;
1323    return js_NewDependentString(cx, str, offset, newlength);
1324}
1325
1326static JSXML *
1327ParseNodeToXML(Parser *parser, ParseNode *pn,
1328               JSXMLArray<JSObject> *inScopeNSes, uintN flags)
1329{
1330    JSContext *cx = parser->context;
1331    JSXML *xml, *kid, *attr, *attrj;
1332    JSLinearString *str;
1333    uint32_t length, n, i, j;
1334    ParseNode *pn2, *pn3, *head, **pnp;
1335    JSObject *ns;
1336    JSObject *qn, *attrjqn;
1337    JSXMLClass xml_class;
1338    int stackDummy;
1339
1340    if (!JS_CHECK_STACK_SIZE(cx->runtime->nativeStackLimit, &stackDummy)) {
1341        ReportCompileErrorNumber(cx, &parser->tokenStream, pn, JSREPORT_ERROR,
1342                                 JSMSG_OVER_RECURSED);
1343        return NULL;
1344    }
1345
1346#define PN2X_SKIP_CHILD ((JSXML *) 1)
1347
1348    /*
1349     * Cases return early to avoid common code that gets an outermost xml's
1350     * object, which protects GC-things owned by xml and its descendants from
1351     * garbage collection.
1352     */
1353    xml = NULL;
1354    if (!js_EnterLocalRootScope(cx))
1355        return NULL;
1356    switch (pn->getKind()) {
1357      case PNK_XMLELEM:
1358        length = inScopeNSes->length;
1359        pn2 = pn->pn_head;
1360        xml = ParseNodeToXML(parser, pn2, inScopeNSes, flags);
1361        if (!xml)
1362            goto fail;
1363
1364        n = pn->pn_count;
1365        JS_ASSERT(n >= 2);
1366        n -= 2;
1367        if (!xml->xml_kids.setCapacity(cx, n))
1368            goto fail;
1369
1370        i = 0;
1371        while ((pn2 = pn2->pn_next) != NULL) {
1372            if (!pn2->pn_next) {
1373                /* Don't append the end tag! */
1374                JS_ASSERT(pn2->isKind(PNK_XMLETAGO));
1375                break;
1376            }
1377
1378            if ((flags & XSF_IGNORE_WHITESPACE) &&
1379                n > 1 && pn2->isKind(PNK_XMLSPACE)) {
1380                --n;
1381                continue;
1382            }
1383
1384            kid = ParseNodeToXML(parser, pn2, inScopeNSes, flags);
1385            if (kid == PN2X_SKIP_CHILD) {
1386                --n;
1387                continue;
1388            }
1389
1390            if (!kid)
1391                goto fail;
1392
1393            /* Store kid in xml right away, to protect it from GC. */
1394            XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid);
1395            kid->parent = xml;
1396            ++i;
1397
1398            /* XXX where is this documented in an XML spec, or in E4X? */
1399            if ((flags & XSF_IGNORE_WHITESPACE) &&
1400                n > 1 && kid->xml_class == JSXML_CLASS_TEXT) {
1401                JSString *str = ChompXMLWhitespace(cx, kid->xml_value);
1402                if (!str)
1403                    goto fail;
1404                kid->xml_value = str;
1405            }
1406        }
1407
1408        JS_ASSERT(i == n);
1409        if (n < pn->pn_count - 2)
1410            xml->xml_kids.trim();
1411        XMLARRAY_TRUNCATE(cx, inScopeNSes, length);
1412        break;
1413
1414      case PNK_XMLLIST:
1415        xml = js_NewXML(cx, JSXML_CLASS_LIST);
1416        if (!xml)
1417            goto fail;
1418
1419        n = pn->pn_count;
1420        if (!xml->xml_kids.setCapacity(cx, n))
1421            goto fail;
1422
1423        i = 0;
1424        for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
1425            /*
1426             * Always ignore insignificant whitespace in lists -- we shouldn't
1427             * condition this on an XML.ignoreWhitespace setting when the list
1428             * constructor is XMLList (note XML/XMLList unification hazard).
1429             */
1430            if (pn2->isKind(PNK_XMLSPACE)) {
1431                --n;
1432                continue;
1433            }
1434
1435            kid = ParseNodeToXML(parser, pn2, inScopeNSes, flags);
1436            if (kid == PN2X_SKIP_CHILD) {
1437                --n;
1438                continue;
1439            }
1440
1441            if (!kid)
1442                goto fail;
1443
1444            XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid);
1445            ++i;
1446        }
1447
1448        if (n < pn->pn_count)
1449            xml->xml_kids.trim();
1450        break;
1451
1452      case PNK_XMLSTAGO:
1453      case PNK_XMLPTAGC:
1454        length = inScopeNSes->length;
1455        pn2 = pn->pn_head;
1456        JS_ASSERT(pn2->isKind(PNK_XMLNAME));
1457        if (pn2->isArity(PN_LIST))
1458            goto syntax;
1459
1460        xml = js_NewXML(cx, JSXML_CLASS_ELEMENT);
1461        if (!xml)
1462            goto fail;
1463
1464        /* First pass: check syntax and process namespace declarations. */
1465        JS_ASSERT(pn->pn_count >= 1);
1466        n = pn->pn_count - 1;
1467        pnp = &pn2->pn_next;
1468        head = *pnp;
1469        while ((pn2 = *pnp) != NULL) {
1470            size_t length;
1471            const jschar *chars;
1472
1473            if (!pn2->isKind(PNK_XMLNAME) || !pn2->isArity(PN_NULLARY))
1474                goto syntax;
1475
1476            /* Enforce "Well-formedness constraint: Unique Att Spec". */
1477            for (pn3 = head; pn3 != pn2; pn3 = pn3->pn_next->pn_next) {
1478                if (pn3->pn_atom == pn2->pn_atom) {
1479                    Value v = StringValue(pn2->pn_atom);
1480                    JSAutoByteString bytes;
1481                    if (js_ValueToPrintable(cx, v, &bytes)) {
1482                        ReportCompileErrorNumber(cx, &parser->tokenStream, pn2,
1483                                                 JSREPORT_ERROR, JSMSG_DUPLICATE_XML_ATTR,
1484                                                 bytes.ptr());
1485                    }
1486                    goto fail;
1487                }
1488            }
1489
1490            JSAtom *atom = pn2->pn_atom;
1491            pn2 = pn2->pn_next;
1492            JS_ASSERT(pn2);
1493            if (!pn2->isKind(PNK_XMLATTR))
1494                goto syntax;
1495
1496            chars = atom->chars();
1497            length = atom->length();
1498            if (length >= 5 &&
1499                IS_XMLNS_CHARS(chars) &&
1500                (length == 5 || chars[5] == ':')) {
1501                JSLinearString *uri, *prefix;
1502
1503                uri = pn2->pn_atom;
1504                if (length == 5) {
1505                    /* 10.3.2.1. Step 6(h)(i)(1)(a). */
1506                    prefix = cx->runtime->emptyString;
1507                } else {
1508                    prefix = js_NewStringCopyN(cx, chars + 6, length - 6);
1509                    if (!prefix)
1510                        goto fail;
1511                }
1512
1513                /*
1514                 * Once the new ns is appended to xml->xml_namespaces, it is
1515                 * protected from GC by the object that owns xml -- which is
1516                 * either xml->object if outermost, or the object owning xml's
1517                 * oldest ancestor if !outermost.
1518                 */
1519                ns = NewXMLNamespace(cx, prefix, uri, JS_TRUE);
1520                if (!ns)
1521                    goto fail;
1522
1523                /*
1524                 * Don't add a namespace that's already in scope.  If someone
1525                 * extracts a child property from its parent via [[Get]], then
1526                 * we enforce the invariant, noted many times in ECMA-357, that
1527                 * the child's namespaces form a possibly-improper superset of
1528                 * its ancestors' namespaces.
1529                 */
1530                if (!XMLARRAY_HAS_MEMBER(inScopeNSes, ns, namespace_identity)) {
1531                    if (!XMLARRAY_APPEND(cx, inScopeNSes, ns) ||
1532                        !XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) {
1533                        goto fail;
1534                    }
1535                }
1536
1537                JS_ASSERT(n >= 2);
1538                n -= 2;
1539                *pnp = pn2->pn_next;
1540                /* XXXbe recycle pn2 */
1541                continue;
1542            }
1543
1544            pnp = &pn2->pn_next;
1545        }
1546
1547        xml->xml_namespaces.trim();
1548
1549        /* Second pass: process tag name and attributes, using namespaces. */
1550        pn2 = pn->pn_head;
1551        qn = ParseNodeToQName(parser, pn2, inScopeNSes, JS_FALSE);
1552        if (!qn)
1553            goto fail;
1554        xml->name = qn;
1555
1556        JS_ASSERT((n & 1) == 0);
1557        n >>= 1;
1558        if (!xml->xml_attrs.setCapacity(cx, n))
1559            goto fail;
1560
1561        for (i = 0; (pn2 = pn2->pn_next) != NULL; i++) {
1562            qn = ParseNodeToQName(parser, pn2, inScopeNSes, JS_TRUE);
1563            if (!qn) {
1564                xml->xml_attrs.length = i;
1565                goto fail;
1566            }
1567
1568            /*
1569             * Enforce "Well-formedness constraint: Unique Att Spec", part 2:
1570             * this time checking local name and namespace URI.
1571             */
1572            for (j = 0; j < i; j++) {
1573                attrj = XMLARRAY_MEMBER(&xml->xml_attrs, j, JSXML);
1574                attrjqn = attrj->name;
1575                if (EqualStrings(attrjqn->getNameURI(), qn->getNameURI()) &&
1576                    EqualStrings(attrjqn->getQNameLocalName(), qn->getQNameLocalName())) {
1577                    Value v = StringValue(pn2->pn_atom);
1578                    JSAutoByteString bytes;
1579                    if (js_ValueToPrintable(cx, v, &bytes)) {
1580                        ReportCompileErrorNumber(cx, &parser->tokenStream, pn2,
1581                                                 JSREPORT_ERROR, JSMSG_DUPLICATE_XML_ATTR,
1582                                                 bytes.ptr());
1583                    }
1584                    goto fail;
1585                }
1586            }
1587
1588            pn2 = pn2->pn_next;
1589            JS_ASSERT(pn2);
1590            JS_ASSERT(pn2->isKind(PNK_XMLATTR));
1591
1592            attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE);
1593            if (!attr)
1594                goto fail;
1595
1596            XMLARRAY_SET_MEMBER(&xml->xml_attrs, i, attr);
1597            attr->parent = xml;
1598            attr->name = qn;
1599            attr->xml_value = pn2->pn_atom;
1600        }
1601
1602        /* Point tag closes its own namespace scope. */
1603        if (pn->isKind(PNK_XMLPTAGC))
1604            XMLARRAY_TRUNCATE(cx, inScopeNSes, length);
1605        break;
1606
1607      case PNK_XMLSPACE:
1608      case PNK_XMLTEXT:
1609      case PNK_XMLCDATA:
1610      case PNK_XMLCOMMENT:
1611      case PNK_XMLPI:
1612        str = pn->pn_atom;
1613        qn = NULL;
1614        if (pn->isKind(PNK_XMLCOMMENT)) {
1615            if (flags & XSF_IGNORE_COMMENTS)
1616                goto skip_child;
1617            xml_class = JSXML_CLASS_COMMENT;
1618        } else if (pn->isKind(PNK_XMLPI)) {
1619            XMLProcessingInstruction &pi = pn->asXMLProcessingInstruction();
1620            if (IS_XML(str)) {
1621                Value v = StringValue(str);
1622                JSAutoByteString bytes;
1623                if (js_ValueToPrintable(cx, v, &bytes)) {
1624                    ReportCompileErrorNumber(cx, &parser->tokenStream, &pi,
1625                                             JSREPORT_ERROR, JSMSG_RESERVED_ID, bytes.ptr());
1626                }
1627                goto fail;
1628            }
1629
1630            if (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS)
1631                goto skip_child;
1632
1633            qn = ParseNodeToQName(parser, &pi, inScopeNSes, JS_FALSE);
1634            if (!qn)
1635                goto fail;
1636
1637            str = pi.data();
1638            xml_class = JSXML_CLASS_PROCESSING_INSTRUCTION;
1639        } else {
1640       

Large files files are truncated, but you can click here to view the full file