LCOV - code coverage report
Current view: directory - js/src/vm - RegExpObject.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 326 240 73.6 %
Date: 2012-04-07 Functions: 36 31 86.1 %

       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);

Generated by: LCOV version 1.7