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 :
41 : #include "frontend/TokenStream.h"
42 : #include "vm/MatchPairs.h"
43 : #include "vm/RegExpStatics.h"
44 : #include "vm/StringBuffer.h"
45 : #include "vm/Xdr.h"
46 :
47 : #include "jsobjinlines.h"
48 :
49 : #include "vm/RegExpObject-inl.h"
50 : #include "vm/RegExpStatics-inl.h"
51 :
52 : using namespace js;
53 : using js::detail::RegExpCode;
54 :
55 : JS_STATIC_ASSERT(IgnoreCaseFlag == JSREG_FOLD);
56 : JS_STATIC_ASSERT(GlobalFlag == JSREG_GLOB);
57 : JS_STATIC_ASSERT(MultilineFlag == JSREG_MULTILINE);
58 : JS_STATIC_ASSERT(StickyFlag == JSREG_STICKY);
59 :
60 : /* RegExpObjectBuilder */
61 :
62 203348 : RegExpObjectBuilder::RegExpObjectBuilder(JSContext *cx, RegExpObject *reobj)
63 203348 : : cx(cx), reobj_(reobj)
64 203348 : {}
65 :
66 : bool
67 203213 : RegExpObjectBuilder::getOrCreate()
68 : {
69 203213 : if (reobj_)
70 198789 : return true;
71 :
72 4424 : JSObject *obj = NewBuiltinClassInstance(cx, &RegExpClass);
73 4424 : if (!obj)
74 0 : return false;
75 4424 : obj->initPrivate(NULL);
76 :
77 4424 : reobj_ = &obj->asRegExp();
78 4424 : return true;
79 : }
80 :
81 : bool
82 197121 : RegExpObjectBuilder::getOrCreateClone(RegExpObject *proto)
83 : {
84 197121 : JS_ASSERT(!reobj_);
85 :
86 197121 : JSObject *clone = NewObjectWithGivenProto(cx, &RegExpClass, proto, proto->getParent());
87 197121 : if (!clone)
88 0 : return false;
89 197121 : clone->initPrivate(NULL);
90 :
91 197121 : reobj_ = &clone->asRegExp();
92 197121 : return true;
93 : }
94 :
95 : RegExpObject *
96 196671 : RegExpObjectBuilder::build(JSAtom *source, RegExpShared &shared)
97 : {
98 196671 : if (!getOrCreate())
99 0 : return NULL;
100 :
101 196671 : if (!reobj_->init(cx, source, shared.getFlags()))
102 0 : return NULL;
103 :
104 196671 : reobj_->setShared(cx, shared);
105 196671 : return reobj_;
106 : }
107 :
108 : RegExpObject *
109 6542 : RegExpObjectBuilder::build(JSAtom *source, RegExpFlag flags)
110 : {
111 6542 : if (!getOrCreate())
112 0 : return NULL;
113 :
114 6542 : return reobj_->init(cx, source, flags) ? reobj_ : NULL;
115 : }
116 :
117 : RegExpObject *
118 197121 : RegExpObjectBuilder::clone(RegExpObject *other, RegExpObject *proto)
119 : {
120 197121 : if (!getOrCreateClone(proto))
121 0 : return NULL;
122 :
123 : /*
124 : * Check that the RegExpShared for the original is okay to use in
125 : * the clone -- if the |RegExpStatics| provides more flags we'll
126 : * need a different |RegExpShared|.
127 : */
128 197121 : RegExpStatics *res = cx->regExpStatics();
129 197121 : RegExpFlag origFlags = other->getFlags();
130 197121 : RegExpFlag staticsFlags = res->getFlags();
131 197121 : if ((origFlags & staticsFlags) != staticsFlags) {
132 450 : RegExpFlag newFlags = RegExpFlag(origFlags | staticsFlags);
133 450 : return build(other->getSource(), newFlags);
134 : }
135 :
136 393342 : RegExpGuard g;
137 196671 : if (!other->getShared(cx, &g))
138 0 : return NULL;
139 :
140 196671 : return build(other->getSource(), *g);
141 : }
142 :
143 : /* MatchPairs */
144 :
145 : MatchPairs *
146 3051568 : MatchPairs::create(LifoAlloc &alloc, size_t pairCount, size_t backingPairCount)
147 : {
148 3051568 : void *mem = alloc.alloc(calculateSize(backingPairCount));
149 3051568 : if (!mem)
150 0 : return NULL;
151 :
152 3051568 : return new (mem) MatchPairs(pairCount);
153 : }
154 :
155 : inline void
156 2253934 : MatchPairs::checkAgainst(size_t inputLength)
157 : {
158 : #if DEBUG
159 6335822 : for (size_t i = 0; i < pairCount(); ++i) {
160 4081888 : MatchPair p = pair(i);
161 4081888 : p.check();
162 4081888 : if (p.isUndefined())
163 984213 : continue;
164 3097675 : JS_ASSERT(size_t(p.limit) <= inputLength);
165 : }
166 : #endif
167 2253934 : }
168 :
169 : /* detail::RegExpCode */
170 :
171 : #if ENABLE_YARR_JIT
172 : void
173 90 : RegExpCode::reportYarrError(JSContext *cx, TokenStream *ts, ErrorCode error)
174 : {
175 90 : switch (error) {
176 : case JSC::Yarr::NoError:
177 0 : JS_NOT_REACHED("Called reportYarrError with value for no error");
178 : return;
179 : #define COMPILE_EMSG(__code, __msg) \
180 : case JSC::Yarr::__code: \
181 : if (ts) \
182 : ReportCompileErrorNumber(cx, ts, NULL, JSREPORT_ERROR, __msg); \
183 : else \
184 : JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, __msg); \
185 : return
186 0 : COMPILE_EMSG(PatternTooLarge, JSMSG_REGEXP_TOO_COMPLEX);
187 0 : COMPILE_EMSG(QuantifierOutOfOrder, JSMSG_BAD_QUANTIFIER);
188 9 : COMPILE_EMSG(QuantifierWithoutAtom, JSMSG_BAD_QUANTIFIER);
189 0 : COMPILE_EMSG(MissingParentheses, JSMSG_MISSING_PAREN);
190 0 : COMPILE_EMSG(ParenthesesUnmatched, JSMSG_UNMATCHED_RIGHT_PAREN);
191 0 : COMPILE_EMSG(ParenthesesTypeInvalid, JSMSG_BAD_QUANTIFIER); /* "(?" with bad next char */
192 0 : COMPILE_EMSG(CharacterClassUnmatched, JSMSG_BAD_CLASS_RANGE);
193 45 : COMPILE_EMSG(CharacterClassInvalidRange, JSMSG_BAD_CLASS_RANGE);
194 9 : COMPILE_EMSG(CharacterClassOutOfOrder, JSMSG_BAD_CLASS_RANGE);
195 9 : COMPILE_EMSG(QuantifierTooLarge, JSMSG_BAD_QUANTIFIER);
196 18 : COMPILE_EMSG(EscapeUnterminated, JSMSG_TRAILING_SLASH);
197 : #undef COMPILE_EMSG
198 : default:
199 0 : JS_NOT_REACHED("Unknown Yarr error code");
200 : }
201 : }
202 :
203 : #else /* !ENABLE_YARR_JIT */
204 :
205 : void
206 : RegExpCode::reportPCREError(JSContext *cx, int error)
207 : {
208 : #define REPORT(msg_) \
209 : JS_ReportErrorFlagsAndNumberUC(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL, msg_); \
210 : return
211 : switch (error) {
212 : case -2: REPORT(JSMSG_REGEXP_TOO_COMPLEX);
213 : case 0: JS_NOT_REACHED("Precondition violation: an error must have occurred.");
214 : case 1: REPORT(JSMSG_TRAILING_SLASH);
215 : case 2: REPORT(JSMSG_TRAILING_SLASH);
216 : case 3: REPORT(JSMSG_REGEXP_TOO_COMPLEX);
217 : case 4: REPORT(JSMSG_BAD_QUANTIFIER);
218 : case 5: REPORT(JSMSG_BAD_QUANTIFIER);
219 : case 6: REPORT(JSMSG_BAD_CLASS_RANGE);
220 : case 7: REPORT(JSMSG_REGEXP_TOO_COMPLEX);
221 : case 8: REPORT(JSMSG_BAD_CLASS_RANGE);
222 : case 9: REPORT(JSMSG_BAD_QUANTIFIER);
223 : case 10: REPORT(JSMSG_UNMATCHED_RIGHT_PAREN);
224 : case 11: REPORT(JSMSG_REGEXP_TOO_COMPLEX);
225 : case 12: REPORT(JSMSG_UNMATCHED_RIGHT_PAREN);
226 : case 13: REPORT(JSMSG_REGEXP_TOO_COMPLEX);
227 : case 14: REPORT(JSMSG_MISSING_PAREN);
228 : case 15: REPORT(JSMSG_BAD_BACKREF);
229 : case 16: REPORT(JSMSG_REGEXP_TOO_COMPLEX);
230 : case 17: REPORT(JSMSG_REGEXP_TOO_COMPLEX);
231 : default:
232 : JS_NOT_REACHED("Precondition violation: unknown PCRE error code.");
233 : }
234 : #undef REPORT
235 : }
236 :
237 : #endif /* ENABLE_YARR_JIT */
238 :
239 : bool
240 24064 : RegExpCode::compile(JSContext *cx, JSLinearString &pattern, unsigned *parenCount, RegExpFlag flags)
241 : {
242 : #if ENABLE_YARR_JIT
243 : /* Parse the pattern. */
244 : ErrorCode yarrError;
245 : YarrPattern yarrPattern(pattern, bool(flags & IgnoreCaseFlag), bool(flags & MultilineFlag),
246 48128 : &yarrError);
247 24064 : if (yarrError) {
248 0 : reportYarrError(cx, NULL, yarrError);
249 0 : return false;
250 : }
251 24064 : *parenCount = yarrPattern.m_numSubpatterns;
252 :
253 : /*
254 : * The YARR JIT compiler attempts to compile the parsed pattern. If
255 : * it cannot, it informs us via |codeBlock.isFallBack()|, in which
256 : * case we have to bytecode compile it.
257 : */
258 :
259 : #ifdef JS_METHODJIT
260 24064 : if (isJITRuntimeEnabled(cx) && !yarrPattern.m_containsBackreferences) {
261 24046 : JSC::ExecutableAllocator *execAlloc = cx->runtime->getExecutableAllocator(cx);
262 24046 : if (!execAlloc) {
263 0 : js_ReportOutOfMemory(cx);
264 0 : return false;
265 : }
266 :
267 24046 : JSGlobalData globalData(execAlloc);
268 24046 : jitCompile(yarrPattern, &globalData, codeBlock);
269 24046 : if (!codeBlock.isFallBack())
270 23884 : return true;
271 : }
272 : #endif
273 :
274 180 : WTF::BumpPointerAllocator *bumpAlloc = cx->runtime->getBumpPointerAllocator(cx);
275 180 : if (!bumpAlloc) {
276 0 : js_ReportOutOfMemory(cx);
277 0 : return false;
278 : }
279 :
280 180 : codeBlock.setFallBack(true);
281 180 : byteCode = byteCompile(yarrPattern, bumpAlloc).get();
282 180 : return true;
283 : #else /* !defined(ENABLE_YARR_JIT) */
284 : int error = 0;
285 : compiled = jsRegExpCompile(pattern.chars(), pattern.length(),
286 : ignoreCase() ? JSRegExpIgnoreCase : JSRegExpDoNotIgnoreCase,
287 : multiline() ? JSRegExpMultiline : JSRegExpSingleLine,
288 : parenCount, &error);
289 : if (error) {
290 : reportPCREError(cx, error);
291 : return false;
292 : }
293 : return true;
294 : #endif
295 : }
296 :
297 : RegExpRunStatus
298 3051568 : RegExpCode::execute(JSContext *cx, const jschar *chars, size_t length, size_t start,
299 : int *output, size_t outputCount)
300 : {
301 : int result;
302 : #if ENABLE_YARR_JIT
303 : (void) cx; /* Unused. */
304 3051568 : if (codeBlock.isFallBack())
305 57033 : result = JSC::Yarr::interpret(byteCode, chars, start, length, output);
306 : else
307 2994535 : result = JSC::Yarr::execute(codeBlock, chars, start, length, output);
308 : #else
309 : result = jsRegExpExecute(cx, compiled, chars, length, start, output, outputCount);
310 : #endif
311 :
312 3051568 : if (result == -1)
313 797634 : return RegExpRunStatus_Success_NotFound;
314 :
315 : #if !ENABLE_YARR_JIT
316 : if (result < 0) {
317 : reportPCREError(cx, result);
318 : return RegExpRunStatus_Error;
319 : }
320 : #endif
321 :
322 2253934 : JS_ASSERT(result >= 0);
323 2253934 : return RegExpRunStatus_Success;
324 : }
325 :
326 : /* RegExpObject */
327 :
328 : static void
329 5486 : regexp_trace(JSTracer *trc, JSObject *obj)
330 : {
331 : /*
332 : * We have to check both conditions, since:
333 : * 1. During TraceRuntime, gcRunning is set
334 : * 2. When a write barrier executes, IS_GC_MARKING_TRACER is true.
335 : */
336 5486 : if (trc->runtime->gcRunning && IS_GC_MARKING_TRACER(trc))
337 5333 : obj->setPrivate(NULL);
338 5486 : }
339 :
340 : Class js::RegExpClass = {
341 : js_RegExp_str,
342 : JSCLASS_HAS_PRIVATE | JSCLASS_IMPLEMENTS_BARRIERS |
343 : JSCLASS_HAS_RESERVED_SLOTS(RegExpObject::RESERVED_SLOTS) |
344 : JSCLASS_HAS_CACHED_PROTO(JSProto_RegExp),
345 : JS_PropertyStub, /* addProperty */
346 : JS_PropertyStub, /* delProperty */
347 : JS_PropertyStub, /* getProperty */
348 : JS_StrictPropertyStub, /* setProperty */
349 : JS_EnumerateStub, /* enumerate */
350 : JS_ResolveStub,
351 : JS_ConvertStub,
352 : NULL, /* finalize */
353 : NULL, /* checkAccess */
354 : NULL, /* call */
355 : NULL, /* construct */
356 : NULL, /* hasInstance */
357 : regexp_trace
358 : };
359 :
360 24064 : RegExpShared::RegExpShared(JSRuntime *rt, RegExpFlag flags)
361 24064 : : parenCount(0), flags(flags), activeUseCount(0), gcNumberWhenUsed(rt->gcNumber)
362 24064 : {}
363 :
364 : RegExpObject *
365 126 : RegExpObject::create(JSContext *cx, RegExpStatics *res, const jschar *chars, size_t length,
366 : RegExpFlag flags, TokenStream *tokenStream)
367 : {
368 126 : RegExpFlag staticsFlags = res->getFlags();
369 126 : return createNoStatics(cx, chars, length, RegExpFlag(flags | staticsFlags), tokenStream);
370 : }
371 :
372 : RegExpObject *
373 3686 : RegExpObject::createNoStatics(JSContext *cx, const jschar *chars, size_t length, RegExpFlag flags,
374 : TokenStream *tokenStream)
375 : {
376 3686 : JSAtom *source = js_AtomizeChars(cx, chars, length);
377 3686 : if (!source)
378 0 : return NULL;
379 :
380 3686 : return createNoStatics(cx, source, flags, tokenStream);
381 : }
382 :
383 : RegExpObject *
384 3686 : RegExpObject::createNoStatics(JSContext *cx, JSAtom *source, RegExpFlag flags,
385 : TokenStream *tokenStream)
386 : {
387 3686 : if (!RegExpCode::checkSyntax(cx, tokenStream, source))
388 9 : return NULL;
389 :
390 3677 : RegExpObjectBuilder builder(cx);
391 3677 : return builder.build(source, flags);
392 : }
393 :
394 : bool
395 4398 : RegExpObject::createShared(JSContext *cx, RegExpGuard *g)
396 : {
397 4398 : JS_ASSERT(!maybeShared());
398 4398 : if (!cx->compartment->regExps.get(cx, getSource(), getFlags(), g))
399 0 : return false;
400 :
401 4398 : setShared(cx, **g);
402 4398 : return true;
403 : }
404 :
405 : Shape *
406 2679 : RegExpObject::assignInitialShape(JSContext *cx)
407 : {
408 2679 : JS_ASSERT(isRegExp());
409 2679 : JS_ASSERT(nativeEmpty());
410 :
411 : JS_STATIC_ASSERT(LAST_INDEX_SLOT == 0);
412 : JS_STATIC_ASSERT(SOURCE_SLOT == LAST_INDEX_SLOT + 1);
413 : JS_STATIC_ASSERT(GLOBAL_FLAG_SLOT == SOURCE_SLOT + 1);
414 : JS_STATIC_ASSERT(IGNORE_CASE_FLAG_SLOT == GLOBAL_FLAG_SLOT + 1);
415 : JS_STATIC_ASSERT(MULTILINE_FLAG_SLOT == IGNORE_CASE_FLAG_SLOT + 1);
416 : JS_STATIC_ASSERT(STICKY_FLAG_SLOT == MULTILINE_FLAG_SLOT + 1);
417 :
418 : /* The lastIndex property alone is writable but non-configurable. */
419 5358 : if (!addDataProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.lastIndexAtom),
420 2679 : LAST_INDEX_SLOT, JSPROP_PERMANENT))
421 : {
422 0 : return NULL;
423 : }
424 :
425 : /* Remaining instance properties are non-writable and non-configurable. */
426 13395 : if (!addDataProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.sourceAtom),
427 2679 : SOURCE_SLOT, JSPROP_PERMANENT | JSPROP_READONLY) ||
428 2679 : !addDataProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.globalAtom),
429 2679 : GLOBAL_FLAG_SLOT, JSPROP_PERMANENT | JSPROP_READONLY) ||
430 2679 : !addDataProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.ignoreCaseAtom),
431 2679 : IGNORE_CASE_FLAG_SLOT, JSPROP_PERMANENT | JSPROP_READONLY) ||
432 2679 : !addDataProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.multilineAtom),
433 2679 : MULTILINE_FLAG_SLOT, JSPROP_PERMANENT | JSPROP_READONLY))
434 : {
435 0 : return NULL;
436 : }
437 :
438 2679 : return addDataProperty(cx, ATOM_TO_JSID(cx->runtime->atomState.stickyAtom),
439 2679 : STICKY_FLAG_SLOT, JSPROP_PERMANENT | JSPROP_READONLY);
440 : }
441 :
442 : inline bool
443 203213 : RegExpObject::init(JSContext *cx, JSAtom *source, RegExpFlag flags)
444 : {
445 203213 : if (nativeEmpty()) {
446 2679 : if (isDelegate()) {
447 0 : if (!assignInitialShape(cx))
448 0 : return false;
449 : } else {
450 2679 : Shape *shape = assignInitialShape(cx);
451 2679 : if (!shape)
452 0 : return false;
453 2679 : EmptyShape::insertInitialShape(cx, shape, getProto());
454 : }
455 2679 : JS_ASSERT(!nativeEmpty());
456 : }
457 :
458 406426 : DebugOnly<JSAtomState *> atomState = &cx->runtime->atomState;
459 203213 : JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->lastIndexAtom))->slot() == LAST_INDEX_SLOT);
460 203213 : JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->sourceAtom))->slot() == SOURCE_SLOT);
461 203213 : JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->globalAtom))->slot() == GLOBAL_FLAG_SLOT);
462 203213 : JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->ignoreCaseAtom))->slot() ==
463 203213 : IGNORE_CASE_FLAG_SLOT);
464 203213 : JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->multilineAtom))->slot() ==
465 203213 : MULTILINE_FLAG_SLOT);
466 203213 : JS_ASSERT(nativeLookup(cx, ATOM_TO_JSID(atomState->stickyAtom))->slot() == STICKY_FLAG_SLOT);
467 :
468 : /*
469 : * If this is a re-initialization with an existing RegExpShared, 'flags'
470 : * may not match getShared()->flags, so forget the RegExpShared.
471 : */
472 203213 : JSObject::setPrivate(NULL);
473 :
474 203213 : zeroLastIndex();
475 203213 : setSource(source);
476 203213 : setGlobal(flags & GlobalFlag);
477 203213 : setIgnoreCase(flags & IgnoreCaseFlag);
478 203213 : setMultiline(flags & MultilineFlag);
479 203213 : setSticky(flags & StickyFlag);
480 203213 : return true;
481 : }
482 :
483 : RegExpRunStatus
484 0 : RegExpObject::execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex,
485 : MatchPairs **output)
486 : {
487 0 : RegExpGuard g;
488 0 : if (!getShared(cx, &g))
489 0 : return RegExpRunStatus_Error;
490 0 : return g->execute(cx, chars, length, lastIndex, output);
491 : }
492 :
493 : JSFlatString *
494 639 : RegExpObject::toString(JSContext *cx) const
495 : {
496 639 : JSAtom *src = getSource();
497 1278 : StringBuffer sb(cx);
498 639 : if (size_t len = src->length()) {
499 639 : if (!sb.reserve(len + 2))
500 0 : return NULL;
501 639 : sb.infallibleAppend('/');
502 639 : sb.infallibleAppend(src->chars(), len);
503 639 : sb.infallibleAppend('/');
504 : } else {
505 0 : if (!sb.append("/(?:)/"))
506 0 : return NULL;
507 : }
508 639 : if (global() && !sb.append('g'))
509 0 : return NULL;
510 639 : if (ignoreCase() && !sb.append('i'))
511 0 : return NULL;
512 639 : if (multiline() && !sb.append('m'))
513 0 : return NULL;
514 639 : if (sticky() && !sb.append('y'))
515 0 : return NULL;
516 :
517 639 : return sb.finishString();
518 : }
519 :
520 : /* RegExpShared */
521 :
522 : bool
523 24064 : RegExpShared::compile(JSContext *cx, JSAtom *source)
524 : {
525 24064 : if (!sticky())
526 24035 : return code.compile(cx, *source, &parenCount, getFlags());
527 :
528 : /*
529 : * The sticky case we implement hackily by prepending a caret onto the front
530 : * and relying on |::execute| to pseudo-slice the string when it sees a sticky regexp.
531 : */
532 : static const jschar prefix[] = {'^', '(', '?', ':'};
533 : static const jschar postfix[] = {')'};
534 :
535 : using mozilla::ArrayLength;
536 58 : StringBuffer sb(cx);
537 29 : if (!sb.reserve(ArrayLength(prefix) + source->length() + ArrayLength(postfix)))
538 0 : return false;
539 29 : sb.infallibleAppend(prefix, ArrayLength(prefix));
540 29 : sb.infallibleAppend(source->chars(), source->length());
541 29 : sb.infallibleAppend(postfix, ArrayLength(postfix));
542 :
543 29 : JSAtom *fakeySource = sb.finishAtom();
544 29 : if (!fakeySource)
545 0 : return false;
546 29 : return code.compile(cx, *fakeySource, &parenCount, getFlags());
547 : }
548 :
549 : RegExpRunStatus
550 3051568 : RegExpShared::execute(JSContext *cx, const jschar *chars, size_t length, size_t *lastIndex,
551 : MatchPairs **output)
552 : {
553 3051568 : const size_t origLength = length;
554 3051568 : size_t backingPairCount = RegExpCode::getOutputSize(pairCount());
555 :
556 3051568 : LifoAlloc &alloc = cx->tempLifoAlloc();
557 3051568 : MatchPairs *matchPairs = MatchPairs::create(alloc, pairCount(), backingPairCount);
558 3051568 : if (!matchPairs)
559 0 : return RegExpRunStatus_Error;
560 :
561 : /*
562 : * |displacement| emulates sticky mode by matching from this offset
563 : * into the char buffer and subtracting the delta off at the end.
564 : */
565 3051568 : size_t start = *lastIndex;
566 3051568 : size_t displacement = 0;
567 :
568 3051568 : if (sticky()) {
569 54 : displacement = *lastIndex;
570 54 : chars += displacement;
571 54 : length -= displacement;
572 54 : start = 0;
573 : }
574 :
575 : RegExpRunStatus status = code.execute(cx, chars, length, start,
576 3051568 : matchPairs->buffer(), backingPairCount);
577 :
578 3051568 : switch (status) {
579 : case RegExpRunStatus_Error:
580 0 : return status;
581 : case RegExpRunStatus_Success_NotFound:
582 797634 : *output = matchPairs;
583 797634 : return status;
584 : default:
585 2253934 : JS_ASSERT(status == RegExpRunStatus_Success);
586 : }
587 :
588 2253934 : matchPairs->displace(displacement);
589 2253934 : matchPairs->checkAgainst(origLength);
590 :
591 2253934 : *lastIndex = matchPairs->pair(0).limit;
592 2253934 : *output = matchPairs;
593 :
594 2253934 : return RegExpRunStatus_Success;
595 : }
596 :
597 : /* RegExpCompartment */
598 :
599 41285 : RegExpCompartment::RegExpCompartment(JSRuntime *rt)
600 41285 : : map_(rt)
601 41285 : {}
602 :
603 82570 : RegExpCompartment::~RegExpCompartment()
604 : {
605 41285 : map_.empty();
606 41285 : }
607 :
608 : bool
609 41285 : RegExpCompartment::init(JSContext *cx)
610 : {
611 41285 : if (!map_.init()) {
612 0 : js_ReportOutOfMemory(cx);
613 0 : return false;
614 : }
615 :
616 41285 : return true;
617 : }
618 :
619 : void
620 83697 : RegExpCompartment::sweep(JSRuntime *rt)
621 : {
622 107842 : for (Map::Enum e(map_); !e.empty(); e.popFront()) {
623 : /* See the comment on RegExpShared lifetime in RegExpObject.h. */
624 24145 : RegExpShared *shared = e.front().value;
625 24145 : if (shared->activeUseCount == 0 && shared->gcNumberWhenUsed < rt->gcStartNumber) {
626 24064 : Foreground::delete_(shared);
627 24064 : e.removeFront();
628 : }
629 : }
630 83697 : }
631 :
632 : inline bool
633 47706 : RegExpCompartment::get(JSContext *cx, JSAtom *keyAtom, JSAtom *source, RegExpFlag flags, Type type,
634 : RegExpGuard *g)
635 : {
636 47706 : Key key(keyAtom, flags, type);
637 95412 : Map::AddPtr p = map_.lookupForAdd(key);
638 47706 : if (p) {
639 23642 : g->init(*p->value);
640 23642 : return true;
641 : }
642 :
643 24064 : RegExpShared *shared = cx->runtime->new_<RegExpShared>(cx->runtime, flags);
644 24064 : if (!shared)
645 0 : goto error;
646 :
647 24064 : if (!shared->compile(cx, source))
648 0 : goto error;
649 :
650 : /* Re-lookup in case there was a GC. */
651 24064 : if (!map_.relookupOrAdd(p, key, shared))
652 0 : goto error;
653 :
654 : /*
655 : * Since 'error' deletes 'shared', only guard 'shared' on success. This is
656 : * safe since 'shared' cannot be deleted by GC until after the call to
657 : * map_.add() directly above.
658 : */
659 24064 : g->init(*shared);
660 24064 : return true;
661 :
662 : error:
663 0 : Foreground::delete_(shared);
664 0 : js_ReportOutOfMemory(cx);
665 0 : return false;
666 : }
667 :
668 : bool
669 47706 : RegExpCompartment::get(JSContext *cx, JSAtom *source, RegExpFlag flags, RegExpGuard *g)
670 : {
671 47706 : return get(cx, source, source, flags, Normal, g);
672 : }
673 :
674 : bool
675 0 : RegExpCompartment::getHack(JSContext *cx, JSAtom *source, JSAtom *hackedSource, RegExpFlag flags,
676 : RegExpGuard *g)
677 : {
678 0 : return get(cx, source, hackedSource, flags, Hack, g);
679 : }
680 :
681 : bool
682 0 : RegExpCompartment::lookupHack(JSAtom *source, RegExpFlag flags, JSContext *cx, RegExpGuard *g)
683 : {
684 0 : if (Map::Ptr p = map_.lookup(Key(source, flags, Hack))) {
685 0 : g->init(*p->value);
686 0 : return true;
687 : }
688 0 : return false;
689 : }
690 :
691 : bool
692 43308 : RegExpCompartment::get(JSContext *cx, JSAtom *atom, JSString *opt, RegExpGuard *g)
693 : {
694 43308 : RegExpFlag flags = RegExpFlag(0);
695 43308 : if (opt && !ParseRegExpFlags(cx, opt, &flags))
696 0 : return false;
697 :
698 43308 : return get(cx, atom, flags, g);
699 : }
700 :
701 : /* Functions */
702 :
703 : JSObject *
704 197121 : js::CloneRegExpObject(JSContext *cx, JSObject *obj, JSObject *proto)
705 : {
706 197121 : JS_ASSERT(obj->isRegExp());
707 197121 : JS_ASSERT(proto->isRegExp());
708 :
709 197121 : RegExpObjectBuilder builder(cx);
710 197121 : return builder.clone(&obj->asRegExp(), &proto->asRegExp());
711 : }
712 :
713 : bool
714 23598 : js::ParseRegExpFlags(JSContext *cx, JSString *flagStr, RegExpFlag *flagsOut)
715 : {
716 23598 : size_t n = flagStr->length();
717 23598 : const jschar *s = flagStr->getChars(cx);
718 23598 : if (!s)
719 0 : return false;
720 :
721 23598 : *flagsOut = RegExpFlag(0);
722 47205 : for (size_t i = 0; i < n; i++) {
723 : #define HANDLE_FLAG(name_) \
724 : JS_BEGIN_MACRO \
725 : if (*flagsOut & (name_)) \
726 : goto bad_flag; \
727 : *flagsOut = RegExpFlag(*flagsOut | (name_)); \
728 : JS_END_MACRO
729 23607 : switch (s[i]) {
730 18 : case 'i': HANDLE_FLAG(IgnoreCaseFlag); break;
731 23580 : case 'g': HANDLE_FLAG(GlobalFlag); break;
732 9 : case 'm': HANDLE_FLAG(MultilineFlag); break;
733 0 : case 'y': HANDLE_FLAG(StickyFlag); break;
734 : default:
735 : bad_flag:
736 : {
737 : char charBuf[2];
738 0 : charBuf[0] = char(s[i]);
739 0 : charBuf[1] = '\0';
740 : JS_ReportErrorFlagsAndNumber(cx, JSREPORT_ERROR, js_GetErrorMessage, NULL,
741 0 : JSMSG_BAD_REGEXP_FLAG, charBuf);
742 0 : return false;
743 : }
744 : }
745 : #undef HANDLE_FLAG
746 : }
747 23598 : return true;
748 : }
749 :
750 : template<XDRMode mode>
751 : bool
752 0 : js::XDRScriptRegExpObject(XDRState<mode> *xdr, HeapPtrObject *objp)
753 : {
754 0 : JSAtom *source = 0;
755 0 : uint32_t flagsword = 0;
756 :
757 : if (mode == XDR_ENCODE) {
758 0 : JS_ASSERT(objp);
759 0 : RegExpObject &reobj = (*objp)->asRegExp();
760 0 : source = reobj.getSource();
761 0 : flagsword = reobj.getFlags();
762 : }
763 0 : if (!XDRAtom(xdr, &source) || !xdr->codeUint32(&flagsword))
764 0 : return false;
765 : if (mode == XDR_DECODE) {
766 0 : RegExpFlag flags = RegExpFlag(flagsword);
767 0 : RegExpObject *reobj = RegExpObject::createNoStatics(xdr->cx(), source, flags, NULL);
768 0 : if (!reobj)
769 0 : return false;
770 :
771 0 : if (!reobj->clearParent(xdr->cx()))
772 0 : return false;
773 0 : if (!reobj->clearType(xdr->cx()))
774 0 : return false;
775 0 : objp->init(reobj);
776 : }
777 0 : return true;
778 : }
779 :
780 : template bool
781 : js::XDRScriptRegExpObject(XDRState<XDR_ENCODE> *xdr, HeapPtrObject *objp);
782 :
783 : template bool
784 : js::XDRScriptRegExpObject(XDRState<XDR_DECODE> *xdr, HeapPtrObject *objp);
|