LCOV - code coverage report
Current view: directory - js/src/vm - RegExpObject.cpp (source / functions) Found Hit Coverage
Test: app.info Lines: 328 258 78.7 %
Date: 2012-06-02 Functions: 35 32 91.4 %

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

Generated by: LCOV version 1.7