PageRenderTime 61ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

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

http://github.com/onedayitwillmake/RealtimeMultiplayerNodeJs
C++ | 2220 lines | 1751 code | 236 blank | 233 comment | 405 complexity | c3cf6b74a7ab61e8451b13f010618873 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, MPL-2.0-no-copyleft-exception, BSD-3-Clause

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

  1. /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2. * vim: set ts=8 sw=4 et tw=99:
  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 Mozilla Communicator client code, released
  18. * March 31, 1998.
  19. *
  20. * The Initial Developer of the Original Code is
  21. * Netscape Communications Corporation.
  22. * Portions created by the Initial Developer are Copyright (C) 1998
  23. * the Initial Developer. All Rights Reserved.
  24. *
  25. * Contributor(s):
  26. *
  27. * Alternatively, the contents of this file may be used under the terms of
  28. * either of the GNU General Public License Version 2 or later (the "GPL"),
  29. * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  30. * in which case the provisions of the GPL or the LGPL are applicable instead
  31. * of those above. If you wish to allow use of your version of this file only
  32. * under the terms of either the GPL or the LGPL, and not to allow others to
  33. * use your version of this file under the terms of the MPL, indicate your
  34. * decision by deleting the provisions above and replace them with the notice
  35. * and other provisions required by the GPL or the LGPL. If you do not delete
  36. * the provisions above, a recipient may use your version of this file under
  37. * the terms of any one of the MPL, the GPL or the LGPL.
  38. *
  39. * ***** END LICENSE BLOCK ***** */
  40. /*
  41. * JS string type implementation.
  42. *
  43. * In order to avoid unnecessary js_LockGCThing/js_UnlockGCThing calls, these
  44. * native methods store strings (possibly newborn) converted from their 'this'
  45. * parameter and arguments on the stack: 'this' conversions at argv[-1], arg
  46. * conversions at their index (argv[0], argv[1]). This is a legitimate method
  47. * of rooting things that might lose their newborn root due to subsequent GC
  48. * allocations in the same native method.
  49. */
  50. #include "jsstddef.h"
  51. #include <stdlib.h>
  52. #include <string.h>
  53. #include "jstypes.h"
  54. #include "jsutil.h" /* Added by JSIFY */
  55. #include "jshash.h" /* Added by JSIFY */
  56. #include "jsprf.h"
  57. #include "jsapi.h"
  58. #include "jsarray.h"
  59. #include "jsatom.h"
  60. #include "jsbool.h"
  61. #include "jsbuiltins.h"
  62. #include "jscntxt.h"
  63. #include "jsversion.h"
  64. #include "jsgc.h"
  65. #include "jsinterp.h"
  66. #include "jslock.h"
  67. #include "jsnum.h"
  68. #include "jsobj.h"
  69. #include "jsopcode.h"
  70. #include "jsregexp.h"
  71. #include "jsscope.h"
  72. #include "jsstr.h"
  73. #include "jsbit.h"
  74. #define JSSTRDEP_RECURSION_LIMIT 100
  75. size_t
  76. js_MinimizeDependentStrings(JSString *str, int level, JSString **basep)
  77. {
  78. JSString *base;
  79. size_t start, length;
  80. JS_ASSERT(JSSTRING_IS_DEPENDENT(str));
  81. base = JSSTRDEP_BASE(str);
  82. start = JSSTRDEP_START(str);
  83. if (JSSTRING_IS_DEPENDENT(base)) {
  84. if (level < JSSTRDEP_RECURSION_LIMIT) {
  85. start += js_MinimizeDependentStrings(base, level + 1, &base);
  86. } else {
  87. do {
  88. start += JSSTRDEP_START(base);
  89. base = JSSTRDEP_BASE(base);
  90. } while (JSSTRING_IS_DEPENDENT(base));
  91. }
  92. if (start == 0) {
  93. JS_ASSERT(JSSTRDEP_IS_PREFIX(str));
  94. JSPREFIX_SET_BASE(str, base);
  95. } else if (start <= JSSTRDEP_START_MASK) {
  96. length = JSSTRDEP_LENGTH(str);
  97. JSSTRDEP_INIT(str, base, start, length);
  98. }
  99. }
  100. *basep = base;
  101. return start;
  102. }
  103. jschar *
  104. js_GetDependentStringChars(JSString *str)
  105. {
  106. size_t start;
  107. JSString *base;
  108. start = js_MinimizeDependentStrings(str, 0, &base);
  109. JS_ASSERT(start < JSFLATSTR_LENGTH(base));
  110. return JSFLATSTR_CHARS(base) + start;
  111. }
  112. const jschar *
  113. js_GetStringChars(JSContext *cx, JSString *str)
  114. {
  115. if (!js_MakeStringImmutable(cx, str))
  116. return NULL;
  117. return JSFLATSTR_CHARS(str);
  118. }
  119. JSString * JS_FASTCALL
  120. js_ConcatStrings(JSContext *cx, JSString *left, JSString *right)
  121. {
  122. size_t rn, ln, lrdist, n;
  123. jschar *rs, *ls, *s;
  124. JSString *ldep; /* non-null if left should become dependent */
  125. JSString *str;
  126. JSSTRING_CHARS_AND_LENGTH(right, rs, rn);
  127. if (rn == 0)
  128. return left;
  129. JSSTRING_CHARS_AND_LENGTH(left, ls, ln);
  130. if (ln == 0)
  131. return right;
  132. if (!JSSTRING_IS_MUTABLE(left)) {
  133. /* We must copy if left does not own a buffer to realloc. */
  134. s = (jschar *) JS_malloc(cx, (ln + rn + 1) * sizeof(jschar));
  135. if (!s)
  136. return NULL;
  137. js_strncpy(s, ls, ln);
  138. ldep = NULL;
  139. } else {
  140. /* We can realloc left's space and make it depend on our result. */
  141. JS_ASSERT(JSSTRING_IS_FLAT(left));
  142. s = (jschar *) JS_realloc(cx, ls, (ln + rn + 1) * sizeof(jschar));
  143. if (!s)
  144. return NULL;
  145. /* Take care: right could depend on left! */
  146. lrdist = (size_t)(rs - ls);
  147. if (lrdist < ln)
  148. rs = s + lrdist;
  149. left->u.chars = ls = s;
  150. ldep = left;
  151. }
  152. js_strncpy(s + ln, rs, rn);
  153. n = ln + rn;
  154. s[n] = 0;
  155. str = js_NewString(cx, s, n);
  156. if (!str) {
  157. /* Out of memory: clean up any space we (re-)allocated. */
  158. if (!ldep) {
  159. JS_free(cx, s);
  160. } else {
  161. s = (jschar *) JS_realloc(cx, ls, (ln + 1) * sizeof(jschar));
  162. if (s)
  163. left->u.chars = s;
  164. }
  165. } else {
  166. JSFLATSTR_SET_MUTABLE(str);
  167. /* Morph left into a dependent prefix if we realloc'd its buffer. */
  168. if (ldep) {
  169. JSPREFIX_INIT(ldep, str, ln);
  170. #ifdef DEBUG
  171. {
  172. JSRuntime *rt = cx->runtime;
  173. JS_RUNTIME_METER(rt, liveDependentStrings);
  174. JS_RUNTIME_METER(rt, totalDependentStrings);
  175. JS_LOCK_RUNTIME_VOID(rt,
  176. (rt->strdepLengthSum += (double)ln,
  177. rt->strdepLengthSquaredSum += (double)ln * (double)ln));
  178. }
  179. #endif
  180. }
  181. }
  182. return str;
  183. }
  184. const jschar *
  185. js_UndependString(JSContext *cx, JSString *str)
  186. {
  187. size_t n, size;
  188. jschar *s;
  189. if (JSSTRING_IS_DEPENDENT(str)) {
  190. n = JSSTRDEP_LENGTH(str);
  191. size = (n + 1) * sizeof(jschar);
  192. s = (jschar *) JS_malloc(cx, size);
  193. if (!s)
  194. return NULL;
  195. js_strncpy(s, JSSTRDEP_CHARS(str), n);
  196. s[n] = 0;
  197. JSFLATSTR_INIT(str, s, n);
  198. #ifdef DEBUG
  199. {
  200. JSRuntime *rt = cx->runtime;
  201. JS_RUNTIME_UNMETER(rt, liveDependentStrings);
  202. JS_RUNTIME_UNMETER(rt, totalDependentStrings);
  203. JS_LOCK_RUNTIME_VOID(rt,
  204. (rt->strdepLengthSum -= (double)n,
  205. rt->strdepLengthSquaredSum -= (double)n * (double)n));
  206. }
  207. #endif
  208. }
  209. return JSFLATSTR_CHARS(str);
  210. }
  211. JSBool
  212. js_MakeStringImmutable(JSContext *cx, JSString *str)
  213. {
  214. if (JSSTRING_IS_DEPENDENT(str) && !js_UndependString(cx, str)) {
  215. JS_RUNTIME_METER(cx->runtime, badUndependStrings);
  216. return JS_FALSE;
  217. }
  218. JSFLATSTR_CLEAR_MUTABLE(str);
  219. return JS_TRUE;
  220. }
  221. static JSString *
  222. ArgToRootedString(JSContext *cx, uintN argc, jsval *vp, uintN arg)
  223. {
  224. JSObject *obj;
  225. JSString *str;
  226. if (arg >= argc)
  227. return ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
  228. vp += 2 + arg;
  229. if (JSVAL_IS_OBJECT(*vp)) {
  230. obj = JSVAL_TO_OBJECT(*vp);
  231. if (!obj)
  232. return ATOM_TO_STRING(cx->runtime->atomState.nullAtom);
  233. if (!OBJ_DEFAULT_VALUE(cx, obj, JSTYPE_STRING, vp))
  234. return NULL;
  235. }
  236. if (JSVAL_IS_STRING(*vp))
  237. return JSVAL_TO_STRING(*vp);
  238. if (JSVAL_IS_INT(*vp)) {
  239. str = js_NumberToString(cx, JSVAL_TO_INT(*vp));
  240. } else if (JSVAL_IS_DOUBLE(*vp)) {
  241. str = js_NumberToString(cx, *JSVAL_TO_DOUBLE(*vp));
  242. } else if (JSVAL_IS_BOOLEAN(*vp)) {
  243. return ATOM_TO_STRING(cx->runtime->atomState.booleanAtoms[
  244. JSVAL_TO_BOOLEAN(*vp)? 1 : 0]);
  245. } else {
  246. JS_ASSERT(JSVAL_IS_VOID(*vp));
  247. return ATOM_TO_STRING(cx->runtime->atomState.typeAtoms[JSTYPE_VOID]);
  248. }
  249. if (str)
  250. *vp = STRING_TO_JSVAL(str);
  251. return str;
  252. }
  253. /*
  254. * Forward declarations for URI encode/decode and helper routines
  255. */
  256. static JSBool
  257. str_decodeURI(JSContext *cx, uintN argc, jsval *vp);
  258. static JSBool
  259. str_decodeURI_Component(JSContext *cx, uintN argc, jsval *vp);
  260. static JSBool
  261. str_encodeURI(JSContext *cx, uintN argc, jsval *vp);
  262. static JSBool
  263. str_encodeURI_Component(JSContext *cx, uintN argc, jsval *vp);
  264. static uint32
  265. Utf8ToOneUcs4Char(const uint8 *utf8Buffer, int utf8Length);
  266. /*
  267. * Contributions from the String class to the set of methods defined for the
  268. * global object. escape and unescape used to be defined in the Mocha library,
  269. * but as ECMA decided to spec them, they've been moved to the core engine
  270. * and made ECMA-compliant. (Incomplete escapes are interpreted as literal
  271. * characters by unescape.)
  272. */
  273. /*
  274. * Stuff to emulate the old libmocha escape, which took a second argument
  275. * giving the type of escape to perform. Retained for compatibility, and
  276. * copied here to avoid reliance on net.h, mkparse.c/NET_EscapeBytes.
  277. */
  278. #define URL_XALPHAS ((uint8) 1)
  279. #define URL_XPALPHAS ((uint8) 2)
  280. #define URL_PATH ((uint8) 4)
  281. static const uint8 urlCharType[256] =
  282. /* Bit 0 xalpha -- the alphas
  283. * Bit 1 xpalpha -- as xalpha but
  284. * converts spaces to plus and plus to %20
  285. * Bit 2 ... path -- as xalphas but doesn't escape '/'
  286. */
  287. /* 0 1 2 3 4 5 6 7 8 9 A B C D E F */
  288. { 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 0x */
  289. 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, /* 1x */
  290. 0,0,0,0,0,0,0,0,0,0,7,4,0,7,7,4, /* 2x !"#$%&'()*+,-./ */
  291. 7,7,7,7,7,7,7,7,7,7,0,0,0,0,0,0, /* 3x 0123456789:;<=>? */
  292. 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 4x @ABCDEFGHIJKLMNO */
  293. 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,7, /* 5X PQRSTUVWXYZ[\]^_ */
  294. 0,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, /* 6x `abcdefghijklmno */
  295. 7,7,7,7,7,7,7,7,7,7,7,0,0,0,0,0, /* 7X pqrstuvwxyz{\}~ DEL */
  296. 0, };
  297. /* This matches the ECMA escape set when mask is 7 (default.) */
  298. #define IS_OK(C, mask) (urlCharType[((uint8) (C))] & (mask))
  299. /* See ECMA-262 Edition 3 B.2.1 */
  300. JSBool
  301. js_str_escape(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval)
  302. {
  303. JSString *str;
  304. size_t i, ni, length, newlength;
  305. const jschar *chars;
  306. jschar *newchars;
  307. jschar ch;
  308. jsint mask;
  309. jsdouble d;
  310. const char digits[] = {'0', '1', '2', '3', '4', '5', '6', '7',
  311. '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
  312. mask = URL_XALPHAS | URL_XPALPHAS | URL_PATH;
  313. if (argc > 1) {
  314. d = js_ValueToNumber(cx, &argv[1]);
  315. if (JSVAL_IS_NULL(argv[1]))
  316. return JS_FALSE;
  317. if (!JSDOUBLE_IS_FINITE(d) ||
  318. (mask = (jsint)d) != d ||
  319. mask & ~(URL_XALPHAS | URL_XPALPHAS | URL_PATH))
  320. {
  321. char numBuf[12];
  322. JS_snprintf(numBuf, sizeof numBuf, "%lx", (unsigned long) mask);
  323. JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL,
  324. JSMSG_BAD_STRING_MASK, numBuf);
  325. return JS_FALSE;
  326. }
  327. }
  328. str = ArgToRootedString(cx, argc, argv - 2, 0);
  329. if (!str)
  330. return JS_FALSE;
  331. JSSTRING_CHARS_AND_LENGTH(str, chars, length);
  332. newlength = length;
  333. /* Take a first pass and see how big the result string will need to be. */
  334. for (i = 0; i < length; i++) {
  335. if ((ch = chars[i]) < 128 && IS_OK(ch, mask))
  336. continue;
  337. if (ch < 256) {
  338. if (mask == URL_XPALPHAS && ch == ' ')
  339. continue; /* The character will be encoded as '+' */
  340. newlength += 2; /* The character will be encoded as %XX */
  341. } else {
  342. newlength += 5; /* The character will be encoded as %uXXXX */
  343. }
  344. /*
  345. * This overflow test works because newlength is incremented by at
  346. * most 5 on each iteration.
  347. */
  348. if (newlength < length) {
  349. js_ReportAllocationOverflow(cx);
  350. return JS_FALSE;
  351. }
  352. }
  353. if (newlength >= ~(size_t)0 / sizeof(jschar)) {
  354. js_ReportAllocationOverflow(cx);
  355. return JS_FALSE;
  356. }
  357. newchars = (jschar *) JS_malloc(cx, (newlength + 1) * sizeof(jschar));
  358. if (!newchars)
  359. return JS_FALSE;
  360. for (i = 0, ni = 0; i < length; i++) {
  361. if ((ch = chars[i]) < 128 && IS_OK(ch, mask)) {
  362. newchars[ni++] = ch;
  363. } else if (ch < 256) {
  364. if (mask == URL_XPALPHAS && ch == ' ') {
  365. newchars[ni++] = '+'; /* convert spaces to pluses */
  366. } else {
  367. newchars[ni++] = '%';
  368. newchars[ni++] = digits[ch >> 4];
  369. newchars[ni++] = digits[ch & 0xF];
  370. }
  371. } else {
  372. newchars[ni++] = '%';
  373. newchars[ni++] = 'u';
  374. newchars[ni++] = digits[ch >> 12];
  375. newchars[ni++] = digits[(ch & 0xF00) >> 8];
  376. newchars[ni++] = digits[(ch & 0xF0) >> 4];
  377. newchars[ni++] = digits[ch & 0xF];
  378. }
  379. }
  380. JS_ASSERT(ni == newlength);
  381. newchars[newlength] = 0;
  382. str = js_NewString(cx, newchars, newlength);
  383. if (!str) {
  384. JS_free(cx, newchars);
  385. return JS_FALSE;
  386. }
  387. *rval = STRING_TO_JSVAL(str);
  388. return JS_TRUE;
  389. }
  390. #undef IS_OK
  391. static JSBool
  392. str_escape(JSContext *cx, uintN argc, jsval *vp)
  393. {
  394. JSObject *obj;
  395. obj = JS_THIS_OBJECT(cx, vp);
  396. return obj && js_str_escape(cx, obj, argc, vp + 2, vp);
  397. }
  398. /* See ECMA-262 Edition 3 B.2.2 */
  399. static JSBool
  400. str_unescape(JSContext *cx, uintN argc, jsval *vp)
  401. {
  402. JSString *str;
  403. size_t i, ni, length;
  404. const jschar *chars;
  405. jschar *newchars;
  406. jschar ch;
  407. str = ArgToRootedString(cx, argc, vp, 0);
  408. if (!str)
  409. return JS_FALSE;
  410. JSSTRING_CHARS_AND_LENGTH(str, chars, length);
  411. /* Don't bother allocating less space for the new string. */
  412. newchars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
  413. if (!newchars)
  414. return JS_FALSE;
  415. ni = i = 0;
  416. while (i < length) {
  417. ch = chars[i++];
  418. if (ch == '%') {
  419. if (i + 1 < length &&
  420. JS7_ISHEX(chars[i]) && JS7_ISHEX(chars[i + 1]))
  421. {
  422. ch = JS7_UNHEX(chars[i]) * 16 + JS7_UNHEX(chars[i + 1]);
  423. i += 2;
  424. } else if (i + 4 < length && chars[i] == 'u' &&
  425. JS7_ISHEX(chars[i + 1]) && JS7_ISHEX(chars[i + 2]) &&
  426. JS7_ISHEX(chars[i + 3]) && JS7_ISHEX(chars[i + 4]))
  427. {
  428. ch = (((((JS7_UNHEX(chars[i + 1]) << 4)
  429. + JS7_UNHEX(chars[i + 2])) << 4)
  430. + JS7_UNHEX(chars[i + 3])) << 4)
  431. + JS7_UNHEX(chars[i + 4]);
  432. i += 5;
  433. }
  434. }
  435. newchars[ni++] = ch;
  436. }
  437. newchars[ni] = 0;
  438. str = js_NewString(cx, newchars, ni);
  439. if (!str) {
  440. JS_free(cx, newchars);
  441. return JS_FALSE;
  442. }
  443. *vp = STRING_TO_JSVAL(str);
  444. return JS_TRUE;
  445. }
  446. #if JS_HAS_UNEVAL
  447. static JSBool
  448. str_uneval(JSContext *cx, uintN argc, jsval *vp)
  449. {
  450. JSString *str;
  451. str = js_ValueToSource(cx, argc != 0 ? vp[2] : JSVAL_VOID);
  452. if (!str)
  453. return JS_FALSE;
  454. *vp = STRING_TO_JSVAL(str);
  455. return JS_TRUE;
  456. }
  457. #endif
  458. const char js_escape_str[] = "escape";
  459. const char js_unescape_str[] = "unescape";
  460. #if JS_HAS_UNEVAL
  461. const char js_uneval_str[] = "uneval";
  462. #endif
  463. const char js_decodeURI_str[] = "decodeURI";
  464. const char js_encodeURI_str[] = "encodeURI";
  465. const char js_decodeURIComponent_str[] = "decodeURIComponent";
  466. const char js_encodeURIComponent_str[] = "encodeURIComponent";
  467. static JSFunctionSpec string_functions[] = {
  468. JS_FN(js_escape_str, str_escape, 1,0),
  469. JS_FN(js_unescape_str, str_unescape, 1,0),
  470. #if JS_HAS_UNEVAL
  471. JS_FN(js_uneval_str, str_uneval, 1,0),
  472. #endif
  473. JS_FN(js_decodeURI_str, str_decodeURI, 1,0),
  474. JS_FN(js_encodeURI_str, str_encodeURI, 1,0),
  475. JS_FN(js_decodeURIComponent_str, str_decodeURI_Component, 1,0),
  476. JS_FN(js_encodeURIComponent_str, str_encodeURI_Component, 1,0),
  477. JS_FS_END
  478. };
  479. jschar js_empty_ucstr[] = {0};
  480. JSSubString js_EmptySubString = {0, js_empty_ucstr};
  481. enum string_tinyid {
  482. STRING_LENGTH = -1
  483. };
  484. static JSPropertySpec string_props[] = {
  485. {js_length_str, STRING_LENGTH,
  486. JSPROP_READONLY|JSPROP_PERMANENT|JSPROP_SHARED, 0,0},
  487. {0,0,0,0,0}
  488. };
  489. static JSBool
  490. str_getProperty(JSContext *cx, JSObject *obj, jsval id, jsval *vp)
  491. {
  492. jsval v;
  493. JSString *str;
  494. jsint slot;
  495. if (!JSVAL_IS_INT(id))
  496. return JS_TRUE;
  497. slot = JSVAL_TO_INT(id);
  498. if (slot == STRING_LENGTH) {
  499. if (OBJ_GET_CLASS(cx, obj) == &js_StringClass) {
  500. /* Follow ECMA-262 by fetching intrinsic length of our string. */
  501. v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
  502. JS_ASSERT(JSVAL_IS_STRING(v));
  503. str = JSVAL_TO_STRING(v);
  504. } else {
  505. /* Preserve compatibility: convert obj to a string primitive. */
  506. str = js_ValueToString(cx, OBJECT_TO_JSVAL(obj));
  507. if (!str)
  508. return JS_FALSE;
  509. }
  510. *vp = INT_TO_JSVAL((jsint) JSSTRING_LENGTH(str));
  511. }
  512. return JS_TRUE;
  513. }
  514. #define STRING_ELEMENT_ATTRS (JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT)
  515. static JSBool
  516. str_enumerate(JSContext *cx, JSObject *obj)
  517. {
  518. jsval v;
  519. JSString *str, *str1;
  520. size_t i, length;
  521. v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
  522. JS_ASSERT(JSVAL_IS_STRING(v));
  523. str = JSVAL_TO_STRING(v);
  524. length = JSSTRING_LENGTH(str);
  525. for (i = 0; i < length; i++) {
  526. str1 = js_NewDependentString(cx, str, i, 1);
  527. if (!str1)
  528. return JS_FALSE;
  529. if (!OBJ_DEFINE_PROPERTY(cx, obj, INT_TO_JSID(i),
  530. STRING_TO_JSVAL(str1), NULL, NULL,
  531. STRING_ELEMENT_ATTRS, NULL)) {
  532. return JS_FALSE;
  533. }
  534. }
  535. return JS_TRUE;
  536. }
  537. static JSBool
  538. str_resolve(JSContext *cx, JSObject *obj, jsval id, uintN flags,
  539. JSObject **objp)
  540. {
  541. jsval v;
  542. JSString *str, *str1;
  543. jsint slot;
  544. if (!JSVAL_IS_INT(id) || (flags & JSRESOLVE_ASSIGNING))
  545. return JS_TRUE;
  546. v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
  547. JS_ASSERT(JSVAL_IS_STRING(v));
  548. str = JSVAL_TO_STRING(v);
  549. slot = JSVAL_TO_INT(id);
  550. if ((size_t)slot < JSSTRING_LENGTH(str)) {
  551. str1 = js_GetUnitString(cx, str, (size_t)slot);
  552. if (!str1)
  553. return JS_FALSE;
  554. if (!OBJ_DEFINE_PROPERTY(cx, obj, INT_TO_JSID(slot),
  555. STRING_TO_JSVAL(str1), NULL, NULL,
  556. STRING_ELEMENT_ATTRS, NULL)) {
  557. return JS_FALSE;
  558. }
  559. *objp = obj;
  560. }
  561. return JS_TRUE;
  562. }
  563. JSClass js_StringClass = {
  564. js_String_str,
  565. JSCLASS_HAS_PRIVATE | JSCLASS_NEW_RESOLVE |
  566. JSCLASS_HAS_CACHED_PROTO(JSProto_String),
  567. JS_PropertyStub, JS_PropertyStub, str_getProperty, JS_PropertyStub,
  568. str_enumerate, (JSResolveOp)str_resolve, JS_ConvertStub, JS_FinalizeStub,
  569. JSCLASS_NO_OPTIONAL_MEMBERS
  570. };
  571. #define NORMALIZE_THIS(cx,vp,str) \
  572. JS_BEGIN_MACRO \
  573. if (JSVAL_IS_STRING(vp[1])) { \
  574. str = JSVAL_TO_STRING(vp[1]); \
  575. } else { \
  576. str = NormalizeThis(cx, vp); \
  577. if (!str) \
  578. return JS_FALSE; \
  579. } \
  580. JS_END_MACRO
  581. static JSString *
  582. NormalizeThis(JSContext *cx, jsval *vp)
  583. {
  584. JSString *str;
  585. if (JSVAL_IS_NULL(vp[1]) && JSVAL_IS_NULL(JS_THIS(cx, vp)))
  586. return NULL;
  587. str = js_ValueToString(cx, vp[1]);
  588. if (!str)
  589. return NULL;
  590. vp[1] = STRING_TO_JSVAL(str);
  591. return str;
  592. }
  593. #if JS_HAS_TOSOURCE
  594. /*
  595. * String.prototype.quote is generic (as are most string methods), unlike
  596. * toSource, toString, and valueOf.
  597. */
  598. static JSBool
  599. str_quote(JSContext *cx, uintN argc, jsval *vp)
  600. {
  601. JSString *str;
  602. NORMALIZE_THIS(cx, vp, str);
  603. str = js_QuoteString(cx, str, '"');
  604. if (!str)
  605. return JS_FALSE;
  606. *vp = STRING_TO_JSVAL(str);
  607. return JS_TRUE;
  608. }
  609. static JSBool
  610. str_toSource(JSContext *cx, uintN argc, jsval *vp)
  611. {
  612. jsval v;
  613. JSString *str;
  614. size_t i, j, k, n;
  615. char buf[16];
  616. jschar *s, *t;
  617. if (!js_GetPrimitiveThis(cx, vp, &js_StringClass, &v))
  618. return JS_FALSE;
  619. JS_ASSERT(JSVAL_IS_STRING(v));
  620. str = js_QuoteString(cx, JSVAL_TO_STRING(v), '"');
  621. if (!str)
  622. return JS_FALSE;
  623. j = JS_snprintf(buf, sizeof buf, "(new %s(", js_StringClass.name);
  624. JSSTRING_CHARS_AND_LENGTH(str, s, k);
  625. n = j + k + 2;
  626. t = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar));
  627. if (!t)
  628. return JS_FALSE;
  629. for (i = 0; i < j; i++)
  630. t[i] = buf[i];
  631. for (j = 0; j < k; i++, j++)
  632. t[i] = s[j];
  633. t[i++] = ')';
  634. t[i++] = ')';
  635. t[i] = 0;
  636. str = js_NewString(cx, t, n);
  637. if (!str) {
  638. JS_free(cx, t);
  639. return JS_FALSE;
  640. }
  641. *vp = STRING_TO_JSVAL(str);
  642. return JS_TRUE;
  643. }
  644. #endif /* JS_HAS_TOSOURCE */
  645. static JSBool
  646. str_toString(JSContext *cx, uintN argc, jsval *vp)
  647. {
  648. return js_GetPrimitiveThis(cx, vp, &js_StringClass, vp);
  649. }
  650. /*
  651. * Java-like string native methods.
  652. */
  653. static JSString *
  654. SubstringTail(JSContext *cx, JSString *str, jsdouble length, jsdouble begin, jsdouble end)
  655. {
  656. if (begin < 0)
  657. begin = 0;
  658. else if (begin > length)
  659. begin = length;
  660. if (end < 0)
  661. end = 0;
  662. else if (end > length)
  663. end = length;
  664. if (end < begin) {
  665. /* ECMA emulates old JDK1.0 java.lang.String.substring. */
  666. jsdouble tmp = begin;
  667. begin = end;
  668. end = tmp;
  669. }
  670. return js_NewDependentString(cx, str, (size_t)begin, (size_t)(end - begin));
  671. }
  672. static JSBool
  673. str_substring(JSContext *cx, uintN argc, jsval *vp)
  674. {
  675. JSString *str;
  676. jsdouble d;
  677. jsdouble length, begin, end;
  678. NORMALIZE_THIS(cx, vp, str);
  679. if (argc != 0) {
  680. d = js_ValueToNumber(cx, &vp[2]);
  681. if (JSVAL_IS_NULL(vp[2]))
  682. return JS_FALSE;
  683. length = JSSTRING_LENGTH(str);
  684. begin = js_DoubleToInteger(d);
  685. if (argc == 1) {
  686. end = length;
  687. } else {
  688. d = js_ValueToNumber(cx, &vp[3]);
  689. if (JSVAL_IS_NULL(vp[3]))
  690. return JS_FALSE;
  691. end = js_DoubleToInteger(d);
  692. }
  693. str = SubstringTail(cx, str, length, begin, end);
  694. if (!str)
  695. return JS_FALSE;
  696. }
  697. *vp = STRING_TO_JSVAL(str);
  698. return JS_TRUE;
  699. }
  700. #ifdef JS_TRACER
  701. static JSString* FASTCALL
  702. String_p_toString(JSContext* cx, JSObject* obj)
  703. {
  704. if (!JS_InstanceOf(cx, obj, &js_StringClass, NULL))
  705. return NULL;
  706. jsval v = OBJ_GET_SLOT(cx, obj, JSSLOT_PRIVATE);
  707. JS_ASSERT(JSVAL_IS_STRING(v));
  708. return JSVAL_TO_STRING(v);
  709. }
  710. static JSString* FASTCALL
  711. String_p_substring(JSContext* cx, JSString* str, int32 begin, int32 end)
  712. {
  713. JS_ASSERT(JS_ON_TRACE(cx));
  714. size_t length = JSSTRING_LENGTH(str);
  715. return SubstringTail(cx, str, length, begin, end);
  716. }
  717. static JSString* FASTCALL
  718. String_p_substring_1(JSContext* cx, JSString* str, int32 begin)
  719. {
  720. JS_ASSERT(JS_ON_TRACE(cx));
  721. size_t length = JSSTRING_LENGTH(str);
  722. return SubstringTail(cx, str, length, begin, length);
  723. }
  724. #endif
  725. JSString* JS_FASTCALL
  726. js_toLowerCase(JSContext *cx, JSString *str)
  727. {
  728. size_t i, n;
  729. jschar *s, *news;
  730. JSSTRING_CHARS_AND_LENGTH(str, s, n);
  731. news = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar));
  732. if (!news)
  733. return NULL;
  734. for (i = 0; i < n; i++)
  735. news[i] = JS_TOLOWER(s[i]);
  736. news[n] = 0;
  737. str = js_NewString(cx, news, n);
  738. if (!str) {
  739. JS_free(cx, news);
  740. return NULL;
  741. }
  742. return str;
  743. }
  744. static JSBool
  745. str_toLowerCase(JSContext *cx, uintN argc, jsval *vp)
  746. {
  747. JSString *str;
  748. NORMALIZE_THIS(cx, vp, str);
  749. str = js_toLowerCase(cx, str);
  750. if (!str)
  751. return JS_FALSE;
  752. *vp = STRING_TO_JSVAL(str);
  753. return JS_TRUE;
  754. }
  755. static JSBool
  756. str_toLocaleLowerCase(JSContext *cx, uintN argc, jsval *vp)
  757. {
  758. JSString *str;
  759. /*
  760. * Forcefully ignore the first (or any) argument and return toLowerCase(),
  761. * ECMA has reserved that argument, presumably for defining the locale.
  762. */
  763. if (cx->localeCallbacks && cx->localeCallbacks->localeToLowerCase) {
  764. NORMALIZE_THIS(cx, vp, str);
  765. return cx->localeCallbacks->localeToLowerCase(cx, str, vp);
  766. }
  767. return str_toLowerCase(cx, 0, vp);
  768. }
  769. JSString* JS_FASTCALL
  770. js_toUpperCase(JSContext *cx, JSString *str)
  771. {
  772. size_t i, n;
  773. jschar *s, *news;
  774. JSSTRING_CHARS_AND_LENGTH(str, s, n);
  775. news = (jschar *) JS_malloc(cx, (n + 1) * sizeof(jschar));
  776. if (!news)
  777. return NULL;
  778. for (i = 0; i < n; i++)
  779. news[i] = JS_TOUPPER(s[i]);
  780. news[n] = 0;
  781. str = js_NewString(cx, news, n);
  782. if (!str) {
  783. JS_free(cx, news);
  784. return NULL;
  785. }
  786. return str;
  787. }
  788. static JSBool
  789. str_toUpperCase(JSContext *cx, uintN argc, jsval *vp)
  790. {
  791. JSString *str;
  792. NORMALIZE_THIS(cx, vp, str);
  793. str = js_toUpperCase(cx, str);
  794. if (!str)
  795. return JS_FALSE;
  796. *vp = STRING_TO_JSVAL(str);
  797. return JS_TRUE;
  798. }
  799. static JSBool
  800. str_toLocaleUpperCase(JSContext *cx, uintN argc, jsval *vp)
  801. {
  802. JSString *str;
  803. /*
  804. * Forcefully ignore the first (or any) argument and return toUpperCase(),
  805. * ECMA has reserved that argument, presumably for defining the locale.
  806. */
  807. if (cx->localeCallbacks && cx->localeCallbacks->localeToUpperCase) {
  808. NORMALIZE_THIS(cx, vp, str);
  809. return cx->localeCallbacks->localeToUpperCase(cx, str, vp);
  810. }
  811. return str_toUpperCase(cx, 0, vp);
  812. }
  813. static JSBool
  814. str_localeCompare(JSContext *cx, uintN argc, jsval *vp)
  815. {
  816. JSString *str, *thatStr;
  817. NORMALIZE_THIS(cx, vp, str);
  818. if (argc == 0) {
  819. *vp = JSVAL_ZERO;
  820. } else {
  821. thatStr = js_ValueToString(cx, vp[2]);
  822. if (!thatStr)
  823. return JS_FALSE;
  824. if (cx->localeCallbacks && cx->localeCallbacks->localeCompare) {
  825. vp[2] = STRING_TO_JSVAL(thatStr);
  826. return cx->localeCallbacks->localeCompare(cx, str, thatStr, vp);
  827. }
  828. *vp = INT_TO_JSVAL(js_CompareStrings(str, thatStr));
  829. }
  830. return JS_TRUE;
  831. }
  832. static JSBool
  833. str_charAt(JSContext *cx, uintN argc, jsval *vp)
  834. {
  835. jsval t;
  836. JSString *str;
  837. jsint i;
  838. jsdouble d;
  839. t = vp[1];
  840. if (JSVAL_IS_STRING(t) && argc != 0 && JSVAL_IS_INT(vp[2])) {
  841. str = JSVAL_TO_STRING(t);
  842. i = JSVAL_TO_INT(vp[2]);
  843. if ((size_t)i >= JSSTRING_LENGTH(str))
  844. goto out_of_range;
  845. } else {
  846. str = NormalizeThis(cx, vp);
  847. if (!str)
  848. return JS_FALSE;
  849. if (argc == 0) {
  850. d = 0.0;
  851. } else {
  852. d = js_ValueToNumber(cx, &vp[2]);
  853. if (JSVAL_IS_NULL(vp[2]))
  854. return JS_FALSE;
  855. d = js_DoubleToInteger(d);
  856. }
  857. if (d < 0 || JSSTRING_LENGTH(str) <= d)
  858. goto out_of_range;
  859. i = (jsint) d;
  860. }
  861. str = js_GetUnitString(cx, str, (size_t)i);
  862. if (!str)
  863. return JS_FALSE;
  864. *vp = STRING_TO_JSVAL(str);
  865. return JS_TRUE;
  866. out_of_range:
  867. *vp = JS_GetEmptyStringValue(cx);
  868. return JS_TRUE;
  869. }
  870. static JSBool
  871. str_charCodeAt(JSContext *cx, uintN argc, jsval *vp)
  872. {
  873. jsval t;
  874. JSString *str;
  875. jsint i;
  876. jsdouble d;
  877. t = vp[1];
  878. if (JSVAL_IS_STRING(t) && argc != 0 && JSVAL_IS_INT(vp[2])) {
  879. str = JSVAL_TO_STRING(t);
  880. i = JSVAL_TO_INT(vp[2]);
  881. if ((size_t)i >= JSSTRING_LENGTH(str))
  882. goto out_of_range;
  883. } else {
  884. str = NormalizeThis(cx, vp);
  885. if (!str)
  886. return JS_FALSE;
  887. if (argc == 0) {
  888. d = 0.0;
  889. } else {
  890. d = js_ValueToNumber(cx, &vp[2]);
  891. if (JSVAL_IS_NULL(vp[2]))
  892. return JS_FALSE;
  893. d = js_DoubleToInteger(d);
  894. }
  895. if (d < 0 || JSSTRING_LENGTH(str) <= d)
  896. goto out_of_range;
  897. i = (jsint) d;
  898. }
  899. *vp = INT_TO_JSVAL(JSSTRING_CHARS(str)[i]);
  900. return JS_TRUE;
  901. out_of_range:
  902. *vp = JS_GetNaNValue(cx);
  903. return JS_TRUE;
  904. }
  905. #ifdef JS_TRACER
  906. int32 FASTCALL
  907. js_String_p_charCodeAt(JSString* str, int32 i)
  908. {
  909. if (i < 0 || (int32)JSSTRING_LENGTH(str) <= i)
  910. return -1;
  911. return JSSTRING_CHARS(str)[i];
  912. }
  913. #endif
  914. jsint
  915. js_BoyerMooreHorspool(const jschar *text, jsint textlen,
  916. const jschar *pat, jsint patlen,
  917. jsint start)
  918. {
  919. jsint i, j, k, m;
  920. uint8 skip[BMH_CHARSET_SIZE];
  921. jschar c;
  922. JS_ASSERT(0 < patlen && patlen <= BMH_PATLEN_MAX);
  923. for (i = 0; i < BMH_CHARSET_SIZE; i++)
  924. skip[i] = (uint8)patlen;
  925. m = patlen - 1;
  926. for (i = 0; i < m; i++) {
  927. c = pat[i];
  928. if (c >= BMH_CHARSET_SIZE)
  929. return BMH_BAD_PATTERN;
  930. skip[c] = (uint8)(m - i);
  931. }
  932. for (k = start + m;
  933. k < textlen;
  934. k += ((c = text[k]) >= BMH_CHARSET_SIZE) ? patlen : skip[c]) {
  935. for (i = k, j = m; ; i--, j--) {
  936. if (j < 0)
  937. return i + 1;
  938. if (text[i] != pat[j])
  939. break;
  940. }
  941. }
  942. return -1;
  943. }
  944. static JSBool
  945. str_indexOf(JSContext *cx, uintN argc, jsval *vp)
  946. {
  947. jsval t;
  948. JSString *str, *str2;
  949. const jschar *text, *pat;
  950. jsint i, j, index, textlen, patlen;
  951. jsdouble d;
  952. t = vp[1];
  953. if (JSVAL_IS_STRING(t) && argc != 0 && JSVAL_IS_STRING(vp[2])) {
  954. str = JSVAL_TO_STRING(t);
  955. str2 = JSVAL_TO_STRING(vp[2]);
  956. } else {
  957. str = NormalizeThis(cx, vp);
  958. if (!str)
  959. return JS_FALSE;
  960. str2 = ArgToRootedString(cx, argc, vp, 0);
  961. if (!str2)
  962. return JS_FALSE;
  963. }
  964. text = JSSTRING_CHARS(str);
  965. textlen = (jsint) JSSTRING_LENGTH(str);
  966. pat = JSSTRING_CHARS(str2);
  967. patlen = (jsint) JSSTRING_LENGTH(str2);
  968. if (argc > 1) {
  969. d = js_ValueToNumber(cx, &vp[3]);
  970. if (JSVAL_IS_NULL(vp[3]))
  971. return JS_FALSE;
  972. d = js_DoubleToInteger(d);
  973. if (d < 0)
  974. i = 0;
  975. else if (d > textlen)
  976. i = textlen;
  977. else
  978. i = (jsint)d;
  979. } else {
  980. i = 0;
  981. }
  982. if (patlen == 0) {
  983. *vp = INT_TO_JSVAL(i);
  984. return JS_TRUE;
  985. }
  986. /* XXX tune the BMH threshold (512) */
  987. if (textlen - i >= 512 && (jsuint)(patlen - 2) <= BMH_PATLEN_MAX - 2) {
  988. index = js_BoyerMooreHorspool(text, textlen, pat, patlen, i);
  989. if (index != BMH_BAD_PATTERN)
  990. goto out;
  991. }
  992. index = -1;
  993. j = 0;
  994. while (i + j < textlen) {
  995. if (text[i + j] == pat[j]) {
  996. if (++j == patlen) {
  997. index = i;
  998. break;
  999. }
  1000. } else {
  1001. i++;
  1002. j = 0;
  1003. }
  1004. }
  1005. out:
  1006. *vp = INT_TO_JSVAL(index);
  1007. return JS_TRUE;
  1008. }
  1009. static JSBool
  1010. str_lastIndexOf(JSContext *cx, uintN argc, jsval *vp)
  1011. {
  1012. JSString *str, *str2;
  1013. const jschar *text, *pat;
  1014. jsint i, j, textlen, patlen;
  1015. jsdouble d;
  1016. NORMALIZE_THIS(cx, vp, str);
  1017. text = JSSTRING_CHARS(str);
  1018. textlen = (jsint) JSSTRING_LENGTH(str);
  1019. str2 = ArgToRootedString(cx, argc, vp, 0);
  1020. if (!str2)
  1021. return JS_FALSE;
  1022. pat = JSSTRING_CHARS(str2);
  1023. patlen = (jsint) JSSTRING_LENGTH(str2);
  1024. if (argc > 1) {
  1025. d = js_ValueToNumber(cx, &vp[3]);
  1026. if (JSVAL_IS_NULL(vp[3]))
  1027. return JS_FALSE;
  1028. if (JSDOUBLE_IS_NaN(d)) {
  1029. i = textlen;
  1030. } else {
  1031. d = js_DoubleToInteger(d);
  1032. if (d < 0)
  1033. i = 0;
  1034. else if (d > textlen)
  1035. i = textlen;
  1036. else
  1037. i = (jsint)d;
  1038. }
  1039. } else {
  1040. i = textlen;
  1041. }
  1042. if (patlen == 0) {
  1043. *vp = INT_TO_JSVAL(i);
  1044. return JS_TRUE;
  1045. }
  1046. j = 0;
  1047. while (i >= 0) {
  1048. /* Don't assume that text is NUL-terminated: it could be dependent. */
  1049. if (i + j < textlen && text[i + j] == pat[j]) {
  1050. if (++j == patlen)
  1051. break;
  1052. } else {
  1053. i--;
  1054. j = 0;
  1055. }
  1056. }
  1057. *vp = INT_TO_JSVAL(i);
  1058. return JS_TRUE;
  1059. }
  1060. static JSBool
  1061. js_TrimString(JSContext *cx, jsval *vp, JSBool trimLeft, JSBool trimRight)
  1062. {
  1063. JSString *str;
  1064. const jschar *chars;
  1065. size_t length, begin, end;
  1066. NORMALIZE_THIS(cx, vp, str);
  1067. JSSTRING_CHARS_AND_LENGTH(str, chars, length);
  1068. begin = 0;
  1069. end = length;
  1070. if (trimLeft) {
  1071. while (begin < length && JS_ISSPACE(chars[begin]))
  1072. ++begin;
  1073. }
  1074. if (trimRight) {
  1075. while (end > begin && JS_ISSPACE(chars[end-1]))
  1076. --end;
  1077. }
  1078. str = js_NewDependentString(cx, str, begin, end - begin);
  1079. if (!str)
  1080. return JS_FALSE;
  1081. *vp = STRING_TO_JSVAL(str);
  1082. return JS_TRUE;
  1083. }
  1084. static JSBool
  1085. str_trim(JSContext *cx, uintN argc, jsval *vp)
  1086. {
  1087. return js_TrimString(cx, vp, JS_TRUE, JS_TRUE);
  1088. }
  1089. static JSBool
  1090. str_trimLeft(JSContext *cx, uintN argc, jsval *vp)
  1091. {
  1092. return js_TrimString(cx, vp, JS_TRUE, JS_FALSE);
  1093. }
  1094. static JSBool
  1095. str_trimRight(JSContext *cx, uintN argc, jsval *vp)
  1096. {
  1097. return js_TrimString(cx, vp, JS_FALSE, JS_TRUE);
  1098. }
  1099. /*
  1100. * Perl-inspired string functions.
  1101. */
  1102. typedef struct GlobData {
  1103. jsbytecode *pc; /* in: program counter resulting in us matching */
  1104. uintN flags; /* inout: mode and flag bits, see below */
  1105. uintN optarg; /* in: index of optional flags argument */
  1106. JSString *str; /* out: 'this' parameter object as string */
  1107. JSRegExp *regexp; /* out: regexp parameter object private data */
  1108. } GlobData;
  1109. /*
  1110. * Mode and flag bit definitions for match_or_replace's GlobData.flags field.
  1111. */
  1112. #define MODE_MATCH 0x00 /* in: return match array on success */
  1113. #define MODE_REPLACE 0x01 /* in: match and replace */
  1114. #define MODE_SEARCH 0x02 /* in: search only, return match index or -1 */
  1115. #define GET_MODE(f) ((f) & 0x03)
  1116. #define FORCE_FLAT 0x04 /* in: force flat (non-regexp) string match */
  1117. #define KEEP_REGEXP 0x08 /* inout: keep GlobData.regexp alive for caller
  1118. of match_or_replace; if set on input
  1119. but clear on output, regexp ownership
  1120. does not pass to caller */
  1121. #define GLOBAL_REGEXP 0x10 /* out: regexp had the 'g' flag */
  1122. static JSBool
  1123. match_or_replace(JSContext *cx,
  1124. JSBool (*glob)(JSContext *cx, jsint count, GlobData *data),
  1125. void (*destroy)(JSContext *cx, GlobData *data),
  1126. GlobData *data, uintN argc, jsval *vp)
  1127. {
  1128. JSString *str, *src, *opt;
  1129. JSObject *reobj;
  1130. JSRegExp *re;
  1131. size_t index, length;
  1132. JSBool ok, test;
  1133. jsint count;
  1134. NORMALIZE_THIS(cx, vp, str);
  1135. data->str = str;
  1136. if (argc != 0 && VALUE_IS_REGEXP(cx, vp[2])) {
  1137. reobj = JSVAL_TO_OBJECT(vp[2]);
  1138. re = (JSRegExp *) JS_GetPrivate(cx, reobj);
  1139. } else {
  1140. src = ArgToRootedString(cx, argc, vp, 0);
  1141. if (!src)
  1142. return JS_FALSE;
  1143. if (data->optarg < argc) {
  1144. opt = js_ValueToString(cx, vp[2 + data->optarg]);
  1145. if (!opt)
  1146. return JS_FALSE;
  1147. } else {
  1148. opt = NULL;
  1149. }
  1150. re = js_NewRegExpOpt(cx, src, opt, (data->flags & FORCE_FLAT) != 0);
  1151. if (!re)
  1152. return JS_FALSE;
  1153. reobj = NULL;
  1154. }
  1155. /* From here on, all control flow must reach the matching DROP. */
  1156. data->regexp = re;
  1157. HOLD_REGEXP(cx, re);
  1158. if (re->flags & JSREG_GLOB)
  1159. data->flags |= GLOBAL_REGEXP;
  1160. index = 0;
  1161. if (GET_MODE(data->flags) == MODE_SEARCH) {
  1162. ok = js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, vp);
  1163. if (ok) {
  1164. *vp = (*vp == JSVAL_TRUE)
  1165. ? INT_TO_JSVAL(cx->regExpStatics.leftContext.length)
  1166. : INT_TO_JSVAL(-1);
  1167. }
  1168. } else if (data->flags & GLOBAL_REGEXP) {
  1169. if (reobj) {
  1170. /* Set the lastIndex property's reserved slot to 0. */
  1171. ok = js_SetLastIndex(cx, reobj, 0);
  1172. } else {
  1173. ok = JS_TRUE;
  1174. }
  1175. if (ok) {
  1176. length = JSSTRING_LENGTH(str);
  1177. for (count = 0; index <= length; count++) {
  1178. ok = js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, vp);
  1179. if (!ok || *vp != JSVAL_TRUE)
  1180. break;
  1181. ok = glob(cx, count, data);
  1182. if (!ok)
  1183. break;
  1184. if (cx->regExpStatics.lastMatch.length == 0) {
  1185. if (index == length)
  1186. break;
  1187. index++;
  1188. }
  1189. }
  1190. if (!ok && destroy)
  1191. destroy(cx, data);
  1192. }
  1193. } else {
  1194. if (GET_MODE(data->flags) == MODE_REPLACE) {
  1195. test = JS_TRUE;
  1196. } else {
  1197. /*
  1198. * MODE_MATCH implies str_match is being called from a script or a
  1199. * scripted function. If the caller cares only about testing null
  1200. * vs. non-null return value, optimize away the array object that
  1201. * would normally be returned in *vp.
  1202. *
  1203. * Assume a full array result is required, then prove otherwise.
  1204. */
  1205. test = JS_FALSE;
  1206. if (data->pc && (*data->pc == JSOP_CALL || *data->pc == JSOP_NEW)) {
  1207. JS_ASSERT(js_CodeSpec[*data->pc].length == 3);
  1208. switch (data->pc[3]) {
  1209. case JSOP_POP:
  1210. case JSOP_IFEQ:
  1211. case JSOP_IFNE:
  1212. case JSOP_IFEQX:
  1213. case JSOP_IFNEX:
  1214. test = JS_TRUE;
  1215. break;
  1216. default:;
  1217. }
  1218. }
  1219. }
  1220. ok = js_ExecuteRegExp(cx, re, str, &index, test, vp);
  1221. }
  1222. DROP_REGEXP(cx, re);
  1223. if (reobj) {
  1224. /* Tell our caller that it doesn't need to destroy data->regexp. */
  1225. data->flags &= ~KEEP_REGEXP;
  1226. } else if (!ok || !(data->flags & KEEP_REGEXP)) {
  1227. /* Caller didn't want to keep data->regexp, so null and destroy it. */
  1228. data->regexp = NULL;
  1229. js_DestroyRegExp(cx, re);
  1230. }
  1231. return ok;
  1232. }
  1233. typedef struct MatchData {
  1234. GlobData base;
  1235. jsval *arrayval; /* NB: local root pointer */
  1236. } MatchData;
  1237. static JSBool
  1238. match_glob(JSContext *cx, jsint count, GlobData *data)
  1239. {
  1240. MatchData *mdata;
  1241. JSObject *arrayobj;
  1242. JSSubString *matchsub;
  1243. JSString *matchstr;
  1244. jsval v;
  1245. mdata = (MatchData *)data;
  1246. arrayobj = JSVAL_TO_OBJECT(*mdata->arrayval);
  1247. if (!arrayobj) {
  1248. arrayobj = js_ConstructObject(cx, &js_ArrayClass, NULL, NULL, 0, NULL);
  1249. if (!arrayobj)
  1250. return JS_FALSE;
  1251. *mdata->arrayval = OBJECT_TO_JSVAL(arrayobj);
  1252. }
  1253. matchsub = &cx->regExpStatics.lastMatch;
  1254. matchstr = js_NewStringCopyN(cx, matchsub->chars, matchsub->length);
  1255. if (!matchstr)
  1256. return JS_FALSE;
  1257. v = STRING_TO_JSVAL(matchstr);
  1258. JS_ASSERT(count <= JSVAL_INT_MAX);
  1259. JSAutoResolveFlags rf(cx, JSRESOLVE_QUALIFIED | JSRESOLVE_ASSIGNING);
  1260. return OBJ_SET_PROPERTY(cx, arrayobj, INT_TO_JSID(count), &v);
  1261. }
  1262. JSBool
  1263. js_StringMatchHelper(JSContext *cx, uintN argc, jsval *vp, jsbytecode *pc)
  1264. {
  1265. JSTempValueRooter tvr;
  1266. MatchData mdata;
  1267. JSBool ok;
  1268. JS_PUSH_SINGLE_TEMP_ROOT(cx, JSVAL_NULL, &tvr);
  1269. mdata.base.pc = pc;
  1270. mdata.base.flags = MODE_MATCH;
  1271. mdata.base.optarg = 1;
  1272. mdata.arrayval = &tvr.u.value;
  1273. ok = match_or_replace(cx, match_glob, NULL, &mdata.base, argc, vp);
  1274. if (ok && !JSVAL_IS_NULL(*mdata.arrayval))
  1275. *vp = *mdata.arrayval;
  1276. JS_POP_TEMP_ROOT(cx, &tvr);
  1277. return ok;
  1278. }
  1279. static JSBool
  1280. str_match(JSContext *cx, uintN argc, jsval *vp)
  1281. {
  1282. JSStackFrame *fp;
  1283. for (fp = cx->fp; fp && !fp->regs; fp = fp->down)
  1284. JS_ASSERT(!fp->script);
  1285. return js_StringMatchHelper(cx, argc, vp, fp ? fp->regs->pc : NULL);
  1286. }
  1287. #ifdef JS_TRACER
  1288. static JSObject* FASTCALL
  1289. String_p_match(JSContext* cx, JSString* str, jsbytecode *pc, JSObject* regexp)
  1290. {
  1291. jsval vp[3] = { JSVAL_NULL, STRING_TO_JSVAL(str), OBJECT_TO_JSVAL(regexp) };
  1292. if (!js_StringMatchHelper(cx, 1, vp, pc))
  1293. return (JSObject*) JSVAL_TO_BOOLEAN(JSVAL_VOID);
  1294. JS_ASSERT(JSVAL_IS_NULL(vp[0]) ||
  1295. (!JSVAL_IS_PRIMITIVE(vp[0]) && OBJ_IS_ARRAY(cx, JSVAL_TO_OBJECT(vp[0]))));
  1296. return JSVAL_TO_OBJECT(vp[0]);
  1297. }
  1298. static JSObject* FASTCALL
  1299. String_p_match_obj(JSContext* cx, JSObject* str, jsbytecode *pc, JSObject* regexp)
  1300. {
  1301. jsval vp[3] = { JSVAL_NULL, OBJECT_TO_JSVAL(str), OBJECT_TO_JSVAL(regexp) };
  1302. if (!js_StringMatchHelper(cx, 1, vp, pc))
  1303. return (JSObject*) JSVAL_TO_BOOLEAN(JSVAL_VOID);
  1304. JS_ASSERT(JSVAL_IS_NULL(vp[0]) ||
  1305. (!JSVAL_IS_PRIMITIVE(vp[0]) && OBJ_IS_ARRAY(cx, JSVAL_TO_OBJECT(vp[0]))));
  1306. return JSVAL_TO_OBJECT(vp[0]);
  1307. }
  1308. #endif
  1309. static JSBool
  1310. str_search(JSContext *cx, uintN argc, jsval *vp)
  1311. {
  1312. GlobData data;
  1313. data.flags = MODE_SEARCH;
  1314. data.optarg = 1;
  1315. return match_or_replace(cx, NULL, NULL, &data, argc, vp);
  1316. }
  1317. typedef struct ReplaceData {
  1318. GlobData base; /* base struct state */
  1319. JSObject *lambda; /* replacement function object or null */
  1320. JSString *repstr; /* replacement string */
  1321. jschar *dollar; /* null or pointer to first $ in repstr */
  1322. jschar *dollarEnd; /* limit pointer for js_strchr_limit */
  1323. jschar *chars; /* result chars, null initially */
  1324. size_t length; /* result length, 0 initially */
  1325. jsint index; /* index in result of next replacement */
  1326. jsint leftIndex; /* left context index in base.str->chars */
  1327. JSSubString dollarStr; /* for "$$" interpret_dollar result */
  1328. } ReplaceData;
  1329. static JSSubString *
  1330. interpret_dollar(JSContext *cx, jschar *dp, jschar *ep, ReplaceData *rdata,
  1331. size_t *skip)
  1332. {
  1333. JSRegExpStatics *res;
  1334. jschar dc, *cp;
  1335. uintN num, tmp;
  1336. JS_ASSERT(*dp == '$');
  1337. /* If there is only a dollar, bail now */
  1338. if (dp + 1 >= ep)
  1339. return NULL;
  1340. /* Interpret all Perl match-induced dollar variables. */
  1341. res = &cx->regExpStatics;
  1342. dc = dp[1];
  1343. if (JS7_ISDEC(dc)) {
  1344. /* ECMA-262 Edition 3: 1-9 or 01-99 */
  1345. num = JS7_UNDEC(dc);
  1346. if (num > res->parenCount)
  1347. return NULL;
  1348. cp = dp + 2;
  1349. if (cp < ep && (dc = *cp, JS7_ISDEC(dc))) {
  1350. tmp = 10 * num + JS7_UNDEC(dc);
  1351. if (tmp <= res->parenCount) {
  1352. cp++;
  1353. num = tmp;
  1354. }
  1355. }
  1356. if (num == 0)
  1357. return NULL;
  1358. /* Adjust num from 1 $n-origin to 0 array-index-origin. */
  1359. num--;
  1360. *skip = cp - dp;
  1361. return REGEXP_PAREN_SUBSTRING(res, num);
  1362. }
  1363. *skip = 2;
  1364. switch (dc) {
  1365. case '$':
  1366. rdata->dollarStr.chars = dp;
  1367. rdata->dollarStr.length = 1;
  1368. return &rdata->dollarStr;
  1369. case '&':
  1370. return &res->lastMatch;
  1371. case '+':
  1372. return &res->lastParen;
  1373. case '`':
  1374. return &res->leftContext;
  1375. case '\'':
  1376. return &res->rightContext;
  1377. }
  1378. return NULL;
  1379. }
  1380. static JSBool
  1381. find_replen(JSContext *cx, ReplaceData *rdata, size_t *sizep)
  1382. {
  1383. JSString *repstr;
  1384. size_t replen, skip;
  1385. jschar *dp, *ep;
  1386. JSSubString *sub;
  1387. JSObject *lambda;
  1388. lambda = rdata->lambda;
  1389. if (lambda) {
  1390. uintN argc, i, j, m, n, p;
  1391. jsval *invokevp, *sp;
  1392. void *mark;
  1393. JSBool ok;
  1394. /*
  1395. * Save the regExpStatics from the current regexp, since they may be
  1396. * clobbered by a RegExp usage in the lambda function. Note that all
  1397. * members of JSRegExpStatics are JSSubStrings, so not GC roots, save
  1398. * input, which is rooted otherwise via vp[1] in str_replace.
  1399. */
  1400. JSRegExpStatics save = cx->regExpStatics;
  1401. JSBool freeMoreParens = JS_FALSE;
  1402. /*
  1403. * In the lambda case, not only do we find the replacement string's
  1404. * length, we compute repstr and return it via rdata for use within
  1405. * do_replace. The lambda is called with arguments ($&, $1, $2, ...,
  1406. * index, input), i.e., all the properties of a regexp match array.
  1407. * For $&, etc., we must create string jsvals from cx->regExpStatics.
  1408. * We grab up stack space to keep the newborn strings GC-rooted.
  1409. */
  1410. p = rdata->base.regexp->parenCount;
  1411. argc = 1 + p + 2;
  1412. invokevp = js_AllocStack(cx, 2 + argc, &mark);
  1413. if (!invokevp)
  1414. return JS_FALSE;
  1415. /* Push lambda and its 'this' parameter. */
  1416. sp = invokevp;
  1417. *sp++ = OBJECT_TO_JSVAL(lambda);
  1418. *sp++ = OBJECT_TO_JSVAL(OBJ_GET_PARENT(cx, lambda));
  1419. #define PUSH_REGEXP_STATIC(sub) \
  1420. JS_BEGIN_MACRO \
  1421. JSString *str = js_NewStringCopyN(cx, \
  1422. cx->regExpStatics.sub.chars, \
  1423. cx->regExpStatics.sub.length); \
  1424. if (!str) { \
  1425. ok = JS_FALSE; \
  1426. goto lambda_out; \
  1427. } \
  1428. *sp++ = STRING_TO_JSVAL(str); \
  1429. JS_END_MACRO
  1430. /* Push $&, $1, $2, ... */
  1431. PUSH_REGEXP_STATIC(lastMatch);
  1432. i = 0;
  1433. m = cx->regExpStatics.parenCount;
  1434. n = JS_MIN(m, 9);
  1435. for (j = 0; i < n; i++, j++)
  1436. PUSH_REGEXP_STATIC(parens[j]);
  1437. for (j = 0; i < m; i++, j++)
  1438. PUSH_REGEXP_STATIC(moreParens[j]);
  1439. /*
  1440. * We need to clear moreParens in the top-of-stack cx->regExpStatics
  1441. * to it won't be possibly realloc'ed, leaving the bottom-of-stack
  1442. * moreParens pointing to freed memory.
  1443. */
  1444. cx->regExpStatics.moreParens = NULL;
  1445. freeMoreParens = JS_TRUE;
  1446. #undef PUSH_REGEXP_STATIC
  1447. /* Make sure to push undefined for any unmatched parens. */
  1448. for (; i < p; i++)
  1449. *sp++ = JSVAL_VOID;
  1450. /* Push match index and input string. */
  1451. *sp++ = INT_TO_JSVAL((jsint)cx->regExpStatics.leftContext.length);
  1452. *sp++ = STRING_TO_JSVAL(rdata->base.str);
  1453. ok = js_Invoke(cx, argc, invokevp, 0);
  1454. if (ok) {
  1455. /*
  1456. * NB: we count on the newborn string root to hold any string
  1457. * created by this js_ValueToString that would otherwise be GC-
  1458. * able, until we use rdata->repstr in do_replace.
  1459. */
  1460. repstr = js_ValueToString(cx, *invokevp);
  1461. if (!repstr) {
  1462. ok = JS_FALSE;
  1463. } else {
  1464. rdata->repstr = repstr;
  1465. *sizep = JSSTRING_LENGTH(repstr);
  1466. }
  1467. }
  1468. lambda_out:
  1469. js_FreeStack(cx, mark);
  1470. if (freeMoreParens)
  1471. JS_free(cx, cx->regExpStatics.moreParens);
  1472. cx->regExpStatics = save;
  1473. return ok;
  1474. }
  1475. repstr = rdata->repstr;
  1476. replen = JSSTRING_LENGTH(repstr);
  1477. for (dp = rdata->dollar, ep = rdata->dollarEnd; dp;
  1478. dp = js_strchr_limit(dp, '$', ep)) {
  1479. sub = interpret_dollar(cx, dp, ep, rdata, &skip);
  1480. if (sub) {
  1481. replen += sub->length - skip;
  1482. dp += skip;
  1483. }
  1484. else
  1485. dp++;
  1486. }
  1487. *sizep = replen;
  1488. return JS_TRUE;
  1489. }
  1490. static void
  1491. do_replace(JSContext *cx, ReplaceData *rdata, jschar *chars)
  1492. {
  1493. JSString *repstr;
  1494. jschar *bp, *cp, *dp, *ep;
  1495. size_t len, skip;
  1496. JSSubString *sub;
  1497. repstr = rdata->repstr;
  1498. bp = cp = JSSTRING_CHARS(repstr);
  1499. for (dp = rdata->dollar, ep = rdata->dollarEnd; dp;
  1500. dp = js_strchr_limit(dp, '$', ep)) {
  1501. len = dp - cp;
  1502. js_strncpy(chars, cp, len);
  1503. chars += len;
  1504. cp = dp;
  1505. sub = interpret_dollar(cx, dp, ep, rdata, &skip);
  1506. if (sub) {
  1507. len = sub->length;
  1508. js_strncpy(chars, sub->chars, len);
  1509. chars += len;
  1510. cp += skip;
  1511. dp += skip;
  1512. } else {
  1513. dp++;
  1514. }
  1515. }
  1516. js_strncpy(chars, cp, JSSTRING_LENGTH(repstr) - (

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