/js/src/builtin/RegExp.cpp

http://github.com/zpao/v8monkey · C++ · 655 lines · 434 code · 104 blank · 117 comment · 93 complexity · 6de26900acefed57bfcb952248245d5a MD5 · raw file

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
  2. * vim: set ts=8 sw=4 et tw=99 ft=cpp:
  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 SpiderMonkey JavaScript code.
  18. *
  19. * The Initial Developer of the Original Code is
  20. * the Mozilla Foundation.
  21. * Portions created by the Initial Developer are Copyright (C) 2011
  22. * the Initial Developer. All Rights Reserved.
  23. *
  24. * Contributor(s):
  25. * Chris Leary <cdleary@mozilla.com>
  26. *
  27. * Alternatively, the contents of this file may be used under the terms of
  28. * either the GNU General Public License Version 2 or later (the "GPL"), or
  29. * 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. #include "jscntxt.h"
  41. #include "builtin/RegExp.h"
  42. #include "vm/RegExpObject-inl.h"
  43. #include "vm/RegExpStatics-inl.h"
  44. using namespace js;
  45. using namespace js::types;
  46. class RegExpMatchBuilder
  47. {
  48. JSContext * const cx;
  49. JSObject * const array;
  50. bool setProperty(JSAtom *name, Value v) {
  51. return !!js_DefineProperty(cx, array, ATOM_TO_JSID(name), &v,
  52. JS_PropertyStub, JS_StrictPropertyStub, JSPROP_ENUMERATE);
  53. }
  54. public:
  55. RegExpMatchBuilder(JSContext *cx, JSObject *array) : cx(cx), array(array) {}
  56. bool append(uint32_t index, Value v) {
  57. JS_ASSERT(!array->getOps()->getElement);
  58. return !!js_DefineElement(cx, array, index, &v, JS_PropertyStub, JS_StrictPropertyStub,
  59. JSPROP_ENUMERATE);
  60. }
  61. bool setIndex(int index) {
  62. return setProperty(cx->runtime->atomState.indexAtom, Int32Value(index));
  63. }
  64. bool setInput(JSString *str) {
  65. JS_ASSERT(str);
  66. return setProperty(cx->runtime->atomState.inputAtom, StringValue(str));
  67. }
  68. };
  69. static bool
  70. CreateRegExpMatchResult(JSContext *cx, JSString *input, const jschar *chars, size_t length,
  71. MatchPairs *matchPairs, Value *rval)
  72. {
  73. /*
  74. * Create the (slow) result array for a match.
  75. *
  76. * Array contents:
  77. * 0: matched string
  78. * 1..pairCount-1: paren matches
  79. * input: input string
  80. * index: start index for the match
  81. */
  82. JSObject *array = NewSlowEmptyArray(cx);
  83. if (!array)
  84. return false;
  85. if (!input) {
  86. input = js_NewStringCopyN(cx, chars, length);
  87. if (!input)
  88. return false;
  89. }
  90. RegExpMatchBuilder builder(cx, array);
  91. for (size_t i = 0; i < matchPairs->pairCount(); ++i) {
  92. MatchPair pair = matchPairs->pair(i);
  93. JSString *captured;
  94. if (pair.isUndefined()) {
  95. JS_ASSERT(i != 0); /* Since we had a match, first pair must be present. */
  96. if (!builder.append(i, UndefinedValue()))
  97. return false;
  98. } else {
  99. captured = js_NewDependentString(cx, input, pair.start, pair.length());
  100. if (!captured || !builder.append(i, StringValue(captured)))
  101. return false;
  102. }
  103. }
  104. if (!builder.setIndex(matchPairs->pair(0).start) || !builder.setInput(input))
  105. return false;
  106. *rval = ObjectValue(*array);
  107. return true;
  108. }
  109. template <class T>
  110. bool
  111. ExecuteRegExpImpl(JSContext *cx, RegExpStatics *res, T &re, JSLinearString *input,
  112. const jschar *chars, size_t length,
  113. size_t *lastIndex, RegExpExecType type, Value *rval)
  114. {
  115. LifoAllocScope allocScope(&cx->tempLifoAlloc());
  116. MatchPairs *matchPairs = NULL;
  117. RegExpRunStatus status = re.execute(cx, chars, length, lastIndex, &matchPairs);
  118. switch (status) {
  119. case RegExpRunStatus_Error:
  120. return false;
  121. case RegExpRunStatus_Success_NotFound:
  122. *rval = NullValue();
  123. return true;
  124. default:
  125. JS_ASSERT(status == RegExpRunStatus_Success);
  126. JS_ASSERT(matchPairs);
  127. }
  128. if (res)
  129. res->updateFromMatchPairs(cx, input, matchPairs);
  130. *lastIndex = matchPairs->pair(0).limit;
  131. if (type == RegExpTest) {
  132. *rval = BooleanValue(true);
  133. return true;
  134. }
  135. return CreateRegExpMatchResult(cx, input, chars, length, matchPairs, rval);
  136. }
  137. bool
  138. js::ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpShared &shared, JSLinearString *input,
  139. const jschar *chars, size_t length,
  140. size_t *lastIndex, RegExpExecType type, Value *rval)
  141. {
  142. return ExecuteRegExpImpl(cx, res, shared, input, chars, length, lastIndex, type, rval);
  143. }
  144. bool
  145. js::ExecuteRegExp(JSContext *cx, RegExpStatics *res, RegExpObject &reobj, JSLinearString *input,
  146. const jschar *chars, size_t length,
  147. size_t *lastIndex, RegExpExecType type, Value *rval)
  148. {
  149. return ExecuteRegExpImpl(cx, res, reobj, input, chars, length, lastIndex, type, rval);
  150. }
  151. /* Note: returns the original if no escaping need be performed. */
  152. static JSAtom *
  153. EscapeNakedForwardSlashes(JSContext *cx, JSAtom *unescaped)
  154. {
  155. size_t oldLen = unescaped->length();
  156. const jschar *oldChars = unescaped->chars();
  157. JS::Anchor<JSString *> anchor(unescaped);
  158. /* We may never need to use |sb|. Start using it lazily. */
  159. StringBuffer sb(cx);
  160. for (const jschar *it = oldChars; it < oldChars + oldLen; ++it) {
  161. if (*it == '/' && (it == oldChars || it[-1] != '\\')) {
  162. /* There's a forward slash that needs escaping. */
  163. if (sb.empty()) {
  164. /* This is the first one we've seen, copy everything up to this point. */
  165. if (!sb.reserve(oldLen + 1))
  166. return NULL;
  167. sb.infallibleAppend(oldChars, size_t(it - oldChars));
  168. }
  169. if (!sb.append('\\'))
  170. return NULL;
  171. }
  172. if (!sb.empty() && !sb.append(*it))
  173. return NULL;
  174. }
  175. return sb.empty() ? unescaped : sb.finishAtom();
  176. }
  177. /*
  178. * Compile a new |RegExpShared| for the |RegExpObject|.
  179. *
  180. * Per ECMAv5 15.10.4.1, we act on combinations of (pattern, flags) as
  181. * arguments:
  182. *
  183. * RegExp, undefined => flags := pattern.flags
  184. * RegExp, _ => throw TypeError
  185. * _ => pattern := ToString(pattern) if defined(pattern) else ''
  186. * flags := ToString(flags) if defined(flags) else ''
  187. */
  188. static bool
  189. CompileRegExpObject(JSContext *cx, RegExpObjectBuilder &builder, CallArgs args)
  190. {
  191. if (args.length() == 0) {
  192. RegExpStatics *res = cx->regExpStatics();
  193. RegExpObject *reobj = builder.build(cx->runtime->emptyString, res->getFlags());
  194. if (!reobj)
  195. return false;
  196. args.rval() = ObjectValue(*reobj);
  197. return true;
  198. }
  199. Value sourceValue = args[0];
  200. /*
  201. * If we get passed in an object whose internal [[Class]] property is
  202. * "RegExp", return a new object with the same source/flags.
  203. */
  204. if (IsObjectWithClass(sourceValue, ESClass_RegExp, cx)) {
  205. /*
  206. * Beware, sourceObj may be a (transparent) proxy to a RegExp, so only
  207. * use generic (proxyable) operations on sourceObj that do not assume
  208. * sourceObj.isRegExp().
  209. */
  210. JSObject &sourceObj = sourceValue.toObject();
  211. if (args.length() >= 2 && !args[1].isUndefined()) {
  212. JS_ReportErrorNumber(cx, js_GetErrorMessage, NULL, JSMSG_NEWREGEXP_FLAGGED);
  213. return false;
  214. }
  215. /*
  216. * Only extract the 'flags' out of sourceObj; do not reuse the
  217. * RegExpShared since it may be from a different compartment.
  218. */
  219. RegExpFlag flags;
  220. {
  221. RegExpShared *shared = RegExpToShared(cx, sourceObj);
  222. if (!shared)
  223. return false;
  224. flags = shared->getFlags();
  225. }
  226. /*
  227. * 'toSource' is a permanent read-only property, so this is equivalent
  228. * to executing RegExpObject::getSource on the unwrapped object.
  229. */
  230. Value v;
  231. if (!sourceObj.getProperty(cx, cx->runtime->atomState.sourceAtom, &v))
  232. return false;
  233. RegExpObject *reobj = builder.build(&v.toString()->asAtom(), flags);
  234. if (!reobj)
  235. return false;
  236. args.rval() = ObjectValue(*reobj);
  237. return true;
  238. }
  239. JSAtom *source;
  240. if (sourceValue.isUndefined()) {
  241. source = cx->runtime->emptyString;
  242. } else {
  243. /* Coerce to string and compile. */
  244. JSString *str = ToString(cx, sourceValue);
  245. if (!str)
  246. return false;
  247. source = js_AtomizeString(cx, str);
  248. if (!source)
  249. return false;
  250. }
  251. RegExpFlag flags = RegExpFlag(0);
  252. if (args.length() > 1 && !args[1].isUndefined()) {
  253. JSString *flagStr = ToString(cx, args[1]);
  254. if (!flagStr)
  255. return false;
  256. args[1].setString(flagStr);
  257. if (!ParseRegExpFlags(cx, flagStr, &flags))
  258. return false;
  259. }
  260. JSAtom *escapedSourceStr = EscapeNakedForwardSlashes(cx, source);
  261. if (!escapedSourceStr)
  262. return false;
  263. if (!js::detail::RegExpCode::checkSyntax(cx, NULL, escapedSourceStr))
  264. return false;
  265. RegExpStatics *res = cx->regExpStatics();
  266. RegExpObject *reobj = builder.build(escapedSourceStr, RegExpFlag(flags | res->getFlags()));
  267. if (!reobj)
  268. return NULL;
  269. args.rval() = ObjectValue(*reobj);
  270. return true;
  271. }
  272. static JSBool
  273. regexp_compile(JSContext *cx, uintN argc, Value *vp)
  274. {
  275. CallArgs args = CallArgsFromVp(argc, vp);
  276. bool ok;
  277. JSObject *obj = NonGenericMethodGuard(cx, args, regexp_compile, &RegExpClass, &ok);
  278. if (!obj)
  279. return ok;
  280. RegExpObjectBuilder builder(cx, &obj->asRegExp());
  281. return CompileRegExpObject(cx, builder, args);
  282. }
  283. static JSBool
  284. regexp_construct(JSContext *cx, uintN argc, Value *vp)
  285. {
  286. CallArgs args = CallArgsFromVp(argc, vp);
  287. if (!IsConstructing(args)) {
  288. /*
  289. * If first arg is regexp and no flags are given, just return the arg.
  290. * Otherwise, delegate to the standard constructor.
  291. * See ECMAv5 15.10.3.1.
  292. */
  293. if (args.length() >= 1 && IsObjectWithClass(args[0], ESClass_RegExp, cx) &&
  294. (args.length() == 1 || args[1].isUndefined()))
  295. {
  296. args.rval() = args[0];
  297. return true;
  298. }
  299. }
  300. RegExpObjectBuilder builder(cx);
  301. return CompileRegExpObject(cx, builder, args);
  302. }
  303. static JSBool
  304. regexp_toString(JSContext *cx, uintN argc, Value *vp)
  305. {
  306. CallArgs args = CallArgsFromVp(argc, vp);
  307. bool ok;
  308. JSObject *obj = NonGenericMethodGuard(cx, args, regexp_toString, &RegExpClass, &ok);
  309. if (!obj)
  310. return ok;
  311. JSString *str = obj->asRegExp().toString(cx);
  312. if (!str)
  313. return false;
  314. *vp = StringValue(str);
  315. return true;
  316. }
  317. static JSFunctionSpec regexp_methods[] = {
  318. #if JS_HAS_TOSOURCE
  319. JS_FN(js_toSource_str, regexp_toString, 0,0),
  320. #endif
  321. JS_FN(js_toString_str, regexp_toString, 0,0),
  322. JS_FN("compile", regexp_compile, 2,0),
  323. JS_FN("exec", regexp_exec, 1,0),
  324. JS_FN("test", regexp_test, 1,0),
  325. JS_FS_END
  326. };
  327. /*
  328. * RegExp static properties.
  329. *
  330. * RegExp class static properties and their Perl counterparts:
  331. *
  332. * RegExp.input $_
  333. * RegExp.multiline $*
  334. * RegExp.lastMatch $&
  335. * RegExp.lastParen $+
  336. * RegExp.leftContext $`
  337. * RegExp.rightContext $'
  338. */
  339. #define DEFINE_STATIC_GETTER(name, code) \
  340. static JSBool \
  341. name(JSContext *cx, JSObject *obj, jsid id, jsval *vp) \
  342. { \
  343. RegExpStatics *res = cx->regExpStatics(); \
  344. code; \
  345. }
  346. DEFINE_STATIC_GETTER(static_input_getter, return res->createPendingInput(cx, vp))
  347. DEFINE_STATIC_GETTER(static_multiline_getter, *vp = BOOLEAN_TO_JSVAL(res->multiline());
  348. return true)
  349. DEFINE_STATIC_GETTER(static_lastMatch_getter, return res->createLastMatch(cx, vp))
  350. DEFINE_STATIC_GETTER(static_lastParen_getter, return res->createLastParen(cx, vp))
  351. DEFINE_STATIC_GETTER(static_leftContext_getter, return res->createLeftContext(cx, vp))
  352. DEFINE_STATIC_GETTER(static_rightContext_getter, return res->createRightContext(cx, vp))
  353. DEFINE_STATIC_GETTER(static_paren1_getter, return res->createParen(cx, 1, vp))
  354. DEFINE_STATIC_GETTER(static_paren2_getter, return res->createParen(cx, 2, vp))
  355. DEFINE_STATIC_GETTER(static_paren3_getter, return res->createParen(cx, 3, vp))
  356. DEFINE_STATIC_GETTER(static_paren4_getter, return res->createParen(cx, 4, vp))
  357. DEFINE_STATIC_GETTER(static_paren5_getter, return res->createParen(cx, 5, vp))
  358. DEFINE_STATIC_GETTER(static_paren6_getter, return res->createParen(cx, 6, vp))
  359. DEFINE_STATIC_GETTER(static_paren7_getter, return res->createParen(cx, 7, vp))
  360. DEFINE_STATIC_GETTER(static_paren8_getter, return res->createParen(cx, 8, vp))
  361. DEFINE_STATIC_GETTER(static_paren9_getter, return res->createParen(cx, 9, vp))
  362. #define DEFINE_STATIC_SETTER(name, code) \
  363. static JSBool \
  364. name(JSContext *cx, JSObject *obj, jsid id, JSBool strict, jsval *vp) \
  365. { \
  366. RegExpStatics *res = cx->regExpStatics(); \
  367. code; \
  368. return true; \
  369. }
  370. DEFINE_STATIC_SETTER(static_input_setter,
  371. if (!JSVAL_IS_STRING(*vp) && !JS_ConvertValue(cx, *vp, JSTYPE_STRING, vp))
  372. return false;
  373. res->setPendingInput(JSVAL_TO_STRING(*vp)))
  374. DEFINE_STATIC_SETTER(static_multiline_setter,
  375. if (!JSVAL_IS_BOOLEAN(*vp) && !JS_ConvertValue(cx, *vp, JSTYPE_BOOLEAN, vp))
  376. return false;
  377. res->setMultiline(cx, !!JSVAL_TO_BOOLEAN(*vp)))
  378. const uint8_t REGEXP_STATIC_PROP_ATTRS = JSPROP_PERMANENT | JSPROP_SHARED | JSPROP_ENUMERATE;
  379. const uint8_t RO_REGEXP_STATIC_PROP_ATTRS = REGEXP_STATIC_PROP_ATTRS | JSPROP_READONLY;
  380. const uint8_t HIDDEN_PROP_ATTRS = JSPROP_PERMANENT | JSPROP_SHARED;
  381. const uint8_t RO_HIDDEN_PROP_ATTRS = HIDDEN_PROP_ATTRS | JSPROP_READONLY;
  382. static JSPropertySpec regexp_static_props[] = {
  383. {"input", 0, REGEXP_STATIC_PROP_ATTRS, static_input_getter, static_input_setter},
  384. {"multiline", 0, REGEXP_STATIC_PROP_ATTRS, static_multiline_getter,
  385. static_multiline_setter},
  386. {"lastMatch", 0, RO_REGEXP_STATIC_PROP_ATTRS, static_lastMatch_getter, NULL},
  387. {"lastParen", 0, RO_REGEXP_STATIC_PROP_ATTRS, static_lastParen_getter, NULL},
  388. {"leftContext", 0, RO_REGEXP_STATIC_PROP_ATTRS, static_leftContext_getter, NULL},
  389. {"rightContext", 0, RO_REGEXP_STATIC_PROP_ATTRS, static_rightContext_getter, NULL},
  390. {"$1", 0, RO_REGEXP_STATIC_PROP_ATTRS, static_paren1_getter, NULL},
  391. {"$2", 0, RO_REGEXP_STATIC_PROP_ATTRS, static_paren2_getter, NULL},
  392. {"$3", 0, RO_REGEXP_STATIC_PROP_ATTRS, static_paren3_getter, NULL},
  393. {"$4", 0, RO_REGEXP_STATIC_PROP_ATTRS, static_paren4_getter, NULL},
  394. {"$5", 0, RO_REGEXP_STATIC_PROP_ATTRS, static_paren5_getter, NULL},
  395. {"$6", 0, RO_REGEXP_STATIC_PROP_ATTRS, static_paren6_getter, NULL},
  396. {"$7", 0, RO_REGEXP_STATIC_PROP_ATTRS, static_paren7_getter, NULL},
  397. {"$8", 0, RO_REGEXP_STATIC_PROP_ATTRS, static_paren8_getter, NULL},
  398. {"$9", 0, RO_REGEXP_STATIC_PROP_ATTRS, static_paren9_getter, NULL},
  399. {"$_", 0, HIDDEN_PROP_ATTRS, static_input_getter, static_input_setter},
  400. {"$*", 0, HIDDEN_PROP_ATTRS, static_multiline_getter, static_multiline_setter},
  401. {"$&", 0, RO_HIDDEN_PROP_ATTRS, static_lastMatch_getter, NULL},
  402. {"$+", 0, RO_HIDDEN_PROP_ATTRS, static_lastParen_getter, NULL},
  403. {"$`", 0, RO_HIDDEN_PROP_ATTRS, static_leftContext_getter, NULL},
  404. {"$'", 0, RO_HIDDEN_PROP_ATTRS, static_rightContext_getter, NULL},
  405. {0,0,0,0,0}
  406. };
  407. JSObject *
  408. js_InitRegExpClass(JSContext *cx, JSObject *obj)
  409. {
  410. JS_ASSERT(obj->isNative());
  411. GlobalObject *global = &obj->asGlobal();
  412. JSObject *proto = global->createBlankPrototype(cx, &RegExpClass);
  413. if (!proto)
  414. return NULL;
  415. proto->setPrivate(NULL);
  416. RegExpObject *reproto = &proto->asRegExp();
  417. RegExpObjectBuilder builder(cx, reproto);
  418. if (!builder.build(cx->runtime->emptyString, RegExpFlag(0)))
  419. return NULL;
  420. if (!DefinePropertiesAndBrand(cx, proto, NULL, regexp_methods))
  421. return NULL;
  422. JSFunction *ctor = global->createConstructor(cx, regexp_construct, &RegExpClass,
  423. CLASS_ATOM(cx, RegExp), 2);
  424. if (!ctor)
  425. return NULL;
  426. if (!LinkConstructorAndPrototype(cx, ctor, proto))
  427. return NULL;
  428. /* Add static properties to the RegExp constructor. */
  429. if (!JS_DefineProperties(cx, ctor, regexp_static_props))
  430. return NULL;
  431. /* Capture normal data properties pregenerated for RegExp objects. */
  432. TypeObject *type = proto->getNewType(cx);
  433. if (!type)
  434. return NULL;
  435. AddTypeProperty(cx, type, "source", Type::StringType());
  436. AddTypeProperty(cx, type, "global", Type::BooleanType());
  437. AddTypeProperty(cx, type, "ignoreCase", Type::BooleanType());
  438. AddTypeProperty(cx, type, "multiline", Type::BooleanType());
  439. AddTypeProperty(cx, type, "sticky", Type::BooleanType());
  440. AddTypeProperty(cx, type, "lastIndex", Type::Int32Type());
  441. if (!DefineConstructorAndPrototype(cx, global, JSProto_RegExp, ctor, proto))
  442. return NULL;
  443. return proto;
  444. }
  445. static const jschar GreedyStarChars[] = {'.', '*'};
  446. static inline bool
  447. StartsWithGreedyStar(JSAtom *source)
  448. {
  449. return false;
  450. #if 0
  451. if (source->length() < 3)
  452. return false;
  453. const jschar *chars = source->chars();
  454. return chars[0] == GreedyStarChars[0] &&
  455. chars[1] == GreedyStarChars[1] &&
  456. chars[2] != '?';
  457. #endif
  458. }
  459. static inline RegExpShared *
  460. GetSharedForGreedyStar(JSContext *cx, JSAtom *source, RegExpFlag flags)
  461. {
  462. if (RegExpShared *hit = cx->compartment->regExps.lookupHack(cx, source, flags))
  463. return hit;
  464. JSAtom *hackedSource = js_AtomizeChars(cx, source->chars() + ArrayLength(GreedyStarChars),
  465. source->length() - ArrayLength(GreedyStarChars));
  466. if (!hackedSource)
  467. return NULL;
  468. return cx->compartment->regExps.getHack(cx, source, hackedSource, flags);
  469. }
  470. /*
  471. * ES5 15.10.6.2 (and 15.10.6.3, which calls 15.10.6.2).
  472. *
  473. * RegExp.prototype.test doesn't need to create a results array, and we use
  474. * |execType| to perform this optimization.
  475. */
  476. static bool
  477. ExecuteRegExp(JSContext *cx, Native native, uintN argc, Value *vp)
  478. {
  479. CallArgs args = CallArgsFromVp(argc, vp);
  480. /* Step 1. */
  481. bool ok;
  482. JSObject *obj = NonGenericMethodGuard(cx, args, native, &RegExpClass, &ok);
  483. if (!obj)
  484. return ok;
  485. RegExpObject &reobj = obj->asRegExp();
  486. RegExpShared *shared;
  487. if (StartsWithGreedyStar(reobj.getSource()))
  488. shared = GetSharedForGreedyStar(cx, reobj.getSource(), reobj.getFlags());
  489. else
  490. shared = reobj.getShared(cx);
  491. if (!shared)
  492. return false;
  493. RegExpShared::Guard re(*shared);
  494. RegExpStatics *res = cx->regExpStatics();
  495. /* Step 2. */
  496. JSString *input = ToString(cx, (args.length() > 0) ? args[0] : UndefinedValue());
  497. if (!input)
  498. return false;
  499. /* Step 3. */
  500. JSLinearString *linearInput = input->ensureLinear(cx);
  501. if (!linearInput)
  502. return false;
  503. const jschar *chars = linearInput->chars();
  504. size_t length = input->length();
  505. /* Step 4. */
  506. const Value &lastIndex = reobj.getLastIndex();
  507. /* Step 5. */
  508. jsdouble i;
  509. if (!ToInteger(cx, lastIndex, &i))
  510. return false;
  511. /* Steps 6-7 (with sticky extension). */
  512. if (!re->global() && !re->sticky())
  513. i = 0;
  514. /* Step 9a. */
  515. if (i < 0 || i > length) {
  516. reobj.zeroLastIndex();
  517. args.rval() = NullValue();
  518. return true;
  519. }
  520. /* Steps 8-21. */
  521. RegExpExecType execType = (native == regexp_test) ? RegExpTest : RegExpExec;
  522. size_t lastIndexInt(i);
  523. if (!ExecuteRegExp(cx, res, *re, linearInput, chars, length, &lastIndexInt, execType,
  524. &args.rval())) {
  525. return false;
  526. }
  527. /* Step 11 (with sticky extension). */
  528. if (re->global() || (!args.rval().isNull() && re->sticky())) {
  529. if (args.rval().isNull())
  530. reobj.zeroLastIndex();
  531. else
  532. reobj.setLastIndex(lastIndexInt);
  533. }
  534. return true;
  535. }
  536. /* ES5 15.10.6.2. */
  537. JSBool
  538. js::regexp_exec(JSContext *cx, uintN argc, Value *vp)
  539. {
  540. return ExecuteRegExp(cx, regexp_exec, argc, vp);
  541. }
  542. /* ES5 15.10.6.3. */
  543. JSBool
  544. js::regexp_test(JSContext *cx, uintN argc, Value *vp)
  545. {
  546. if (!ExecuteRegExp(cx, regexp_test, argc, vp))
  547. return false;
  548. if (!vp->isTrue())
  549. vp->setBoolean(false);
  550. return true;
  551. }