PageRenderTime 55ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 1ms

/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
  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) - (cp - bp));
  1517. }
  1518. static void
  1519. replace_destroy(JSContext *cx, GlobData *data)
  1520. {
  1521. ReplaceData *rdata;
  1522. rdata = (ReplaceData *)data;
  1523. JS_free(cx, rdata->chars);
  1524. rdata->chars = NULL;
  1525. }
  1526. static JSBool
  1527. replace_glob(JSContext *cx, jsint count, GlobData *data)
  1528. {
  1529. ReplaceData *rdata;
  1530. JSString *str;
  1531. size_t leftoff, leftlen, replen, growth;
  1532. const jschar *left;
  1533. jschar *chars;
  1534. rdata = (ReplaceData *)data;
  1535. str = data->str;
  1536. leftoff = rdata->leftIndex;
  1537. left = JSSTRING_CHARS(str) + leftoff;
  1538. leftlen = cx->regExpStatics.lastMatch.chars - left;
  1539. rdata->leftIndex = cx->regExpStatics.lastMatch.chars - JSSTRING_CHARS(str);
  1540. rdata->leftIndex += cx->regExpStatics.lastMatch.length;
  1541. if (!find_replen(cx, rdata, &replen))
  1542. return JS_FALSE;
  1543. growth = leftlen + replen;
  1544. chars = (jschar *)
  1545. (rdata->chars
  1546. ? JS_realloc(cx, rdata->chars, (rdata->length + growth + 1)
  1547. * sizeof(jschar))
  1548. : JS_malloc(cx, (growth + 1) * sizeof(jschar)));
  1549. if (!chars)
  1550. return JS_FALSE;
  1551. rdata->chars = chars;
  1552. rdata->length += growth;
  1553. chars += rdata->index;
  1554. rdata->index += growth;
  1555. js_strncpy(chars, left, leftlen);
  1556. chars += leftlen;
  1557. do_replace(cx, rdata, chars);
  1558. return JS_TRUE;
  1559. }
  1560. static JSBool
  1561. str_replace(JSContext *cx, uintN argc, jsval *vp)
  1562. {
  1563. JSObject *lambda;
  1564. JSString *repstr;
  1565. if (argc >= 2 && JS_TypeOfValue(cx, vp[3]) == JSTYPE_FUNCTION) {
  1566. lambda = JSVAL_TO_OBJECT(vp[3]);
  1567. repstr = NULL;
  1568. } else {
  1569. lambda = NULL;
  1570. repstr = ArgToRootedString(cx, argc, vp, 1);
  1571. if (!repstr)
  1572. return JS_FALSE;
  1573. }
  1574. return js_StringReplaceHelper(cx, argc, lambda, repstr, vp);
  1575. }
  1576. #ifdef JS_TRACER
  1577. static JSString* FASTCALL
  1578. String_p_replace_str(JSContext* cx, JSString* str, JSObject* regexp, JSString* repstr)
  1579. {
  1580. jsval vp[4] = {
  1581. JSVAL_NULL, STRING_TO_JSVAL(str), OBJECT_TO_JSVAL(regexp), STRING_TO_JSVAL(repstr)
  1582. };
  1583. if (!js_StringReplaceHelper(cx, 2, NULL, repstr, vp))
  1584. return NULL;
  1585. JS_ASSERT(JSVAL_IS_STRING(vp[0]));
  1586. return JSVAL_TO_STRING(vp[0]);
  1587. }
  1588. static JSString* FASTCALL
  1589. String_p_replace_str2(JSContext* cx, JSString* str, JSString* patstr, JSString* repstr)
  1590. {
  1591. jsval vp[4] = {
  1592. JSVAL_NULL, STRING_TO_JSVAL(str), STRING_TO_JSVAL(patstr), STRING_TO_JSVAL(repstr)
  1593. };
  1594. if (!js_StringReplaceHelper(cx, 2, NULL, repstr, vp))
  1595. return NULL;
  1596. JS_ASSERT(JSVAL_IS_STRING(vp[0]));
  1597. return JSVAL_TO_STRING(vp[0]);
  1598. }
  1599. static JSString* FASTCALL
  1600. String_p_replace_str3(JSContext* cx, JSString* str, JSString* patstr, JSString* repstr,
  1601. JSString* flagstr)
  1602. {
  1603. jsval vp[5] = {
  1604. JSVAL_NULL, STRING_TO_JSVAL(str), STRING_TO_JSVAL(patstr), STRING_TO_JSVAL(repstr),
  1605. STRING_TO_JSVAL(flagstr)
  1606. };
  1607. if (!js_StringReplaceHelper(cx, 3, NULL, repstr, vp))
  1608. return NULL;
  1609. JS_ASSERT(JSVAL_IS_STRING(vp[0]));
  1610. return JSVAL_TO_STRING(vp[0]);
  1611. }
  1612. #endif
  1613. JSBool
  1614. js_StringReplaceHelper(JSContext *cx, uintN argc, JSObject *lambda,
  1615. JSString *repstr, jsval *vp)
  1616. {
  1617. ReplaceData rdata;
  1618. JSBool ok;
  1619. size_t leftlen, rightlen, length;
  1620. jschar *chars;
  1621. JSString *str;
  1622. /*
  1623. * For ECMA Edition 3, the first argument is to be converted to a string
  1624. * to match in a "flat" sense (without regular expression metachars having
  1625. * special meanings) UNLESS the first arg is a RegExp object.
  1626. */
  1627. rdata.base.flags = MODE_REPLACE | KEEP_REGEXP | FORCE_FLAT;
  1628. rdata.base.optarg = 2;
  1629. rdata.lambda = lambda;
  1630. rdata.repstr = repstr;
  1631. if (repstr) {
  1632. rdata.dollarEnd = JSSTRING_CHARS(repstr) + JSSTRING_LENGTH(repstr);
  1633. rdata.dollar = js_strchr_limit(JSSTRING_CHARS(repstr), '$',
  1634. rdata.dollarEnd);
  1635. } else {
  1636. rdata.dollar = rdata.dollarEnd = NULL;
  1637. }
  1638. rdata.chars = NULL;
  1639. rdata.length = 0;
  1640. rdata.index = 0;
  1641. rdata.leftIndex = 0;
  1642. ok = match_or_replace(cx, replace_glob, replace_destroy, &rdata.base,
  1643. argc, vp);
  1644. if (!ok)
  1645. return JS_FALSE;
  1646. if (!rdata.chars) {
  1647. if ((rdata.base.flags & GLOBAL_REGEXP) || *vp != JSVAL_TRUE) {
  1648. /* Didn't match even once. */
  1649. *vp = STRING_TO_JSVAL(rdata.base.str);
  1650. goto out;
  1651. }
  1652. leftlen = cx->regExpStatics.leftContext.length;
  1653. ok = find_replen(cx, &rdata, &length);
  1654. if (!ok)
  1655. goto out;
  1656. length += leftlen;
  1657. chars = (jschar *) JS_malloc(cx, (length + 1) * sizeof(jschar));
  1658. if (!chars) {
  1659. ok = JS_FALSE;
  1660. goto out;
  1661. }
  1662. js_strncpy(chars, cx->regExpStatics.leftContext.chars, leftlen);
  1663. do_replace(cx, &rdata, chars + leftlen);
  1664. rdata.chars = chars;
  1665. rdata.length = length;
  1666. }
  1667. rightlen = cx->regExpStatics.rightContext.length;
  1668. length = rdata.length + rightlen;
  1669. chars = (jschar *)
  1670. JS_realloc(cx, rdata.chars, (length + 1) * sizeof(jschar));
  1671. if (!chars) {
  1672. JS_free(cx, rdata.chars);
  1673. ok = JS_FALSE;
  1674. goto out;
  1675. }
  1676. js_strncpy(chars + rdata.length, cx->regExpStatics.rightContext.chars,
  1677. rightlen);
  1678. chars[length] = 0;
  1679. str = js_NewString(cx, chars, length);
  1680. if (!str) {
  1681. JS_free(cx, chars);
  1682. ok = JS_FALSE;
  1683. goto out;
  1684. }
  1685. *vp = STRING_TO_JSVAL(str);
  1686. out:
  1687. /* If KEEP_REGEXP is still set, it's our job to destroy regexp now. */
  1688. if (rdata.base.flags & KEEP_REGEXP)
  1689. js_DestroyRegExp(cx, rdata.base.regexp);
  1690. return ok;
  1691. }
  1692. /*
  1693. * Subroutine used by str_split to find the next split point in str, starting
  1694. * at offset *ip and looking either for the separator substring given by sep, or
  1695. * for the next re match. In the re case, return the matched separator in *sep,
  1696. * and the possibly updated offset in *ip.
  1697. *
  1698. * Return -2 on error, -1 on end of string, >= 0 for a valid index of the next
  1699. * separator occurrence if found, or str->length if no separator is found.
  1700. */
  1701. static jsint
  1702. find_split(JSContext *cx, JSString *str, JSRegExp *re, jsint *ip,
  1703. JSSubString *sep)
  1704. {
  1705. jsint i, j, k;
  1706. size_t length;
  1707. jschar *chars;
  1708. /*
  1709. * Stop if past end of string. If at end of string, we will compare the
  1710. * null char stored there (by js_NewString*) to sep->chars[j] in the while
  1711. * loop at the end of this function, so that
  1712. *
  1713. * "ab,".split(',') => ["ab", ""]
  1714. *
  1715. * and the resulting array converts back to the string "ab," for symmetry.
  1716. * However, we ape Perl and do this only if there is a sufficiently large
  1717. * limit argument (see str_split).
  1718. */
  1719. i = *ip;
  1720. length = JSSTRING_LENGTH(str);
  1721. if ((size_t)i > length)
  1722. return -1;
  1723. chars = JSSTRING_CHARS(str);
  1724. /*
  1725. * Match a regular expression against the separator at or above index i.
  1726. * Call js_ExecuteRegExp with true for the test argument. On successful
  1727. * match, get the separator from cx->regExpStatics.lastMatch.
  1728. */
  1729. if (re) {
  1730. size_t index;
  1731. jsval rval;
  1732. again:
  1733. /* JS1.2 deviated from Perl by never matching at end of string. */
  1734. index = (size_t)i;
  1735. if (!js_ExecuteRegExp(cx, re, str, &index, JS_TRUE, &rval))
  1736. return -2;
  1737. if (rval != JSVAL_TRUE) {
  1738. /* Mismatch: ensure our caller advances i past end of string. */
  1739. sep->length = 1;
  1740. return length;
  1741. }
  1742. i = (jsint)index;
  1743. *sep = cx->regExpStatics.lastMatch;
  1744. if (sep->length == 0) {
  1745. /*
  1746. * Empty string match: never split on an empty match at the start
  1747. * of a find_split cycle. Same rule as for an empty global match
  1748. * in match_or_replace.
  1749. */
  1750. if (i == *ip) {
  1751. /*
  1752. * "Bump-along" to avoid sticking at an empty match, but don't
  1753. * bump past end of string -- our caller must do that by adding
  1754. * sep->length to our return value.
  1755. */
  1756. if ((size_t)i == length)
  1757. return -1;
  1758. i++;
  1759. goto again;
  1760. }
  1761. if ((size_t)i == length) {
  1762. /*
  1763. * If there was a trivial zero-length match at the end of the
  1764. * split, then we shouldn't output the matched string at the end
  1765. * of the split array. See ECMA-262 Ed. 3, 15.5.4.14, Step 15.
  1766. */
  1767. sep->chars = NULL;
  1768. }
  1769. }
  1770. JS_ASSERT((size_t)i >= sep->length);
  1771. return i - sep->length;
  1772. }
  1773. /*
  1774. * Special case: if sep is the empty string, split str into one character
  1775. * substrings. Let our caller worry about whether to split once at end of
  1776. * string into an empty substring.
  1777. */
  1778. if (sep->length == 0)
  1779. return ((size_t)i == length) ? -1 : i + 1;
  1780. /*
  1781. * Now that we know sep is non-empty, search starting at i in str for an
  1782. * occurrence of all of sep's chars. If we find them, return the index of
  1783. * the first separator char. Otherwise, return length.
  1784. */
  1785. j = 0;
  1786. while ((size_t)(k = i + j) < length) {
  1787. if (chars[k] == sep->chars[j]) {
  1788. if ((size_t)++j == sep->length)
  1789. return i;
  1790. } else {
  1791. i++;
  1792. j = 0;
  1793. }
  1794. }
  1795. return k;
  1796. }
  1797. static JSBool
  1798. str_split(JSContext *cx, uintN argc, jsval *vp)
  1799. {
  1800. JSString *str, *sub;
  1801. JSObject *arrayobj;
  1802. jsval v;
  1803. JSBool ok, limited;
  1804. JSRegExp *re;
  1805. JSSubString *sep, tmp;
  1806. jsdouble d;
  1807. jsint i, j;
  1808. uint32 len, limit;
  1809. NORMALIZE_THIS(cx, vp, str);
  1810. arrayobj = js_ConstructObject(cx, &js_ArrayClass, NULL, NULL, 0, NULL);
  1811. if (!arrayobj)
  1812. return JS_FALSE;
  1813. *vp = OBJECT_TO_JSVAL(arrayobj);
  1814. if (argc == 0) {
  1815. v = STRING_TO_JSVAL(str);
  1816. ok = OBJ_SET_PROPERTY(cx, arrayobj, INT_TO_JSID(0), &v);
  1817. } else {
  1818. if (VALUE_IS_REGEXP(cx, vp[2])) {
  1819. re = (JSRegExp *) JS_GetPrivate(cx, JSVAL_TO_OBJECT(vp[2]));
  1820. sep = &tmp;
  1821. /* Set a magic value so we can detect a successful re match. */
  1822. sep->chars = NULL;
  1823. sep->length = 0;
  1824. } else {
  1825. JSString *str2 = js_ValueToString(cx, vp[2]);
  1826. if (!str2)
  1827. return JS_FALSE;
  1828. vp[2] = STRING_TO_JSVAL(str2);
  1829. /*
  1830. * Point sep at a local copy of str2's header because find_split
  1831. * will modify sep->length.
  1832. */
  1833. JSSTRING_CHARS_AND_LENGTH(str2, tmp.chars, tmp.length);
  1834. sep = &tmp;
  1835. re = NULL;
  1836. }
  1837. /* Use the second argument as the split limit, if given. */
  1838. limited = (argc > 1) && !JSVAL_IS_VOID(vp[3]);
  1839. limit = 0; /* Avoid warning. */
  1840. if (limited) {
  1841. d = js_ValueToNumber(cx, &vp[3]);
  1842. if (JSVAL_IS_NULL(vp[3]))
  1843. return JS_FALSE;
  1844. /* Clamp limit between 0 and 1 + string length. */
  1845. limit = js_DoubleToECMAUint32(d);
  1846. if (limit > JSSTRING_LENGTH(str))
  1847. limit = 1 + JSSTRING_LENGTH(str);
  1848. }
  1849. len = i = 0;
  1850. while ((j = find_split(cx, str, re, &i, sep)) >= 0) {
  1851. if (limited && len >= limit)
  1852. break;
  1853. sub = js_NewDependentString(cx, str, i, (size_t)(j - i));
  1854. if (!sub)
  1855. return JS_FALSE;
  1856. v = STRING_TO_JSVAL(sub);
  1857. if (!JS_SetElement(cx, arrayobj, len, &v))
  1858. return JS_FALSE;
  1859. len++;
  1860. /*
  1861. * Imitate perl's feature of including parenthesized substrings
  1862. * that matched part of the delimiter in the new array, after the
  1863. * split substring that was delimited.
  1864. */
  1865. if (re && sep->chars) {
  1866. uintN num;
  1867. JSSubString *parsub;
  1868. for (num = 0; num < cx->regExpStatics.parenCount; num++) {
  1869. if (limited && len >= limit)
  1870. break;
  1871. parsub = REGEXP_PAREN_SUBSTRING(&cx->regExpStatics, num);
  1872. sub = js_NewStringCopyN(cx, parsub->chars, parsub->length);
  1873. if (!sub)
  1874. return JS_FALSE;
  1875. v = STRING_TO_JSVAL(sub);
  1876. if (!JS_SetElement(cx, arrayobj, len, &v))
  1877. return JS_FALSE;
  1878. len++;
  1879. }
  1880. sep->chars = NULL;
  1881. }
  1882. i = j + sep->length;
  1883. }
  1884. ok = (j != -2);
  1885. }
  1886. return ok;
  1887. }
  1888. #ifdef JS_TRACER
  1889. static JSObject* FASTCALL
  1890. String_p_split(JSContext* cx, JSString* str, JSString* sepstr)
  1891. {
  1892. // FIXME: Avoid building and then parsing this array.
  1893. jsval vp[4] = { JSVAL_NULL, STRING_TO_JSVAL(str), STRING_TO_JSVAL(sepstr), JSVAL_VOID };
  1894. if (!str_split(cx, 2, vp))
  1895. return NULL;
  1896. JS_ASSERT(JSVAL_IS_OBJECT(vp[0]));
  1897. return JSVAL_TO_OBJECT(vp[0]);
  1898. }
  1899. #endif
  1900. #if JS_HAS_PERL_SUBSTR
  1901. static JSBool
  1902. str_substr(JSContext *cx, uintN argc, jsval *vp)
  1903. {
  1904. JSString *str;
  1905. jsdouble d;
  1906. jsdouble length, begin, end;
  1907. NORMALIZE_THIS(cx, vp, str);
  1908. if (argc != 0) {
  1909. d = js_ValueToNumber(cx, &vp[2]);
  1910. if (JSVAL_IS_NULL(vp[2]))
  1911. return JS_FALSE;
  1912. length = JSSTRING_LENGTH(str);
  1913. begin = js_DoubleToInteger(d);
  1914. if (begin < 0) {
  1915. begin += length;
  1916. if (begin < 0)
  1917. begin = 0;
  1918. } else if (begin > length) {
  1919. begin = length;
  1920. }
  1921. if (argc == 1) {
  1922. end = length;
  1923. } else {
  1924. d = js_ValueToNumber(cx, &vp[3]);
  1925. if (JSVAL_IS_NULL(vp[3]))
  1926. return JS_FALSE;
  1927. end = js_DoubleToInteger(d);
  1928. if (end < 0)
  1929. end = 0;
  1930. end += begin;
  1931. if (end > length)
  1932. end = length;
  1933. }
  1934. str = js_NewDependentString(cx, str,
  1935. (size_t)begin,
  1936. (size_t)(end - begin));
  1937. if (!str)
  1938. return JS_FALSE;
  1939. }
  1940. *vp = STRING_TO_JSVAL(str);
  1941. return JS_TRUE;
  1942. }
  1943. #endif /* JS_HAS_PERL_SUBSTR */
  1944. /*
  1945. * Python-esque sequence operations.
  1946. */
  1947. static JSBool
  1948. str_concat(JSContext *cx, uintN argc, jsval *vp)
  1949. {
  1950. JSString *str, *str2;
  1951. jsval *argv;
  1952. uintN i;
  1953. NORMALIZE_THIS(cx, vp, str);
  1954. for (i = 0, argv = vp + 2; i < argc; i++) {
  1955. str2 = js_ValueToString(cx, argv[i]);
  1956. if (!str2)
  1957. return JS_FALSE;
  1958. argv[i] = STRING_TO_JSVAL(str2);
  1959. str = js_ConcatStrings(cx, str, str2);
  1960. if (!str)
  1961. return JS_FALSE;
  1962. }
  1963. *vp = STRING_TO_JSVAL(str);
  1964. return JS_TRUE;
  1965. }
  1966. #ifdef JS_TRACER
  1967. static JSString* FASTCALL
  1968. String_p_concat_1int(JSContext* cx, JSString* str, int32 i)
  1969. {
  1970. // FIXME: should be able to use stack buffer and avoid istr...
  1971. JSString* istr = js_NumberToString(cx, i);
  1972. if (!istr)
  1973. return NULL;
  1974. return js_ConcatStrings(cx, str, istr);
  1975. }
  1976. static JSString* FASTCALL
  1977. String_p_concat_2str(JSContext* cx, JSString* str, JSString* a, JSString* b)
  1978. {
  1979. str = js_ConcatStrings(cx, str, a);
  1980. if (str)
  1981. return js_ConcatStrings(cx, str, b);
  1982. r