/js/lib/Socket.IO-node/support/expresso/deps/jscoverage/js/jsxml.cpp
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
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 "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[] = "&"; 117const char js_gt_entity_str[] = ">"; 118const char js_lt_entity_str[] = "<"; 119const char js_quot_entity_str[] = """; 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 = NewXMLName…
Large files files are truncated, but you can click here to view the full file