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