LCOV - app.info - js/src/vm/RegExpObject.cpp

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