PageRenderTime 71ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

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

http://github.com/onedayitwillmake/RealtimeMultiplayerNodeJs
C++ | 2138 lines | 1652 code | 255 blank | 231 comment | 438 complexity | 1bad5945bfae6a690cea885d2ba4c3d7 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause
  1. /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2. * vim: set ts=4 sw=4 et tw=78:
  3. *
  4. * ***** BEGIN LICENSE BLOCK *****
  5. * Version: MPL 1.1/GPL 2.0/LGPL 2.1
  6. *
  7. * The contents of this file are subject to the Mozilla Public License Version
  8. * 1.1 (the "License"); you may not use this file except in compliance with
  9. * the License. You may obtain a copy of the License at
  10. * http://www.mozilla.org/MPL/
  11. *
  12. * Software distributed under the License is distributed on an "AS IS" basis,
  13. * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  14. * for the specific language governing rights and limitations under the
  15. * License.
  16. *
  17. * The Original Code is SpiderMonkey E4X code, released August, 2004.
  18. *
  19. * The Initial Developer of the Original Code is
  20. * Netscape Communications Corporation.
  21. * Portions created by the Initial Developer are Copyright (C) 1998
  22. * the Initial Developer. All Rights Reserved.
  23. *
  24. * Contributor(s):
  25. *
  26. * Alternatively, the contents of this file may be used under the terms of
  27. * either of the GNU General Public License Version 2 or later (the "GPL"),
  28. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  29. * in which case the provisions of the GPL or the LGPL are applicable instead
  30. * of those above. If you wish to allow use of your version of this file only
  31. * under the terms of either the GPL or the LGPL, and not to allow others to
  32. * use your version of this file under the terms of the MPL, indicate your
  33. * decision by deleting the provisions above and replace them with the notice
  34. * and other provisions required by the GPL or the LGPL. If you do not delete
  35. * the provisions above, a recipient may use your version of this file under
  36. * the terms of any one of the MPL, the GPL or the LGPL.
  37. *
  38. * ***** END LICENSE BLOCK ***** */
  39. #include "jsstddef.h"
  40. #include "jsversion.h"
  41. #if JS_HAS_XML_SUPPORT
  42. #include <math.h>
  43. #include <stdlib.h>
  44. #include <string.h>
  45. #include "jstypes.h"
  46. #include "jsbit.h"
  47. #include "jsprf.h"
  48. #include "jsutil.h"
  49. #include "jsapi.h"
  50. #include "jsarray.h"
  51. #include "jsatom.h"
  52. #include "jsbool.h"
  53. #include "jscntxt.h"
  54. #include "jsfun.h"
  55. #include "jsgc.h"
  56. #include "jsinterp.h"
  57. #include "jslock.h"
  58. #include "jsnum.h"
  59. #include "jsobj.h"
  60. #include "jsopcode.h"
  61. #include "jsparse.h"
  62. #include "jsscan.h"
  63. #include "jsscope.h"
  64. #include "jsscript.h"
  65. #include "jsstr.h"
  66. #include "jsxml.h"
  67. #include "jsstaticcheck.h"
  68. #ifdef DEBUG
  69. #include <string.h> /* for #ifdef DEBUG memset calls */
  70. #endif
  71. /*
  72. * NOTES
  73. * - in the js shell, you must use the -x command line option, or call
  74. * options('xml') before compiling anything that uses XML literals
  75. *
  76. * TODO
  77. * - XXXbe patrol
  78. * - Fuse objects and their JSXML* private data into single GC-things
  79. * - fix function::foo vs. x.(foo == 42) collision using proper namespacing
  80. * - fix the !TCF_HAS_DEFXMLNS optimization in js_FoldConstants
  81. * - JSCLASS_DOCUMENT_OBSERVER support -- live two-way binding to Gecko's DOM!
  82. * - JS_TypeOfValue sure could use a cleaner interface to "types"
  83. */
  84. #ifdef XML_METERING
  85. static struct {
  86. jsrefcount qname;
  87. jsrefcount xmlnamespace;
  88. jsrefcount xml;
  89. jsrefcount xmlobj;
  90. } xml_stats;
  91. #define METER(x) JS_ATOMIC_INCREMENT(&(x))
  92. #define UNMETER(x) JS_ATOMIC_DECREMENT(&(x))
  93. #else
  94. #define METER(x) /* nothing */
  95. #define UNMETER(x) /* nothing */
  96. #endif
  97. /*
  98. * Random utilities and global functions.
  99. */
  100. const char js_isXMLName_str[] = "isXMLName";
  101. const char js_XMLList_str[] = "XMLList";
  102. const char js_localName_str[] = "localName";
  103. const char js_xml_parent_str[] = "parent";
  104. const char js_prefix_str[] = "prefix";
  105. const char js_toXMLString_str[] = "toXMLString";
  106. const char js_uri_str[] = "uri";
  107. const char js_amp_entity_str[] = "&amp;";
  108. const char js_gt_entity_str[] = "&gt;";
  109. const char js_lt_entity_str[] = "&lt;";
  110. const char js_quot_entity_str[] = "&quot;";
  111. #define IS_EMPTY(str) (JSSTRING_LENGTH(str) == 0)
  112. #define IS_STAR(str) (JSSTRING_LENGTH(str) == 1 && *JSSTRING_CHARS(str) == '*')
  113. /* Slot indexes shared between Namespace and QName objects. */
  114. const uint32 JSSLOT_PREFIX = JSSLOT_PRIVATE;
  115. const uint32 JSSLOT_URI = JSSLOT_PRIVATE + 1;
  116. /* Namespace-specific slot. */
  117. const uint32 JSSLOT_DECLARED = JSSLOT_PRIVATE + 2;
  118. /* QName-specific slot. */
  119. const uint32 JSSLOT_LOCAL_NAME = JSSLOT_PRIVATE + 2;
  120. const uint32 NAMESPACE_RESERVED_SLOTS = 3;
  121. const uint32 QNAME_RESERVED_SLOTS = 3;
  122. static JSBool
  123. IsQNameClass(JSClass *clasp)
  124. {
  125. return clasp == &js_QNameClass.base ||
  126. clasp == &js_AttributeNameClass ||
  127. clasp == &js_AnyNameClass;
  128. }
  129. static JSString *
  130. GetSlotString(const JSObject *obj, uint32 slot)
  131. {
  132. jsval v;
  133. JS_ASSERT(slot == JSSLOT_PREFIX ||
  134. slot == JSSLOT_URI ||
  135. slot == JSSLOT_LOCAL_NAME);
  136. JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_NamespaceClass.base ||
  137. IsQNameClass(STOBJ_GET_CLASS(obj)));
  138. JS_ASSERT_IF(STOBJ_GET_CLASS(obj) == &js_NamespaceClass.base,
  139. slot != JSSLOT_LOCAL_NAME);
  140. v = obj->fslots[slot];
  141. if (JSVAL_IS_VOID(v))
  142. return NULL;
  143. JS_ASSERT(JSVAL_IS_STRING(v));
  144. return JSVAL_TO_STRING(v);
  145. }
  146. static JS_INLINE JSString *
  147. GetPrefix(const JSObject *obj)
  148. {
  149. return GetSlotString(obj, JSSLOT_PREFIX);
  150. }
  151. static JSString *
  152. GetURI(const JSObject *obj)
  153. {
  154. return GetSlotString(obj, JSSLOT_URI);
  155. }
  156. static JSString *
  157. GetLocalName(const JSObject *obj)
  158. {
  159. return GetSlotString(obj, JSSLOT_LOCAL_NAME);
  160. }
  161. static JSBool
  162. IsDeclared(const JSObject *obj)
  163. {
  164. jsval v;
  165. JS_ASSERT(STOBJ_GET_CLASS(obj) == &js_NamespaceClass.base);
  166. v = obj->fslots[JSSLOT_DECLARED];
  167. JS_ASSERT(JSVAL_IS_VOID(v) || v == JSVAL_TRUE);
  168. return v == JSVAL_TRUE;
  169. }
  170. static JSBool
  171. xml_isXMLName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
  172. jsval *rval)
  173. {
  174. *rval = BOOLEAN_TO_JSVAL(js_IsXMLName(cx, argv[0]));
  175. return JS_TRUE;
  176. }
  177. /*
  178. * Namespace class and library functions.
  179. */
  180. enum namespace_tinyid {
  181. NAMESPACE_PREFIX = -1,
  182. NAMESPACE_URI = -2
  183. };
  184. static JSBool
  185. namespace_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  186. {
  187. if (!JSVAL_IS_INT(id))
  188. return JS_TRUE;
  189. if (STOBJ_GET_CLASS(obj) != &js_NamespaceClass.base)
  190. return JS_TRUE;
  191. switch (JSVAL_TO_INT(id)) {
  192. case NAMESPACE_PREFIX:
  193. *vp = obj->fslots[JSSLOT_PREFIX];
  194. break;
  195. case NAMESPACE_URI:
  196. *vp = obj->fslots[JSSLOT_URI];
  197. break;
  198. }
  199. return JS_TRUE;
  200. }
  201. static void
  202. namespace_finalize(JSContext *cx, JSObject *obj)
  203. {
  204. if (cx->runtime->functionNamespaceObject == obj)
  205. cx->runtime->functionNamespaceObject = NULL;
  206. }
  207. static JSBool
  208. namespace_equality(JSContext *cx, JSObject *obj, jsval v, JSBool *bp)
  209. {
  210. JSObject *obj2;
  211. JS_ASSERT(JSVAL_IS_OBJECT(v));
  212. obj2 = JSVAL_TO_OBJECT(v);
  213. *bp = (!obj2 || OBJ_GET_CLASS(cx, obj2) != &js_NamespaceClass.base)
  214. ? JS_FALSE
  215. : js_EqualStrings(GetURI(obj), GetURI(obj2));
  216. return JS_TRUE;
  217. }
  218. JS_FRIEND_DATA(JSExtendedClass) js_NamespaceClass = {
  219. { "Namespace",
  220. JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED |
  221. JSCLASS_HAS_RESERVED_SLOTS(NAMESPACE_RESERVED_SLOTS) |
  222. JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_Namespace),
  223. JS_PropertyStub, JS_PropertyStub, namespace_getProperty, NULL,
  224. JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, namespace_finalize,
  225. NULL, NULL, NULL, NULL,
  226. NULL, NULL, NULL, NULL },
  227. namespace_equality,NULL, NULL, NULL,
  228. NULL, NULL, NULL, NULL
  229. };
  230. #define NAMESPACE_ATTRS \
  231. (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
  232. static JSPropertySpec namespace_props[] = {
  233. {js_prefix_str, NAMESPACE_PREFIX, NAMESPACE_ATTRS, 0, 0},
  234. {js_uri_str, NAMESPACE_URI, NAMESPACE_ATTRS, 0, 0},
  235. {0,0,0,0,0}
  236. };
  237. static JSBool
  238. namespace_toString(JSContext *cx, uintN argc, jsval *vp)
  239. {
  240. JSObject *obj;
  241. obj = JS_THIS_OBJECT(cx, vp);
  242. if (!JS_InstanceOf(cx, obj, &js_NamespaceClass.base, vp))
  243. return JS_FALSE;
  244. *vp = obj->fslots[JSSLOT_URI];
  245. return JS_TRUE;
  246. }
  247. static JSFunctionSpec namespace_methods[] = {
  248. JS_FN(js_toString_str, namespace_toString, 0,0),
  249. JS_FS_END
  250. };
  251. static JSObject *
  252. NewXMLNamespace(JSContext *cx, JSString *prefix, JSString *uri, JSBool declared)
  253. {
  254. JSObject *obj;
  255. obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL, 0);
  256. if (!obj)
  257. return JS_FALSE;
  258. JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_PREFIX]));
  259. JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_URI]));
  260. JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_DECLARED]));
  261. if (prefix)
  262. obj->fslots[JSSLOT_PREFIX] = STRING_TO_JSVAL(prefix);
  263. if (uri)
  264. obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(uri);
  265. if (declared)
  266. obj->fslots[JSSLOT_DECLARED] = JSVAL_TRUE;
  267. METER(xml_stats.xmlnamespace);
  268. return obj;
  269. }
  270. /*
  271. * QName class and library functions.
  272. */
  273. enum qname_tinyid {
  274. QNAME_URI = -1,
  275. QNAME_LOCALNAME = -2
  276. };
  277. static JSBool
  278. qname_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  279. {
  280. if (!JSVAL_IS_INT(id))
  281. return JS_TRUE;
  282. if (STOBJ_GET_CLASS(obj) != &js_QNameClass.base)
  283. return JS_TRUE;
  284. switch (JSVAL_TO_INT(id)) {
  285. case QNAME_URI:
  286. *vp = obj->fslots[JSSLOT_URI];
  287. if (*vp == JSVAL_VOID)
  288. *vp = JSVAL_NULL;
  289. break;
  290. case QNAME_LOCALNAME:
  291. *vp = obj->fslots[JSSLOT_LOCAL_NAME];
  292. break;
  293. }
  294. return JS_TRUE;
  295. }
  296. static void
  297. anyname_finalize(JSContext* cx, JSObject* obj)
  298. {
  299. /* Make sure the next call to js_GetAnyName doesn't try to use obj. */
  300. if (cx->runtime->anynameObject == obj)
  301. cx->runtime->anynameObject = NULL;
  302. }
  303. static JSBool
  304. qname_identity(JSObject *qna, JSObject *qnb)
  305. {
  306. JSString *uri1 = GetURI(qna);
  307. JSString *uri2 = GetURI(qnb);
  308. if (!uri1 ^ !uri2)
  309. return JS_FALSE;
  310. if (uri1 && !js_EqualStrings(uri1, uri2))
  311. return JS_FALSE;
  312. return js_EqualStrings(GetLocalName(qna), GetLocalName(qnb));
  313. }
  314. static JSBool
  315. qname_equality(JSContext *cx, JSObject *qn, jsval v, JSBool *bp)
  316. {
  317. JSObject *obj2;
  318. JS_ASSERT(JSVAL_IS_OBJECT(v));
  319. obj2 = JSVAL_TO_OBJECT(v);
  320. *bp = (!obj2 || OBJ_GET_CLASS(cx, obj2) != &js_QNameClass.base)
  321. ? JS_FALSE
  322. : qname_identity(qn, obj2);
  323. return JS_TRUE;
  324. }
  325. JS_FRIEND_DATA(JSExtendedClass) js_QNameClass = {
  326. { "QName",
  327. JSCLASS_CONSTRUCT_PROTOTYPE | JSCLASS_IS_EXTENDED |
  328. JSCLASS_HAS_RESERVED_SLOTS(QNAME_RESERVED_SLOTS) |
  329. JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_QName),
  330. JS_PropertyStub, JS_PropertyStub, qname_getProperty, NULL,
  331. JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
  332. NULL, NULL, NULL, NULL,
  333. NULL, NULL, NULL, NULL },
  334. qname_equality, NULL, NULL, NULL,
  335. NULL, NULL, NULL, NULL
  336. };
  337. /*
  338. * Classes for the ECMA-357-internal types AttributeName and AnyName, which
  339. * are like QName, except that they have no property getters. They share the
  340. * qname_toString method, and therefore are exposed as constructable objects
  341. * in this implementation.
  342. */
  343. JS_FRIEND_DATA(JSClass) js_AttributeNameClass = {
  344. js_AttributeName_str,
  345. JSCLASS_CONSTRUCT_PROTOTYPE |
  346. JSCLASS_HAS_RESERVED_SLOTS(QNAME_RESERVED_SLOTS) |
  347. JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_AttributeName),
  348. JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
  349. JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, JS_FinalizeStub,
  350. NULL, NULL, NULL, NULL,
  351. NULL, NULL, NULL, NULL
  352. };
  353. JS_FRIEND_DATA(JSClass) js_AnyNameClass = {
  354. js_AnyName_str,
  355. JSCLASS_CONSTRUCT_PROTOTYPE |
  356. JSCLASS_HAS_RESERVED_SLOTS(QNAME_RESERVED_SLOTS) |
  357. JSCLASS_MARK_IS_TRACE | JSCLASS_HAS_CACHED_PROTO(JSProto_AnyName),
  358. JS_PropertyStub, JS_PropertyStub, JS_PropertyStub, JS_PropertyStub,
  359. JS_EnumerateStub, JS_ResolveStub, JS_ConvertStub, anyname_finalize,
  360. NULL, NULL, NULL, NULL,
  361. NULL, NULL, NULL, NULL
  362. };
  363. #define QNAME_ATTRS \
  364. (JSPROP_ENUMERATE | JSPROP_READONLY | JSPROP_PERMANENT | JSPROP_SHARED)
  365. static JSPropertySpec qname_props[] = {
  366. {js_uri_str, QNAME_URI, QNAME_ATTRS, 0, 0},
  367. {js_localName_str, QNAME_LOCALNAME, QNAME_ATTRS, 0, 0},
  368. {0,0,0,0,0}
  369. };
  370. static JSBool
  371. qname_toString(JSContext *cx, uintN argc, jsval *vp)
  372. {
  373. JSObject *obj;
  374. JSClass *clasp;
  375. JSString *uri, *str, *qualstr;
  376. size_t length;
  377. jschar *chars;
  378. obj = JS_THIS_OBJECT(cx, vp);
  379. if (!obj)
  380. return JS_FALSE;
  381. clasp = OBJ_GET_CLASS(cx, obj);
  382. if (clasp != &js_AttributeNameClass &&
  383. clasp != &js_AnyNameClass &&
  384. !JS_InstanceOf(cx, obj, &js_QNameClass.base, vp + 2)) {
  385. return JS_FALSE;
  386. }
  387. uri = GetURI(obj);
  388. if (!uri) {
  389. /* No uri means wildcard qualifier. */
  390. str = ATOM_TO_STRING(cx->runtime->atomState.starQualifierAtom);
  391. } else if (IS_EMPTY(uri)) {
  392. /* Empty string for uri means localName is in no namespace. */
  393. str = cx->runtime->emptyString;
  394. } else {
  395. qualstr = ATOM_TO_STRING(cx->runtime->atomState.qualifierAtom);
  396. str = js_ConcatStrings(cx, uri, qualstr);
  397. if (!str)
  398. return JS_FALSE;
  399. }
  400. str = js_ConcatStrings(cx, str, GetLocalName(obj));
  401. if (!str)
  402. return JS_FALSE;
  403. if (str && clasp == &js_AttributeNameClass) {
  404. length = JSSTRING_LENGTH(str);
  405. chars = (jschar *) JS_malloc(cx, (length + 2) * sizeof(jschar));
  406. if (!chars)
  407. return JS_FALSE;
  408. *chars = '@';
  409. js_strncpy(chars + 1, JSSTRING_CHARS(str), length);
  410. chars[++length] = 0;
  411. str = js_NewString(cx, chars, length);
  412. if (!str) {
  413. JS_free(cx, chars);
  414. return JS_FALSE;
  415. }
  416. }
  417. *vp = STRING_TO_JSVAL(str);
  418. return JS_TRUE;
  419. }
  420. static JSFunctionSpec qname_methods[] = {
  421. JS_FN(js_toString_str, qname_toString, 0,0),
  422. JS_FS_END
  423. };
  424. static void
  425. InitXMLQName(JSObject *obj, JSString *uri, JSString *prefix,
  426. JSString *localName)
  427. {
  428. JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_PREFIX]));
  429. JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_URI]));
  430. JS_ASSERT(JSVAL_IS_VOID(obj->fslots[JSSLOT_LOCAL_NAME]));
  431. if (uri)
  432. obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(uri);
  433. if (prefix)
  434. obj->fslots[JSSLOT_PREFIX] = STRING_TO_JSVAL(prefix);
  435. if (localName)
  436. obj->fslots[JSSLOT_LOCAL_NAME] = STRING_TO_JSVAL(localName);
  437. }
  438. static JSObject *
  439. NewXMLQName(JSContext *cx, JSString *uri, JSString *prefix, JSString *localName,
  440. JSClass *clasp = &js_QNameClass.base)
  441. {
  442. JSObject *obj;
  443. JS_ASSERT(IsQNameClass(clasp));
  444. obj = js_NewObject(cx, clasp, NULL, NULL, 0);
  445. if (!obj)
  446. return NULL;
  447. InitXMLQName(obj, uri, prefix, localName);
  448. METER(xml_stats.qname);
  449. return obj;
  450. }
  451. JSObject *
  452. js_ConstructXMLQNameObject(JSContext *cx, jsval nsval, jsval lnval)
  453. {
  454. jsval argv[2];
  455. /*
  456. * ECMA-357 11.1.2,
  457. * The _QualifiedIdentifier : PropertySelector :: PropertySelector_
  458. * production, step 2.
  459. */
  460. if (!JSVAL_IS_PRIMITIVE(nsval) &&
  461. OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nsval)) == &js_AnyNameClass) {
  462. nsval = JSVAL_NULL;
  463. }
  464. argv[0] = nsval;
  465. argv[1] = lnval;
  466. return js_ConstructObject(cx, &js_QNameClass.base, NULL, NULL, 2, argv);
  467. }
  468. static JSBool
  469. IsXMLName(const jschar *cp, size_t n)
  470. {
  471. JSBool rv;
  472. jschar c;
  473. rv = JS_FALSE;
  474. if (n != 0 && JS_ISXMLNSSTART(*cp)) {
  475. while (--n != 0) {
  476. c = *++cp;
  477. if (!JS_ISXMLNS(c))
  478. return rv;
  479. }
  480. rv = JS_TRUE;
  481. }
  482. return rv;
  483. }
  484. JSBool
  485. js_IsXMLName(JSContext *cx, jsval v)
  486. {
  487. JSString *name;
  488. JSErrorReporter older;
  489. /*
  490. * Inline specialization of the QName constructor called with v passed as
  491. * the only argument, to compute the localName for the constructed qname,
  492. * without actually allocating the object or computing its uri and prefix.
  493. * See ECMA-357 13.1.2.1 step 1 and 13.3.2.
  494. */
  495. if (!JSVAL_IS_PRIMITIVE(v) &&
  496. IsQNameClass(OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(v)))) {
  497. name = GetLocalName(JSVAL_TO_OBJECT(v));
  498. } else {
  499. older = JS_SetErrorReporter(cx, NULL);
  500. name = js_ValueToString(cx, v);
  501. JS_SetErrorReporter(cx, older);
  502. if (!name) {
  503. JS_ClearPendingException(cx);
  504. return JS_FALSE;
  505. }
  506. }
  507. return IsXMLName(JSSTRING_CHARS(name), JSSTRING_LENGTH(name));
  508. }
  509. /*
  510. * When argc is -1, it indicates argv is empty but the code should behave as
  511. * if argc is 1 and argv[0] is JSVAL_VOID.
  512. */
  513. static JSBool
  514. NamespaceHelper(JSContext *cx, JSObject *obj, intN argc, jsval *argv,
  515. jsval *rval)
  516. {
  517. jsval urival, prefixval;
  518. JSObject *uriobj;
  519. JSBool isNamespace, isQName;
  520. JSClass *clasp;
  521. JSString *empty, *uri, *prefix;
  522. isNamespace = isQName = JS_FALSE;
  523. #ifdef __GNUC__ /* suppress bogus gcc warnings */
  524. uriobj = NULL;
  525. #endif
  526. if (argc <= 0) {
  527. urival = JSVAL_VOID;
  528. } else {
  529. urival = argv[argc > 1];
  530. if (!JSVAL_IS_PRIMITIVE(urival)) {
  531. uriobj = JSVAL_TO_OBJECT(urival);
  532. clasp = OBJ_GET_CLASS(cx, uriobj);
  533. isNamespace = (clasp == &js_NamespaceClass.base);
  534. isQName = (clasp == &js_QNameClass.base);
  535. }
  536. }
  537. if (!obj) {
  538. /* Namespace called as function. */
  539. if (argc == 1 && isNamespace) {
  540. /* Namespace called with one Namespace argument is identity. */
  541. *rval = urival;
  542. return JS_TRUE;
  543. }
  544. obj = js_NewObject(cx, &js_NamespaceClass.base, NULL, NULL, 0);
  545. if (!obj)
  546. return JS_FALSE;
  547. *rval = OBJECT_TO_JSVAL(obj);
  548. }
  549. METER(xml_stats.xmlnamespace);
  550. empty = cx->runtime->emptyString;
  551. obj->fslots[JSSLOT_PREFIX] = STRING_TO_JSVAL(empty);
  552. obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(empty);
  553. if (argc == 1 || argc == -1) {
  554. if (isNamespace) {
  555. obj->fslots[JSSLOT_URI] = uriobj->fslots[JSSLOT_URI];
  556. obj->fslots[JSSLOT_PREFIX] = uriobj->fslots[JSSLOT_PREFIX];
  557. } else if (isQName && (uri = GetURI(uriobj))) {
  558. obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(uri);
  559. obj->fslots[JSSLOT_PREFIX] = uriobj->fslots[JSSLOT_PREFIX];
  560. } else {
  561. uri = js_ValueToString(cx, urival);
  562. if (!uri)
  563. return JS_FALSE;
  564. obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(uri);
  565. if (!IS_EMPTY(uri))
  566. obj->fslots[JSSLOT_PREFIX] = JSVAL_VOID;
  567. }
  568. } else if (argc == 2) {
  569. if (!isQName || !(uri = GetURI(uriobj))) {
  570. uri = js_ValueToString(cx, urival);
  571. if (!uri)
  572. return JS_FALSE;
  573. }
  574. obj->fslots[JSSLOT_URI] = STRING_TO_JSVAL(uri);
  575. prefixval = argv[0];
  576. if (IS_EMPTY(uri)) {
  577. if (!JSVAL_IS_VOID(prefixval)) {
  578. prefix = js_ValueToString(cx, prefixval);
  579. if (!prefix)
  580. return JS_FALSE;
  581. if (!IS_EMPTY(prefix)) {
  582. JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
  583. JSMSG_BAD_XML_NAMESPACE,
  584. js_ValueToPrintableString(cx,
  585. STRING_TO_JSVAL(prefix)));
  586. return JS_FALSE;
  587. }
  588. }
  589. } else if (JSVAL_IS_VOID(prefixval) || !js_IsXMLName(cx, prefixval)) {
  590. obj->fslots[JSSLOT_PREFIX] = JSVAL_VOID;
  591. } else {
  592. prefix = js_ValueToString(cx, prefixval);
  593. if (!prefix)
  594. return JS_FALSE;
  595. obj->fslots[JSSLOT_PREFIX] = STRING_TO_JSVAL(prefix);
  596. }
  597. }
  598. return JS_TRUE;
  599. }
  600. static JSBool
  601. Namespace(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  602. {
  603. return NamespaceHelper(cx,
  604. (cx->fp->flags & JSFRAME_CONSTRUCTING) ? obj : NULL,
  605. argc, argv, rval);
  606. }
  607. /*
  608. * When argc is -1, it indicates argv is empty but the code should behave as
  609. * if argc is 1 and argv[0] is JSVAL_VOID.
  610. */
  611. static JSBool
  612. QNameHelper(JSContext *cx, JSObject *obj, JSClass *clasp, intN argc,
  613. jsval *argv, jsval *rval)
  614. {
  615. jsval nameval, nsval;
  616. JSBool isQName, isNamespace;
  617. JSObject *qn;
  618. JSString *uri, *prefix, *name;
  619. JSObject *obj2;
  620. JS_ASSERT(clasp == &js_QNameClass.base ||
  621. clasp == &js_AttributeNameClass);
  622. if (argc <= 0) {
  623. nameval = JSVAL_VOID;
  624. isQName = JS_FALSE;
  625. } else {
  626. nameval = argv[argc > 1];
  627. isQName =
  628. !JSVAL_IS_PRIMITIVE(nameval) &&
  629. OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nameval)) == &js_QNameClass.base;
  630. }
  631. if (!obj) {
  632. /* QName called as function. */
  633. if (argc == 1 && isQName) {
  634. /* QName called with one QName argument is identity. */
  635. *rval = nameval;
  636. return JS_TRUE;
  637. }
  638. /*
  639. * Create and return a new QName or AttributeName object exactly as if
  640. * constructed.
  641. */
  642. obj = js_NewObject(cx, clasp, NULL, NULL, 0);
  643. if (!obj)
  644. return JS_FALSE;
  645. *rval = OBJECT_TO_JSVAL(obj);
  646. }
  647. METER(xml_stats.qname);
  648. if (isQName) {
  649. /* If namespace is not specified and name is a QName, clone it. */
  650. qn = JSVAL_TO_OBJECT(nameval);
  651. if (argc == 1) {
  652. uri = GetURI(qn);
  653. prefix = GetPrefix(qn);
  654. name = GetLocalName(qn);
  655. goto out;
  656. }
  657. /* Namespace and qname were passed -- use the qname's localName. */
  658. nameval = qn->fslots[JSSLOT_LOCAL_NAME];
  659. }
  660. if (argc == 0) {
  661. name = cx->runtime->emptyString;
  662. } else if (argc < 0) {
  663. name = ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
  664. } else {
  665. name = js_ValueToString(cx, nameval);
  666. if (!name)
  667. return JS_FALSE;
  668. argv[argc > 1] = STRING_TO_JSVAL(name);
  669. }
  670. if (argc > 1 && !JSVAL_IS_VOID(argv[0])) {
  671. nsval = argv[0];
  672. } else if (IS_STAR(name)) {
  673. nsval = JSVAL_NULL;
  674. } else {
  675. if (!js_GetDefaultXMLNamespace(cx, &nsval))
  676. return JS_FALSE;
  677. JS_ASSERT(!JSVAL_IS_PRIMITIVE(nsval));
  678. JS_ASSERT(OBJ_GET_CLASS(cx, JSVAL_TO_OBJECT(nsval)) ==
  679. &js_NamespaceClass.base);
  680. }
  681. if (JSVAL_IS_NULL(nsval)) {
  682. /* NULL prefix represents *undefined* in ECMA-357 13.3.2 5(a). */
  683. uri = prefix = NULL;
  684. } else {
  685. /*
  686. * Inline specialization of the Namespace constructor called with
  687. * nsval passed as the only argument, to compute the uri and prefix
  688. * for the constructed namespace, without actually allocating the
  689. * object or computing other members. See ECMA-357 13.3.2 6(a) and
  690. * 13.2.2.
  691. */
  692. isNamespace = isQName = JS_FALSE;
  693. if (!JSVAL_IS_PRIMITIVE(nsval)) {
  694. obj2 = JSVAL_TO_OBJECT(nsval);
  695. clasp = OBJ_GET_CLASS(cx, obj2);
  696. isNamespace = (clasp == &js_NamespaceClass.base);
  697. isQName = (clasp == &js_QNameClass.base);
  698. }
  699. #ifdef __GNUC__ /* suppress bogus gcc warnings */
  700. else obj2 = NULL;
  701. #endif
  702. if (isNamespace) {
  703. uri = GetURI(obj2);
  704. prefix = GetPrefix(obj2);
  705. } else if (isQName && (uri = GetURI(obj2))) {
  706. JS_ASSERT(argc > 1);
  707. prefix = GetPrefix(obj2);
  708. } else {
  709. JS_ASSERT(argc > 1);
  710. uri = js_ValueToString(cx, nsval);
  711. if (!uri)
  712. return JS_FALSE;
  713. argv[0] = STRING_TO_JSVAL(uri); /* local root */
  714. /* NULL here represents *undefined* in ECMA-357 13.2.2 3(c)iii. */
  715. prefix = IS_EMPTY(uri) ? cx->runtime->emptyString : NULL;
  716. }
  717. }
  718. out:
  719. InitXMLQName(obj, uri, prefix, name);
  720. return JS_TRUE;
  721. }
  722. static JSBool
  723. QName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  724. {
  725. return QNameHelper(cx, (cx->fp->flags & JSFRAME_CONSTRUCTING) ? obj : NULL,
  726. &js_QNameClass.base, argc, argv, rval);
  727. }
  728. static JSBool
  729. AttributeName(JSContext *cx, JSObject *obj, uintN argc, jsval *argv,
  730. jsval *rval)
  731. {
  732. return QNameHelper(cx, (cx->fp->flags & JSFRAME_CONSTRUCTING) ? obj : NULL,
  733. &js_AttributeNameClass, argc, argv, rval);
  734. }
  735. /*
  736. * XMLArray library functions.
  737. */
  738. static JSBool
  739. namespace_identity(const void *a, const void *b)
  740. {
  741. const JSObject *nsa = (const JSObject *) a;
  742. const JSObject *nsb = (const JSObject *) b;
  743. JSString *prefixa = GetPrefix(nsa);
  744. JSString *prefixb = GetPrefix(nsb);
  745. if (prefixa && prefixb) {
  746. if (!js_EqualStrings(prefixa, prefixb))
  747. return JS_FALSE;
  748. } else {
  749. if (prefixa || prefixb)
  750. return JS_FALSE;
  751. }
  752. return js_EqualStrings(GetURI(nsa), GetURI(nsb));
  753. }
  754. static JSBool
  755. attr_identity(const void *a, const void *b)
  756. {
  757. const JSXML *xmla = (const JSXML *) a;
  758. const JSXML *xmlb = (const JSXML *) b;
  759. return qname_identity(xmla->name, xmlb->name);
  760. }
  761. static void
  762. XMLArrayCursorInit(JSXMLArrayCursor *cursor, JSXMLArray *array)
  763. {
  764. JSXMLArrayCursor *next;
  765. cursor->array = array;
  766. cursor->index = 0;
  767. next = cursor->next = array->cursors;
  768. if (next)
  769. next->prevp = &cursor->next;
  770. cursor->prevp = &array->cursors;
  771. array->cursors = cursor;
  772. cursor->root = NULL;
  773. }
  774. static void
  775. XMLArrayCursorFinish(JSXMLArrayCursor *cursor)
  776. {
  777. JSXMLArrayCursor *next;
  778. if (!cursor->array)
  779. return;
  780. next = cursor->next;
  781. if (next)
  782. next->prevp = cursor->prevp;
  783. *cursor->prevp = next;
  784. cursor->array = NULL;
  785. }
  786. static void *
  787. XMLArrayCursorNext(JSXMLArrayCursor *cursor)
  788. {
  789. JSXMLArray *array;
  790. array = cursor->array;
  791. if (!array || cursor->index >= array->length)
  792. return NULL;
  793. return cursor->root = array->vector[cursor->index++];
  794. }
  795. static void *
  796. XMLArrayCursorItem(JSXMLArrayCursor *cursor)
  797. {
  798. JSXMLArray *array;
  799. array = cursor->array;
  800. if (!array || cursor->index >= array->length)
  801. return NULL;
  802. return cursor->root = array->vector[cursor->index];
  803. }
  804. static void
  805. XMLArrayCursorTrace(JSTracer *trc, JSXMLArrayCursor *cursor)
  806. {
  807. void *root;
  808. #ifdef DEBUG
  809. size_t index = 0;
  810. #endif
  811. for (; cursor; cursor = cursor->next) {
  812. root = cursor->root;
  813. JS_SET_TRACING_INDEX(trc, "cursor_root", index++);
  814. js_CallValueTracerIfGCThing(trc, (jsval)root);
  815. }
  816. }
  817. /* NB: called with null cx from the GC, via xml_trace => XMLArrayTrim. */
  818. static JSBool
  819. XMLArraySetCapacity(JSContext *cx, JSXMLArray *array, uint32 capacity)
  820. {
  821. void **vector;
  822. if (capacity == 0) {
  823. /* We could let realloc(p, 0) free this, but purify gets confused. */
  824. if (array->vector)
  825. free(array->vector);
  826. vector = NULL;
  827. } else {
  828. if (
  829. #if JS_BITS_PER_WORD == 32
  830. (size_t)capacity > ~(size_t)0 / sizeof(void *) ||
  831. #endif
  832. !(vector = (void **)
  833. realloc(array->vector, capacity * sizeof(void *)))) {
  834. if (cx)
  835. JS_ReportOutOfMemory(cx);
  836. return JS_FALSE;
  837. }
  838. }
  839. array->capacity = JSXML_PRESET_CAPACITY | capacity;
  840. array->vector = vector;
  841. return JS_TRUE;
  842. }
  843. static void
  844. XMLArrayTrim(JSXMLArray *array)
  845. {
  846. if (array->capacity & JSXML_PRESET_CAPACITY)
  847. return;
  848. if (array->length < array->capacity)
  849. XMLArraySetCapacity(NULL, array, array->length);
  850. }
  851. static JSBool
  852. XMLArrayInit(JSContext *cx, JSXMLArray *array, uint32 capacity)
  853. {
  854. array->length = array->capacity = 0;
  855. array->vector = NULL;
  856. array->cursors = NULL;
  857. return capacity == 0 || XMLArraySetCapacity(cx, array, capacity);
  858. }
  859. static void
  860. XMLArrayFinish(JSContext *cx, JSXMLArray *array)
  861. {
  862. JSXMLArrayCursor *cursor;
  863. JS_free(cx, array->vector);
  864. while ((cursor = array->cursors) != NULL)
  865. XMLArrayCursorFinish(cursor);
  866. #ifdef DEBUG
  867. memset(array, 0xd5, sizeof *array);
  868. #endif
  869. }
  870. #define XML_NOT_FOUND ((uint32) -1)
  871. static uint32
  872. XMLArrayFindMember(const JSXMLArray *array, void *elt, JSIdentityOp identity)
  873. {
  874. void **vector;
  875. uint32 i, n;
  876. /* The identity op must not reallocate array->vector. */
  877. vector = array->vector;
  878. if (identity) {
  879. for (i = 0, n = array->length; i < n; i++) {
  880. if (identity(vector[i], elt))
  881. return i;
  882. }
  883. } else {
  884. for (i = 0, n = array->length; i < n; i++) {
  885. if (vector[i] == elt)
  886. return i;
  887. }
  888. }
  889. return XML_NOT_FOUND;
  890. }
  891. /*
  892. * Grow array vector capacity by powers of two to LINEAR_THRESHOLD, and after
  893. * that, grow by LINEAR_INCREMENT. Both must be powers of two, and threshold
  894. * should be greater than increment.
  895. */
  896. #define LINEAR_THRESHOLD 256
  897. #define LINEAR_INCREMENT 32
  898. static JSBool
  899. XMLArrayAddMember(JSContext *cx, JSXMLArray *array, uint32 index, void *elt)
  900. {
  901. uint32 capacity, i;
  902. int log2;
  903. void **vector;
  904. if (index >= array->length) {
  905. if (index >= JSXML_CAPACITY(array)) {
  906. /* Arrange to clear JSXML_PRESET_CAPACITY from array->capacity. */
  907. capacity = index + 1;
  908. if (index >= LINEAR_THRESHOLD) {
  909. capacity = JS_ROUNDUP(capacity, LINEAR_INCREMENT);
  910. } else {
  911. JS_CEILING_LOG2(log2, capacity);
  912. capacity = JS_BIT(log2);
  913. }
  914. if (
  915. #if JS_BITS_PER_WORD == 32
  916. (size_t)capacity > ~(size_t)0 / sizeof(void *) ||
  917. #endif
  918. !(vector = (void **)
  919. realloc(array->vector, capacity * sizeof(void *)))) {
  920. JS_ReportOutOfMemory(cx);
  921. return JS_FALSE;
  922. }
  923. array->capacity = capacity;
  924. array->vector = vector;
  925. for (i = array->length; i < index; i++)
  926. vector[i] = NULL;
  927. }
  928. array->length = index + 1;
  929. }
  930. array->vector[index] = elt;
  931. return JS_TRUE;
  932. }
  933. static JSBool
  934. XMLArrayInsert(JSContext *cx, JSXMLArray *array, uint32 i, uint32 n)
  935. {
  936. uint32 j;
  937. JSXMLArrayCursor *cursor;
  938. j = array->length;
  939. JS_ASSERT(i <= j);
  940. if (!XMLArraySetCapacity(cx, array, j + n))
  941. return JS_FALSE;
  942. array->length = j + n;
  943. JS_ASSERT(n != (uint32)-1);
  944. while (j != i) {
  945. --j;
  946. array->vector[j + n] = array->vector[j];
  947. }
  948. for (cursor = array->cursors; cursor; cursor = cursor->next) {
  949. if (cursor->index > i)
  950. cursor->index += n;
  951. }
  952. return JS_TRUE;
  953. }
  954. static void *
  955. XMLArrayDelete(JSContext *cx, JSXMLArray *array, uint32 index, JSBool compress)
  956. {
  957. uint32 length;
  958. void **vector, *elt;
  959. JSXMLArrayCursor *cursor;
  960. length = array->length;
  961. if (index >= length)
  962. return NULL;
  963. vector = array->vector;
  964. elt = vector[index];
  965. if (compress) {
  966. while (++index < length)
  967. vector[index-1] = vector[index];
  968. array->length = length - 1;
  969. array->capacity = JSXML_CAPACITY(array);
  970. } else {
  971. vector[index] = NULL;
  972. }
  973. for (cursor = array->cursors; cursor; cursor = cursor->next) {
  974. if (cursor->index > index)
  975. --cursor->index;
  976. }
  977. return elt;
  978. }
  979. static void
  980. XMLArrayTruncate(JSContext *cx, JSXMLArray *array, uint32 length)
  981. {
  982. void **vector;
  983. JS_ASSERT(!array->cursors);
  984. if (length >= array->length)
  985. return;
  986. if (length == 0) {
  987. if (array->vector)
  988. free(array->vector);
  989. vector = NULL;
  990. } else {
  991. vector = (void **) realloc(array->vector, length * sizeof(void *));
  992. if (!vector)
  993. return;
  994. }
  995. if (array->length > length)
  996. array->length = length;
  997. array->capacity = length;
  998. array->vector = vector;
  999. }
  1000. #define XMLARRAY_FIND_MEMBER(a,e,f) XMLArrayFindMember(a, (void *)(e), f)
  1001. #define XMLARRAY_HAS_MEMBER(a,e,f) (XMLArrayFindMember(a, (void *)(e), f) != \
  1002. XML_NOT_FOUND)
  1003. #define XMLARRAY_MEMBER(a,i,t) (((i) < (a)->length) \
  1004. ? (t *) (a)->vector[i] \
  1005. : NULL)
  1006. #define XMLARRAY_SET_MEMBER(a,i,e) JS_BEGIN_MACRO \
  1007. if ((a)->length <= (i)) \
  1008. (a)->length = (i) + 1; \
  1009. ((a)->vector[i] = (void *)(e)); \
  1010. JS_END_MACRO
  1011. #define XMLARRAY_ADD_MEMBER(x,a,i,e)XMLArrayAddMember(x, a, i, (void *)(e))
  1012. #define XMLARRAY_INSERT(x,a,i,n) XMLArrayInsert(x, a, i, n)
  1013. #define XMLARRAY_APPEND(x,a,e) XMLARRAY_ADD_MEMBER(x, a, (a)->length, (e))
  1014. #define XMLARRAY_DELETE(x,a,i,c,t) ((t *) XMLArrayDelete(x, a, i, c))
  1015. #define XMLARRAY_TRUNCATE(x,a,n) XMLArrayTruncate(x, a, n)
  1016. /*
  1017. * Define XML setting property strings and constants early, so everyone can
  1018. * use the same names and their magic numbers (tinyids, flags).
  1019. */
  1020. static const char js_ignoreComments_str[] = "ignoreComments";
  1021. static const char js_ignoreProcessingInstructions_str[]
  1022. = "ignoreProcessingInstructions";
  1023. static const char js_ignoreWhitespace_str[] = "ignoreWhitespace";
  1024. static const char js_prettyPrinting_str[] = "prettyPrinting";
  1025. static const char js_prettyIndent_str[] = "prettyIndent";
  1026. /*
  1027. * NB: These XML static property tinyids must
  1028. * (a) not collide with the generic negative tinyids at the top of jsfun.c;
  1029. * (b) index their corresponding xml_static_props array elements.
  1030. * Don't change 'em!
  1031. */
  1032. enum xml_static_tinyid {
  1033. XML_IGNORE_COMMENTS,
  1034. XML_IGNORE_PROCESSING_INSTRUCTIONS,
  1035. XML_IGNORE_WHITESPACE,
  1036. XML_PRETTY_PRINTING,
  1037. XML_PRETTY_INDENT
  1038. };
  1039. static JSBool
  1040. xml_setting_getter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  1041. {
  1042. return JS_TRUE;
  1043. }
  1044. static JSBool
  1045. xml_setting_setter(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  1046. {
  1047. uint8 flag;
  1048. JS_ASSERT(JSVAL_IS_INT(id));
  1049. flag = JS_BIT(JSVAL_TO_INT(id));
  1050. if (js_ValueToBoolean(*vp))
  1051. cx->xmlSettingFlags |= flag;
  1052. else
  1053. cx->xmlSettingFlags &= ~flag;
  1054. return JS_TRUE;
  1055. }
  1056. static JSPropertySpec xml_static_props[] = {
  1057. {js_ignoreComments_str, XML_IGNORE_COMMENTS, JSPROP_PERMANENT,
  1058. xml_setting_getter, xml_setting_setter},
  1059. {js_ignoreProcessingInstructions_str,
  1060. XML_IGNORE_PROCESSING_INSTRUCTIONS, JSPROP_PERMANENT,
  1061. xml_setting_getter, xml_setting_setter},
  1062. {js_ignoreWhitespace_str, XML_IGNORE_WHITESPACE, JSPROP_PERMANENT,
  1063. xml_setting_getter, xml_setting_setter},
  1064. {js_prettyPrinting_str, XML_PRETTY_PRINTING, JSPROP_PERMANENT,
  1065. xml_setting_getter, xml_setting_setter},
  1066. {js_prettyIndent_str, XML_PRETTY_INDENT, JSPROP_PERMANENT,
  1067. xml_setting_getter, NULL},
  1068. {0,0,0,0,0}
  1069. };
  1070. /* Derive cx->xmlSettingFlags bits from xml_static_props tinyids. */
  1071. #define XSF_IGNORE_COMMENTS JS_BIT(XML_IGNORE_COMMENTS)
  1072. #define XSF_IGNORE_PROCESSING_INSTRUCTIONS \
  1073. JS_BIT(XML_IGNORE_PROCESSING_INSTRUCTIONS)
  1074. #define XSF_IGNORE_WHITESPACE JS_BIT(XML_IGNORE_WHITESPACE)
  1075. #define XSF_PRETTY_PRINTING JS_BIT(XML_PRETTY_PRINTING)
  1076. #define XSF_CACHE_VALID JS_BIT(XML_PRETTY_INDENT)
  1077. /*
  1078. * Extra, unrelated but necessarily disjoint flag used by ParseNodeToXML.
  1079. * This flag means a couple of things:
  1080. *
  1081. * - The top JSXML created for a parse tree must have an object owning it.
  1082. *
  1083. * - That the default namespace normally inherited from the temporary
  1084. * <parent xmlns='...'> tag that wraps a runtime-concatenated XML source
  1085. * string must, in the case of a precompiled XML object tree, inherit via
  1086. * ad-hoc code in ParseNodeToXML.
  1087. *
  1088. * Because of the second purpose, we name this flag XSF_PRECOMPILED_ROOT.
  1089. */
  1090. #define XSF_PRECOMPILED_ROOT (XSF_CACHE_VALID << 1)
  1091. /* Macros for special-casing xml:, xmlns= and xmlns:foo= in ParseNodeToQName. */
  1092. #define IS_XML(str) \
  1093. (JSSTRING_LENGTH(str) == 3 && IS_XML_CHARS(JSSTRING_CHARS(str)))
  1094. #define IS_XMLNS(str) \
  1095. (JSSTRING_LENGTH(str) == 5 && IS_XMLNS_CHARS(JSSTRING_CHARS(str)))
  1096. #define IS_XML_CHARS(chars) \
  1097. (JS_TOLOWER((chars)[0]) == 'x' && \
  1098. JS_TOLOWER((chars)[1]) == 'm' && \
  1099. JS_TOLOWER((chars)[2]) == 'l')
  1100. #define HAS_NS_AFTER_XML(chars) \
  1101. (JS_TOLOWER((chars)[3]) == 'n' && \
  1102. JS_TOLOWER((chars)[4]) == 's')
  1103. #define IS_XMLNS_CHARS(chars) \
  1104. (IS_XML_CHARS(chars) && HAS_NS_AFTER_XML(chars))
  1105. #define STARTS_WITH_XML(chars,length) \
  1106. (length >= 3 && IS_XML_CHARS(chars))
  1107. static const char xml_namespace_str[] = "http://www.w3.org/XML/1998/namespace";
  1108. static const char xmlns_namespace_str[] = "http://www.w3.org/2000/xmlns/";
  1109. static JSObject *
  1110. ParseNodeToQName(JSContext *cx, JSParseContext *pc, JSParseNode *pn,
  1111. JSXMLArray *inScopeNSes, JSBool isAttributeName)
  1112. {
  1113. JSString *str, *uri, *prefix, *localName;
  1114. size_t length, offset;
  1115. const jschar *start, *limit, *colon;
  1116. uint32 n;
  1117. JSObject *ns;
  1118. JSString *nsprefix;
  1119. JS_ASSERT(pn->pn_arity == PN_NULLARY);
  1120. str = ATOM_TO_STRING(pn->pn_atom);
  1121. JSSTRING_CHARS_AND_LENGTH(str, start, length);
  1122. JS_ASSERT(length != 0 && *start != '@');
  1123. JS_ASSERT(length != 1 || *start != '*');
  1124. uri = cx->runtime->emptyString;
  1125. limit = start + length;
  1126. colon = js_strchr_limit(start, ':', limit);
  1127. if (colon) {
  1128. offset = PTRDIFF(colon, start, jschar);
  1129. prefix = js_NewDependentString(cx, str, 0, offset);
  1130. if (!prefix)
  1131. return NULL;
  1132. if (STARTS_WITH_XML(start, offset)) {
  1133. if (offset == 3) {
  1134. uri = JS_InternString(cx, xml_namespace_str);
  1135. if (!uri)
  1136. return NULL;
  1137. } else if (offset == 5 && HAS_NS_AFTER_XML(start)) {
  1138. uri = JS_InternString(cx, xmlns_namespace_str);
  1139. if (!uri)
  1140. return NULL;
  1141. } else {
  1142. uri = NULL;
  1143. }
  1144. } else {
  1145. uri = NULL;
  1146. n = inScopeNSes->length;
  1147. while (n != 0) {
  1148. --n;
  1149. ns = XMLARRAY_MEMBER(inScopeNSes, n, JSObject);
  1150. nsprefix = GetPrefix(ns);
  1151. if (nsprefix && js_EqualStrings(nsprefix, prefix)) {
  1152. uri = GetURI(ns);
  1153. break;
  1154. }
  1155. }
  1156. }
  1157. if (!uri) {
  1158. js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn,
  1159. JSREPORT_ERROR,
  1160. JSMSG_BAD_XML_NAMESPACE,
  1161. js_ValueToPrintableString(cx,
  1162. STRING_TO_JSVAL(prefix)));
  1163. return NULL;
  1164. }
  1165. localName = js_NewStringCopyN(cx, colon + 1, length - (offset + 1));
  1166. if (!localName)
  1167. return NULL;
  1168. } else {
  1169. if (isAttributeName) {
  1170. /*
  1171. * An unprefixed attribute is not in any namespace, so set prefix
  1172. * as well as uri to the empty string.
  1173. */
  1174. prefix = uri;
  1175. } else {
  1176. /*
  1177. * Loop from back to front looking for the closest declared default
  1178. * namespace.
  1179. */
  1180. n = inScopeNSes->length;
  1181. while (n != 0) {
  1182. --n;
  1183. ns = XMLARRAY_MEMBER(inScopeNSes, n, JSObject);
  1184. nsprefix = GetPrefix(ns);
  1185. if (!nsprefix || IS_EMPTY(nsprefix)) {
  1186. uri = GetURI(ns);
  1187. break;
  1188. }
  1189. }
  1190. prefix = IS_EMPTY(uri) ? cx->runtime->emptyString : NULL;
  1191. }
  1192. localName = str;
  1193. }
  1194. return NewXMLQName(cx, uri, prefix, localName);
  1195. }
  1196. static JSString *
  1197. ChompXMLWhitespace(JSContext *cx, JSString *str)
  1198. {
  1199. size_t length, newlength, offset;
  1200. const jschar *cp, *start, *end;
  1201. jschar c;
  1202. JSSTRING_CHARS_AND_LENGTH(str, start, length);
  1203. for (cp = start, end = cp + length; cp < end; cp++) {
  1204. c = *cp;
  1205. if (!JS_ISXMLSPACE(c))
  1206. break;
  1207. }
  1208. while (end > cp) {
  1209. c = end[-1];
  1210. if (!JS_ISXMLSPACE(c))
  1211. break;
  1212. --end;
  1213. }
  1214. newlength = PTRDIFF(end, cp, jschar);
  1215. if (newlength == length)
  1216. return str;
  1217. offset = PTRDIFF(cp, start, jschar);
  1218. return js_NewDependentString(cx, str, offset, newlength);
  1219. }
  1220. static JSXML *
  1221. ParseNodeToXML(JSContext *cx, JSParseContext *pc, JSParseNode *pn,
  1222. JSXMLArray *inScopeNSes, uintN flags)
  1223. {
  1224. JSXML *xml, *kid, *attr, *attrj;
  1225. JSString *str;
  1226. uint32 length, n, i, j;
  1227. JSParseNode *pn2, *pn3, *head, **pnp;
  1228. JSObject *ns;
  1229. JSObject *qn, *attrjqn;
  1230. JSXMLClass xml_class;
  1231. int stackDummy;
  1232. if (!JS_CHECK_STACK_SIZE(cx, stackDummy)) {
  1233. js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn, JSREPORT_ERROR,
  1234. JSMSG_OVER_RECURSED);
  1235. return NULL;
  1236. }
  1237. #define PN2X_SKIP_CHILD ((JSXML *) 1)
  1238. /*
  1239. * Cases return early to avoid common code that gets an outermost xml's
  1240. * object, which protects GC-things owned by xml and its descendants from
  1241. * garbage collection.
  1242. */
  1243. xml = NULL;
  1244. if (!js_EnterLocalRootScope(cx))
  1245. return NULL;
  1246. switch (pn->pn_type) {
  1247. case TOK_XMLELEM:
  1248. length = inScopeNSes->length;
  1249. pn2 = pn->pn_head;
  1250. xml = ParseNodeToXML(cx, pc, pn2, inScopeNSes, flags);
  1251. if (!xml)
  1252. goto fail;
  1253. flags &= ~XSF_PRECOMPILED_ROOT;
  1254. n = pn->pn_count;
  1255. JS_ASSERT(n >= 2);
  1256. n -= 2;
  1257. if (!XMLArraySetCapacity(cx, &xml->xml_kids, n))
  1258. goto fail;
  1259. i = 0;
  1260. while ((pn2 = pn2->pn_next) != NULL) {
  1261. if (!pn2->pn_next) {
  1262. /* Don't append the end tag! */
  1263. JS_ASSERT(pn2->pn_type == TOK_XMLETAGO);
  1264. break;
  1265. }
  1266. if ((flags & XSF_IGNORE_WHITESPACE) &&
  1267. n > 1 && pn2->pn_type == TOK_XMLSPACE) {
  1268. --n;
  1269. continue;
  1270. }
  1271. kid = ParseNodeToXML(cx, pc, pn2, inScopeNSes, flags);
  1272. if (kid == PN2X_SKIP_CHILD) {
  1273. --n;
  1274. continue;
  1275. }
  1276. if (!kid)
  1277. goto fail;
  1278. /* Store kid in xml right away, to protect it from GC. */
  1279. XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid);
  1280. kid->parent = xml;
  1281. ++i;
  1282. /* XXX where is this documented in an XML spec, or in E4X? */
  1283. if ((flags & XSF_IGNORE_WHITESPACE) &&
  1284. n > 1 && kid->xml_class == JSXML_CLASS_TEXT) {
  1285. str = ChompXMLWhitespace(cx, kid->xml_value);
  1286. if (!str)
  1287. goto fail;
  1288. kid->xml_value = str;
  1289. }
  1290. }
  1291. JS_ASSERT(i == n);
  1292. if (n < pn->pn_count - 2)
  1293. XMLArrayTrim(&xml->xml_kids);
  1294. XMLARRAY_TRUNCATE(cx, inScopeNSes, length);
  1295. break;
  1296. case TOK_XMLLIST:
  1297. xml = js_NewXML(cx, JSXML_CLASS_LIST);
  1298. if (!xml)
  1299. goto fail;
  1300. n = pn->pn_count;
  1301. if (!XMLArraySetCapacity(cx, &xml->xml_kids, n))
  1302. goto fail;
  1303. i = 0;
  1304. for (pn2 = pn->pn_head; pn2; pn2 = pn2->pn_next) {
  1305. /*
  1306. * Always ignore insignificant whitespace in lists -- we shouldn't
  1307. * condition this on an XML.ignoreWhitespace setting when the list
  1308. * constructor is XMLList (note XML/XMLList unification hazard).
  1309. */
  1310. if (pn2->pn_type == TOK_XMLSPACE) {
  1311. --n;
  1312. continue;
  1313. }
  1314. kid = ParseNodeToXML(cx, pc, pn2, inScopeNSes, flags);
  1315. if (kid == PN2X_SKIP_CHILD) {
  1316. --n;
  1317. continue;
  1318. }
  1319. if (!kid)
  1320. goto fail;
  1321. XMLARRAY_SET_MEMBER(&xml->xml_kids, i, kid);
  1322. ++i;
  1323. }
  1324. if (n < pn->pn_count)
  1325. XMLArrayTrim(&xml->xml_kids);
  1326. break;
  1327. case TOK_XMLSTAGO:
  1328. case TOK_XMLPTAGC:
  1329. length = inScopeNSes->length;
  1330. pn2 = pn->pn_head;
  1331. JS_ASSERT(pn2->pn_type == TOK_XMLNAME);
  1332. if (pn2->pn_arity == PN_LIST)
  1333. goto syntax;
  1334. xml = js_NewXML(cx, JSXML_CLASS_ELEMENT);
  1335. if (!xml)
  1336. goto fail;
  1337. /* First pass: check syntax and process namespace declarations. */
  1338. JS_ASSERT(pn->pn_count >= 1);
  1339. n = pn->pn_count - 1;
  1340. pnp = &pn2->pn_next;
  1341. head = *pnp;
  1342. while ((pn2 = *pnp) != NULL) {
  1343. size_t length;
  1344. const jschar *chars;
  1345. if (pn2->pn_type != TOK_XMLNAME || pn2->pn_arity != PN_NULLARY)
  1346. goto syntax;
  1347. /* Enforce "Well-formedness constraint: Unique Att Spec". */
  1348. for (pn3 = head; pn3 != pn2; pn3 = pn3->pn_next->pn_next) {
  1349. if (pn3->pn_atom == pn2->pn_atom) {
  1350. js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn2,
  1351. JSREPORT_ERROR,
  1352. JSMSG_DUPLICATE_XML_ATTR,
  1353. js_ValueToPrintableString(cx,
  1354. ATOM_KEY(pn2->pn_atom)));
  1355. goto fail;
  1356. }
  1357. }
  1358. str = ATOM_TO_STRING(pn2->pn_atom);
  1359. pn2 = pn2->pn_next;
  1360. JS_ASSERT(pn2);
  1361. if (pn2->pn_type != TOK_XMLATTR)
  1362. goto syntax;
  1363. JSSTRING_CHARS_AND_LENGTH(str, chars, length);
  1364. if (length >= 5 &&
  1365. IS_XMLNS_CHARS(chars) &&
  1366. (length == 5 || chars[5] == ':')) {
  1367. JSString *uri, *prefix;
  1368. uri = ATOM_TO_STRING(pn2->pn_atom);
  1369. if (length == 5) {
  1370. /* 10.3.2.1. Step 6(h)(i)(1)(a). */
  1371. prefix = cx->runtime->emptyString;
  1372. } else {
  1373. prefix = js_NewStringCopyN(cx, chars + 6, length - 6);
  1374. if (!prefix)
  1375. goto fail;
  1376. }
  1377. /*
  1378. * Once the new ns is appended to xml->xml_namespaces, it is
  1379. * protected from GC by the object that owns xml -- which is
  1380. * either xml->object if outermost, or the object owning xml's
  1381. * oldest ancestor if !outermost.
  1382. */
  1383. ns = NewXMLNamespace(cx, prefix, uri, JS_TRUE);
  1384. if (!ns)
  1385. goto fail;
  1386. /*
  1387. * Don't add a namespace that's already in scope. If someone
  1388. * extracts a child property from its parent via [[Get]], then
  1389. * we enforce the invariant, noted many times in ECMA-357, that
  1390. * the child's namespaces form a possibly-improper superset of
  1391. * its ancestors' namespaces.
  1392. */
  1393. if (!XMLARRAY_HAS_MEMBER(inScopeNSes, ns, namespace_identity)) {
  1394. if (!XMLARRAY_APPEND(cx, inScopeNSes, ns) ||
  1395. !XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns)) {
  1396. goto fail;
  1397. }
  1398. }
  1399. JS_ASSERT(n >= 2);
  1400. n -= 2;
  1401. *pnp = pn2->pn_next;
  1402. /* XXXbe recycle pn2 */
  1403. continue;
  1404. }
  1405. pnp = &pn2->pn_next;
  1406. }
  1407. /*
  1408. * If called from js_ParseNodeToXMLObject, emulate the effect of the
  1409. * <parent xmlns='%s'>...</parent> wrapping done by "ToXML Applied to
  1410. * the String Type" (ECMA-357 10.3.1).
  1411. */
  1412. if (flags & XSF_PRECOMPILED_ROOT) {
  1413. JS_ASSERT(length >= 1);
  1414. ns = XMLARRAY_MEMBER(inScopeNSes, 0, JSObject);
  1415. JS_ASSERT(!XMLARRAY_HAS_MEMBER(&xml->xml_namespaces, ns,
  1416. namespace_identity));
  1417. ns = NewXMLNamespace(cx, GetPrefix(ns), GetURI(ns), JS_FALSE);
  1418. if (!ns)
  1419. goto fail;
  1420. if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
  1421. goto fail;
  1422. }
  1423. XMLArrayTrim(&xml->xml_namespaces);
  1424. /* Second pass: process tag name and attributes, using namespaces. */
  1425. pn2 = pn->pn_head;
  1426. qn = ParseNodeToQName(cx, pc, pn2, inScopeNSes, JS_FALSE);
  1427. if (!qn)
  1428. goto fail;
  1429. xml->name = qn;
  1430. JS_ASSERT((n & 1) == 0);
  1431. n >>= 1;
  1432. if (!XMLArraySetCapacity(cx, &xml->xml_attrs, n))
  1433. goto fail;
  1434. for (i = 0; (pn2 = pn2->pn_next) != NULL; i++) {
  1435. qn = ParseNodeToQName(cx, pc, pn2, inScopeNSes, JS_TRUE);
  1436. if (!qn) {
  1437. xml->xml_attrs.length = i;
  1438. goto fail;
  1439. }
  1440. /*
  1441. * Enforce "Well-formedness constraint: Unique Att Spec", part 2:
  1442. * this time checking local name and namespace URI.
  1443. */
  1444. for (j = 0; j < i; j++) {
  1445. attrj = XMLARRAY_MEMBER(&xml->xml_attrs, j, JSXML);
  1446. attrjqn = attrj->name;
  1447. if (js_EqualStrings(GetURI(attrjqn), GetURI(qn)) &&
  1448. js_EqualStrings(GetLocalName(attrjqn), GetLocalName(qn))) {
  1449. js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn2,
  1450. JSREPORT_ERROR,
  1451. JSMSG_DUPLICATE_XML_ATTR,
  1452. js_ValueToPrintableString(cx,
  1453. ATOM_KEY(pn2->pn_atom)));
  1454. goto fail;
  1455. }
  1456. }
  1457. pn2 = pn2->pn_next;
  1458. JS_ASSERT(pn2);
  1459. JS_ASSERT(pn2->pn_type == TOK_XMLATTR);
  1460. attr = js_NewXML(cx, JSXML_CLASS_ATTRIBUTE);
  1461. if (!attr)
  1462. goto fail;
  1463. XMLARRAY_SET_MEMBER(&xml->xml_attrs, i, attr);
  1464. attr->parent = xml;
  1465. attr->name = qn;
  1466. attr->xml_value = ATOM_TO_STRING(pn2->pn_atom);
  1467. }
  1468. /* Point tag closes its own namespace scope. */
  1469. if (pn->pn_type == TOK_XMLPTAGC)
  1470. XMLARRAY_TRUNCATE(cx, inScopeNSes, length);
  1471. break;
  1472. case TOK_XMLSPACE:
  1473. case TOK_XMLTEXT:
  1474. case TOK_XMLCDATA:
  1475. case TOK_XMLCOMMENT:
  1476. case TOK_XMLPI:
  1477. str = ATOM_TO_STRING(pn->pn_atom);
  1478. qn = NULL;
  1479. if (pn->pn_type == TOK_XMLCOMMENT) {
  1480. if (flags & XSF_IGNORE_COMMENTS)
  1481. goto skip_child;
  1482. xml_class = JSXML_CLASS_COMMENT;
  1483. } else if (pn->pn_type == TOK_XMLPI) {
  1484. if (IS_XML(str)) {
  1485. js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn,
  1486. JSREPORT_ERROR,
  1487. JSMSG_RESERVED_ID,
  1488. js_ValueToPrintableString(cx,
  1489. STRING_TO_JSVAL(str)));
  1490. goto fail;
  1491. }
  1492. if (flags & XSF_IGNORE_PROCESSING_INSTRUCTIONS)
  1493. goto skip_child;
  1494. qn = ParseNodeToQName(cx, pc, pn, inScopeNSes, JS_FALSE);
  1495. if (!qn)
  1496. goto fail;
  1497. str = pn->pn_atom2
  1498. ? ATOM_TO_STRING(pn->pn_atom2)
  1499. : cx->runtime->emptyString;
  1500. xml_class = JSXML_CLASS_PROCESSING_INSTRUCTION;
  1501. } else {
  1502. /* CDATA section content, or element text. */
  1503. xml_class = JSXML_CLASS_TEXT;
  1504. }
  1505. xml = js_NewXML(cx, xml_class);
  1506. if (!xml)
  1507. goto fail;
  1508. xml->name = qn;
  1509. if (pn->pn_type == TOK_XMLSPACE)
  1510. xml->xml_flags |= XMLF_WHITESPACE_TEXT;
  1511. xml->xml_value = str;
  1512. break;
  1513. default:
  1514. goto syntax;
  1515. }
  1516. js_LeaveLocalRootScopeWithResult(cx, (jsval) xml);
  1517. if ((flags & XSF_PRECOMPILED_ROOT) && !js_GetXMLObject(cx, xml))
  1518. return NULL;
  1519. return xml;
  1520. skip_child:
  1521. js_LeaveLocalRootScope(cx);
  1522. return PN2X_SKIP_CHILD;
  1523. #undef PN2X_SKIP_CHILD
  1524. syntax:
  1525. js_ReportCompileErrorNumber(cx, &pc->tokenStream, pn, JSREPORT_ERROR,
  1526. JSMSG_BAD_XML_MARKUP);
  1527. fail:
  1528. js_LeaveLocalRootScope(cx);
  1529. return NULL;
  1530. }
  1531. /*
  1532. * XML helper, object-ops, and library functions. We start with the helpers,
  1533. * in ECMA-357 order, but merging XML (9.1) and XMLList (9.2) helpers.
  1534. */
  1535. static JSBool
  1536. GetXMLSetting(JSContext *cx, const char *name, jsval *vp)
  1537. {
  1538. jsval v;
  1539. if (!js_FindClassObject(cx, NULL, INT_TO_JSID(JSProto_XML), &v))
  1540. return JS_FALSE;
  1541. if (!VALUE_IS_FUNCTION(cx, v)) {
  1542. *vp = JSVAL_VOID;
  1543. return JS_TRUE;
  1544. }
  1545. return JS_GetProperty(cx, JSVAL_TO_OBJECT(v), name, vp);
  1546. }
  1547. static JSBool
  1548. FillSettingsCache(JSContext *cx)
  1549. {
  1550. int i;
  1551. const char *name;
  1552. jsval v;
  1553. /* Note: XML_PRETTY_INDENT is not a boolean setting. */
  1554. for (i = XML_IGNORE_COMMENTS; i < XML_PRETTY_INDENT; i++) {
  1555. name = xml_static_props[i].name;
  1556. if (!GetXMLSetting(cx, name, &v))
  1557. return JS_FALSE;
  1558. if (js_ValueToBoolean(v))
  1559. cx->xmlSettingFlags |= JS_BIT(i);
  1560. else
  1561. cx->xmlSettingFlags &= ~JS_BIT(i);
  1562. }
  1563. cx->xmlSettingFlags |= XSF_CACHE_VALID;
  1564. return JS_TRUE;
  1565. }
  1566. static JSBool
  1567. GetBooleanXMLSetting(JSContext *cx, const char *name, JSBool *bp)
  1568. {
  1569. int i;
  1570. if (!(cx->xmlSettingFlags & XSF_CACHE_VALID) && !FillSettingsCache(cx))
  1571. return JS_FALSE;
  1572. for (i = 0; xml_static_props[i].name; i++) {
  1573. if (!strcmp(xml_static_props[i].name, name)) {
  1574. *bp = (cx->xmlSettingFlags & JS_BIT(i)) != 0;
  1575. return JS_TRUE;
  1576. }
  1577. }
  1578. *bp = JS_FALSE;
  1579. return JS_TRUE;
  1580. }
  1581. static JSBool
  1582. GetUint32XMLSetting(JSContext *cx, const char *name, uint32 *uip)
  1583. {
  1584. jsval v;
  1585. return GetXMLSetting(cx, name, &v) && JS_ValueToECMAUint32(cx, v, uip);
  1586. }
  1587. static JSBool
  1588. GetXMLSettingFlags(JSContext *cx, uintN *flagsp)
  1589. {
  1590. JSBool flag;
  1591. /* Just get the first flag to validate the setting flags cache. */
  1592. if (!GetBooleanXMLSetting(cx, js_ignoreComments_str, &flag))
  1593. return JS_FALSE;
  1594. *flagsp = cx->xmlSettingFlags;
  1595. return JS_TRUE;
  1596. }
  1597. static JSXML *
  1598. ParseXMLSource(JSContext *cx, JSString *src)
  1599. {
  1600. jsval nsval;
  1601. JSString *uri;
  1602. size_t urilen, srclen, length, offset, dstlen;
  1603. jschar *chars;
  1604. const jschar *srcp, *endp;
  1605. JSXML *xml;
  1606. JSParseContext pc;
  1607. const char *filename;
  1608. uintN lineno;
  1609. JSStackFrame *fp;
  1610. JSOp op;
  1611. JSParseNode *pn;
  1612. JSXMLArray nsarray;
  1613. uintN flags;
  1614. static const char prefix[] = "<parent xmlns=\"";
  1615. static const char middle[] = "\">";
  1616. static const char suffix[] = "</parent>";
  1617. #define constrlen(constr) (sizeof(constr) - 1)
  1618. if (!js_GetDefaultXMLNamespace(cx, &nsval))
  1619. return NULL;
  1620. uri = GetURI(JSVAL_TO_OBJECT(nsval));
  1621. uri = js_EscapeAttributeValue(cx, uri, JS_FALSE);
  1622. urilen = JSSTRING_LENGTH(uri);
  1623. srclen = JSSTRING_LENGTH(src);
  1624. length = constrlen(prefix) + urilen + constrlen(middle) + srclen +
  1625. constrlen(suffix);
  1626. chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
  1627. if (!chars)
  1628. return NULL;
  1629. dstlen = length;
  1630. js_InflateStringToBuffer(cx, prefix, constrlen(prefix), chars, &dstlen);
  1631. offset = dstlen;
  1632. js_strncpy(chars + offset, JSSTRING_CHARS(uri), urilen);
  1633. offset += urilen;
  1634. dstlen = length - offset + 1;
  1635. js_InflateStringToBuffer(cx, middle, constrlen(middle), chars + offset,
  1636. &dstlen);
  1637. offset += dstlen;
  1638. srcp = JSSTRING_CHARS(src);
  1639. js_strncpy(chars + offset, srcp, srclen);
  1640. offset += srclen;
  1641. dstlen = length - offset + 1;
  1642. js_InflateStringToBuffer(cx, suffix, constrlen(suffix), chars + offset,
  1643. &dstlen);
  1644. chars [offset + dstlen] = 0;
  1645. xml = NULL;
  1646. for (fp = cx->fp; fp && !fp->regs; fp = fp->down)
  1647. JS_ASSERT(!fp->script);
  1648. filename = NULL;
  1649. lineno = 1;
  1650. if (fp) {
  1651. op = (JSOp) *fp->regs->pc;
  1652. if (op == JSOP_TOXML || op == JSOP_TOXMLLIST) {
  1653. filename = fp->script->filename;
  1654. lineno = js_FramePCToLineNumber(cx, fp);
  1655. for (endp = srcp + srclen; srcp < endp; srcp++) {
  1656. if (*srcp == '\n')
  1657. --lineno;
  1658. }
  1659. }
  1660. }
  1661. if (!js_InitParseContext(cx, &pc, NULL, NULL, chars, length, NULL,
  1662. filename, lineno))
  1663. goto out;
  1664. pn = js_ParseXMLText(cx, cx->fp->scopeChain, &pc, JS_FALSE);
  1665. if (pn && XMLArrayInit(cx, &nsarray, 1)) {
  1666. if (GetXMLSettingFlags(cx, &flags))
  1667. xml = ParseNodeToXML(cx, &pc, pn, &nsarray, flags);
  1668. XMLArrayFinish(cx, &nsarray);
  1669. }
  1670. js_FinishParseContext(cx, &pc);
  1671. out:
  1672. JS_free(cx, chars);
  1673. return xml;
  1674. #undef constrlen
  1675. }
  1676. /*
  1677. * Errata in 10.3.1, 10.4.1, and 13.4.4.24 (at least).
  1678. *
  1679. * 10.3.1 Step 6(a) fails to NOTE that implementations that do not enforce
  1680. * the constraint:
  1681. *
  1682. * for all x belonging to XML:
  1683. * x.[[InScopeNamespaces]] >= x.[[Parent]].[[InScopeNamespaces]]
  1684. *
  1685. * must union x.[[InScopeNamespaces]] into x[0].[[InScopeNamespaces]] here
  1686. * (in new sub-step 6(a), renumbering the others to (b) and (c)).
  1687. *
  1688. * Same goes for 10.4.1 Step 7(a).
  1689. *
  1690. * In order for XML.prototype.namespaceDeclarations() to work correctly, the
  1691. * default namespace thereby unioned into x[0].[[InScopeNamespaces]] must be
  1692. * flagged as not declared, so that 13.4.4.24 Step 8(a) can exclude all such
  1693. * undeclared namespaces associated with x not belonging to ancestorNS.
  1694. */
  1695. static JSXML *
  1696. OrphanXMLChild(JSContext *cx, JSXML *xml, uint32 i)
  1697. {
  1698. JSObject *ns;
  1699. ns = XMLARRAY_MEMBER(&xml->xml_namespaces, 0, JSObject);
  1700. xml = XMLARRAY_MEMBER(&xml->xml_kids, i, JSXML);
  1701. if (!ns || !xml)
  1702. return xml;
  1703. if (xml->xml_class == JSXML_CLASS_ELEMENT) {
  1704. if (!XMLARRAY_APPEND(cx, &xml->xml_namespaces, ns))
  1705. return NULL;
  1706. ns->fslots[JSSLOT_DECLARED] = JSVAL_VOID;
  1707. }
  1708. xml->parent = NULL;
  1709. return xml;
  1710. }
  1711. static JSObject *
  1712. ToXML(JSContext *cx, jsval v)
  1713. {
  1714. JSObject *obj;
  1715. JSXML *xml;
  1716. JSClass *clasp;
  1717. JSString *str;
  1718. uint32 length;
  1719. if (JSVAL_IS_PRIMITIVE(v)) {
  1720. if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
  1721. goto bad;
  1722. } else {
  1723. obj = JSVAL_TO_OBJECT(v);
  1724. if (OBJECT_IS_XML(cx, obj)) {
  1725. xml = (JSXML *) JS_GetPrivate(cx, obj);
  1726. if (xml->xml_class == JSXML_CLASS_LIST) {
  1727. if (xml->xml_kids.length != 1)
  1728. goto bad;
  1729. xml = XMLARRAY_MEMBER(&xml->xml_kids, 0, JSXML);
  1730. if (xml) {
  1731. JS_ASSERT(xml->xml_class != JSXML_CLASS_LIST);
  1732. return js_GetXMLObject(cx, xml);
  1733. }
  1734. }
  1735. return obj;
  1736. }
  1737. clasp = OBJ_GET_CLASS(cx, obj);
  1738. if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) {
  1739. JS_ASSERT(0);
  1740. }
  1741. if (clasp != &js_StringClass &&
  1742. clasp != &js_NumberClass &&
  1743. clasp != &js_BooleanClass) {
  1744. goto bad;
  1745. }
  1746. }
  1747. str = js_ValueToString(cx, v);
  1748. if (!str)
  1749. return NULL;
  1750. if (IS_EMPTY(str)) {
  1751. length = 0;
  1752. #ifdef __GNUC__ /* suppress bogus gcc warnings */
  1753. xml = NULL;
  1754. #endif
  1755. } else {
  1756. xml = ParseXMLSource(cx, str);
  1757. if (!xml)
  1758. return NULL;
  1759. length = JSXML_LENGTH(xml);
  1760. }
  1761. if (length == 0) {
  1762. obj = js_NewXMLObject(cx, JSXML_CLASS_TEXT);
  1763. if (!obj)
  1764. return NULL;
  1765. } else if (length == 1) {
  1766. xml = OrphanXMLChild(cx, xml, 0);
  1767. if (!xml)
  1768. return NULL;
  1769. obj = js_GetXMLObject(cx, xml);
  1770. if (!obj)
  1771. return NULL;
  1772. } else {
  1773. JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_SYNTAX_ERROR);
  1774. return NULL;
  1775. }
  1776. return obj;
  1777. bad:
  1778. js_ReportValueError(cx, JSMSG_BAD_XML_CONVERSION,
  1779. JSDVG_IGNORE_STACK, v, NULL);
  1780. return NULL;
  1781. }
  1782. static JSBool
  1783. Append(JSContext *cx, JSXML *list, JSXML *kid);
  1784. static JSObject *
  1785. ToXMLList(JSContext *cx, jsval v)
  1786. {
  1787. JSObject *obj, *listobj;
  1788. JSXML *xml, *list, *kid;
  1789. JSClass *clasp;
  1790. JSString *str;
  1791. uint32 i, length;
  1792. if (JSVAL_IS_PRIMITIVE(v)) {
  1793. if (JSVAL_IS_NULL(v) || JSVAL_IS_VOID(v))
  1794. goto bad;
  1795. } else {
  1796. obj = JSVAL_TO_OBJECT(v);
  1797. if (OBJECT_IS_XML(cx, obj)) {
  1798. xml = (JSXML *) JS_GetPrivate(cx, obj);
  1799. if (xml->xml_class != JSXML_CLASS_LIST) {
  1800. listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
  1801. if (!listobj)
  1802. return NULL;
  1803. list = (JSXML *) JS_GetPrivate(cx, listobj);
  1804. if (!Append(cx, list, xml))
  1805. return NULL;
  1806. return listobj;
  1807. }
  1808. return obj;
  1809. }
  1810. clasp = OBJ_GET_CLASS(cx, obj);
  1811. if (clasp->flags & JSCLASS_DOCUMENT_OBSERVER) {
  1812. JS_ASSERT(0);
  1813. }
  1814. if (clasp != &js_StringClass &&
  1815. clasp != &js_NumberClass &&
  1816. clasp != &js_BooleanClass) {
  1817. goto bad;
  1818. }
  1819. }
  1820. str = js_ValueToString(cx, v);
  1821. if (!str)
  1822. return NULL;
  1823. if (IS_EMPTY(str)) {
  1824. xml = NULL;
  1825. length = 0;
  1826. } else {
  1827. if (!js_EnterLocalRootScope(cx))
  1828. return NULL;
  1829. xml = ParseXMLSource(cx, str);
  1830. if (!xml) {
  1831. js_LeaveLocalRootScope(cx);
  1832. return NULL;
  1833. }
  1834. length = JSXML_LENGTH(xml);
  1835. }
  1836. listobj = js_NewXMLObject(cx, JSXML_CLASS_LIST);
  1837. if (listobj) {
  1838. list = (JSXML *) JS_GetPrivate(cx, listobj);
  1839. for (i = 0; i < length; i++) {
  1840. kid = OrphanXMLChild(cx, xml, i);
  1841. if (!kid || !Append(cx, list, kid)) {
  1842. listobj = NULL;
  1843. break;
  1844. }
  1845. }
  1846. }
  1847. if (xml)
  1848. js_LeaveLocalRootScopeWithResult(cx, (jsval) listobj);
  1849. return listobj;
  1850. bad:
  1851. js_ReportValueError(cx, JSMSG_BAD_XMLLIST_CONVERSION,
  1852. JSDVG_IGNORE_STACK, v, NULL);
  1853. return NULL;
  1854. }
  1855. /*
  1856. * ECMA-357 10.2.1 Steps 5-7 pulled out as common subroutines of XMLToXMLString
  1857. * and their library-public js_* counterparts. The guts of MakeXMLCDataString,
  1858. * MakeXMLCommentString, and MakeXMLPIString are further factored into a common
  1859. * MakeXMLSpecialString subroutine.
  1860. *
  1861. * These functions take ownership of sb->base, if sb is non-null, in all cases
  1862. * of success or failure.
  1863. */
  1864. static JSString *
  1865. MakeXMLSpecialString(JSContext *cx, JSStringBuffer *sb,
  1866. JSString *str, JSString *str2,
  1867. const jschar *prefix, size_t prefixlength,
  1868. const jschar *suffix, size_t suffixlength)
  1869. {
  1870. JSStringBuffer localSB;
  1871. size_t length, length2, newlength;
  1872. jschar *bp, *base;
  1873. if (!sb) {
  1874. sb = &localSB;
  1875. js_InitStringBuffer(sb);
  1876. }
  1877. length = JSSTRING_LENGTH(str);
  1878. length2 = str2 ? JSSTRING_LENGTH(str2) : 0;
  1879. newlength = STRING_BUFFER_OFFSET(sb) +
  1880. prefixlength + length + ((length2 != 0) ? 1 + length2 : 0) +
  1881. suffixlength;
  1882. bp = base = (jschar *)
  1883. JS_r