LCOV - app.info - js/src/yarr/YarrPattern.cpp

LCOV - code coverage report
| Current view: | directory - js/src/yarr - YarrPattern.cpp (source / functions) |  | Found | Hit | Coverage |
| Test: | app.info | Lines: | 395 | 338 | 85.6 %
| Date: | 2012-04-07 | Functions: | 39 | 36 | 92.3 %

           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                 :  * Copyright (C) 2009 Apple Inc. All rights reserved.
           6                 :  * Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged
           7                 :  *
           8                 :  * Redistribution and use in source and binary forms, with or without
           9                 :  * modification, are permitted provided that the following conditions
          10                 :  * are met:
          11                 :  * 1. Redistributions of source code must retain the above copyright
          12                 :  *    notice, this list of conditions and the following disclaimer.
          13                 :  * 2. Redistributions in binary form must reproduce the above copyright
          14                 :  *    notice, this list of conditions and the following disclaimer in the
          15                 :  *    documentation and/or other materials provided with the distribution.
          16                 :  *
          17                 :  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
          18                 :  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
          19                 :  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
          20                 :  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
          21                 :  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
          22                 :  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
          23                 :  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
          24                 :  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
          25                 :  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
          26                 :  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
          27                 :  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
          28                 :  *
          29                 :  * ***** END LICENSE BLOCK ***** */
          30                 :
          31                 : #include "YarrPattern.h"
          32                 :
          33                 : #include "Yarr.h"
          34                 : #include "YarrParser.h"
          35                 :
          36                 : using namespace WTF;
          37                 :
          38                 : namespace JSC { namespace Yarr {
          39                 :
          40                 : #include "RegExpJitTables.h"
          41                 :
          42                 : #if WTF_CPU_SPARC
          43                 : #define BASE_FRAME_SIZE 24
          44                 : #else
          45                 : #define BASE_FRAME_SIZE 0
          46                 : #endif
          47                 :
          48           24064 : class CharacterClassConstructor {
          49                 : public:
          50           24064 :     CharacterClassConstructor(bool isCaseInsensitive = false)
          51           24064 :         : m_isCaseInsensitive(isCaseInsensitive)
          52                 :     {
          53           24064 :     }
          54                 :
          55            1041 :     void reset()
          56                 :     {
          57            1041 :         m_matches.clear();
          58            1041 :         m_ranges.clear();
          59            1041 :         m_matchesUnicode.clear();
          60            1041 :         m_rangesUnicode.clear();
          61            1041 :     }
          62                 :
          63             234 :     void append(const CharacterClass* other)
          64                 :     {
          65             441 :         for (size_t i = 0; i < other->m_matches.size(); ++i)
          66             207 :             addSorted(m_matches, other->m_matches[i]);
          67             711 :         for (size_t i = 0; i < other->m_ranges.size(); ++i)
          68             477 :             addSortedRange(m_ranges, other->m_ranges[i].begin, other->m_ranges[i].end);
          69            1026 :         for (size_t i = 0; i < other->m_matchesUnicode.size(); ++i)
          70             792 :             addSorted(m_matchesUnicode, other->m_matchesUnicode[i]);
          71             423 :         for (size_t i = 0; i < other->m_rangesUnicode.size(); ++i)
          72             189 :             addSortedRange(m_rangesUnicode, other->m_rangesUnicode[i].begin, other->m_rangesUnicode[i].end);
          73             234 :     }
          74                 :
          75            1989 :     void putChar(UChar ch)
          76                 :     {
          77            1989 :         if (ch <= 0x7f) {
          78            1962 :             if (m_isCaseInsensitive && isASCIIAlpha(ch)) {
          79             432 :                 addSorted(m_matches, toASCIIUpper(ch));
          80             432 :                 addSorted(m_matches, toASCIILower(ch));
          81                 :             } else
          82            1530 :                 addSorted(m_matches, ch);
          83                 :         } else {
          84                 :             UChar upper, lower;
          85              27 :             if (m_isCaseInsensitive && ((upper = Unicode::toUpper(ch)) != (lower = Unicode::toLower(ch)))) {
          86               0 :                 addSorted(m_matchesUnicode, upper);
          87               0 :                 addSorted(m_matchesUnicode, lower);
          88                 :             } else
          89              27 :                 addSorted(m_matchesUnicode, ch);
          90                 :         }
          91            1989 :     }
          92                 :
          93                 :     // returns true if this character has another case, and 'ch' is the upper case form.
          94               0 :     static inline bool isUnicodeUpper(UChar ch)
          95                 :     {
          96               0 :         return ch != Unicode::toLower(ch);
          97                 :     }
          98                 :
          99                 :     // returns true if this character has another case, and 'ch' is the lower case form.
         100               0 :     static inline bool isUnicodeLower(UChar ch)
         101                 :     {
         102               0 :         return ch != Unicode::toUpper(ch);
         103                 :     }
         104                 :
         105             249 :     void putRange(UChar lo, UChar hi)
         106                 :     {
         107             249 :         if (lo <= 0x7f) {
         108             204 :             char asciiLo = lo;
         109             204 :             char asciiHi = std::min(hi, (UChar)0x7f);
         110             204 :             addSortedRange(m_ranges, lo, asciiHi);
         111                 :
         112             204 :             if (m_isCaseInsensitive) {
         113              45 :                 if ((asciiLo <= 'Z') && (asciiHi >= 'A'))
         114               0 :                     addSortedRange(m_ranges, std::max(asciiLo, 'A')+('a'-'A'), std::min(asciiHi, 'Z')+('a'-'A'));
         115              45 :                 if ((asciiLo <= 'z') && (asciiHi >= 'a'))
         116              36 :                     addSortedRange(m_ranges, std::max(asciiLo, 'a')+('A'-'a'), std::min(asciiHi, 'z')+('A'-'a'));
         117                 :             }
         118                 :         }
         119             249 :         if (hi >= 0x80) {
         120              45 :             uint32_t unicodeCurr = std::max(lo, (UChar)0x80);
         121              45 :             addSortedRange(m_rangesUnicode, unicodeCurr, hi);
         122                 :
         123              45 :             if (m_isCaseInsensitive) {
         124               0 :                 while (unicodeCurr <= hi) {
         125                 :                     // If the upper bound of the range (hi) is 0xffff, the increments to
         126                 :                     // unicodeCurr in this loop may take it to 0x10000.  This is fine
         127                 :                     // (if so we won't re-enter the loop, since the loop condition above
         128                 :                     // will definitely fail) - but this does mean we cannot use a UChar
         129                 :                     // to represent unicodeCurr, we must use a 32-bit value instead.
         130               0 :                     ASSERT(unicodeCurr <= 0xffff);
         131                 :
         132               0 :                     if (isUnicodeUpper(unicodeCurr)) {
         133               0 :                         UChar lowerCaseRangeBegin = Unicode::toLower(unicodeCurr);
         134               0 :                         UChar lowerCaseRangeEnd = lowerCaseRangeBegin;
         135               0 :                         while ((++unicodeCurr <= hi) && isUnicodeUpper(unicodeCurr) && (Unicode::toLower(unicodeCurr) == (lowerCaseRangeEnd + 1)))
         136               0 :                             lowerCaseRangeEnd++;
         137               0 :                         addSortedRange(m_rangesUnicode, lowerCaseRangeBegin, lowerCaseRangeEnd);
         138               0 :                     } else if (isUnicodeLower(unicodeCurr)) {
         139               0 :                         UChar upperCaseRangeBegin = Unicode::toUpper(unicodeCurr);
         140               0 :                         UChar upperCaseRangeEnd = upperCaseRangeBegin;
         141               0 :                         while ((++unicodeCurr <= hi) && isUnicodeLower(unicodeCurr) && (Unicode::toUpper(unicodeCurr) == (upperCaseRangeEnd + 1)))
         142               0 :                             upperCaseRangeEnd++;
         143               0 :                         addSortedRange(m_rangesUnicode, upperCaseRangeBegin, upperCaseRangeEnd);
         144                 :                     } else
         145               0 :                         ++unicodeCurr;
         146                 :                 }
         147                 :             }
         148                 :         }
         149             249 :     }
         150                 :
         151            1041 :     CharacterClass* charClass()
         152                 :     {
         153            1041 :         CharacterClass* characterClass = js::OffTheBooks::new_<CharacterClass>(PassRefPtr<CharacterClassTable>(0));
         154                 :
         155            1041 :         characterClass->m_matches.append(m_matches);
         156            1041 :         characterClass->m_ranges.append(m_ranges);
         157            1041 :         characterClass->m_matchesUnicode.append(m_matchesUnicode);
         158            1041 :         characterClass->m_rangesUnicode.append(m_rangesUnicode);
         159                 :
         160            1041 :         reset();
         161                 :
         162            1041 :         return characterClass;
         163                 :     }
         164                 :
         165                 : private:
         166            3420 :     void addSorted(Vector<UChar>& matches, UChar ch)
         167                 :     {
         168            3420 :         unsigned pos = 0;
         169            3420 :         unsigned range = matches.size();
         170                 :
         171                 :         // binary chop, find position to insert char.
         172           10890 :         while (range) {
         173            4158 :             unsigned index = range >> 1;
         174                 :
         175            4158 :             int val = matches[pos+index] - ch;
         176            4158 :             if (!val)
         177             108 :                 return;
         178            4050 :             else if (val > 0)
         179             990 :                 range = index;
         180                 :             else {
         181            3060 :                 pos += (index+1);
         182            3060 :                 range -= (index+1);
         183                 :             }
         184                 :         }
         185                 :
         186            3312 :         if (pos == matches.size())
         187            2556 :             matches.append(ch);
         188                 :         else
         189             756 :             matches.insert(pos, ch);
         190                 :     }
         191                 :
         192             951 :     void addSortedRange(Vector<CharacterRange>& ranges, UChar lo, UChar hi)
         193                 :     {
         194             951 :         unsigned end = ranges.size();
         195                 :
         196                 :         // Simple linear scan - I doubt there are that many ranges anyway...
         197                 :         // feel free to fix this with something faster (eg binary chop).
         198            1554 :         for (unsigned i = 0; i < end; ++i) {
         199                 :             // does the new range fall before the current position in the array
         200             783 :             if (hi < ranges[i].begin) {
         201                 :                 // optional optimization: concatenate appending ranges? - may not be worthwhile.
         202             135 :                 if (hi == (ranges[i].begin - 1)) {
         203              27 :                     ranges[i].begin = lo;
         204              27 :                     return;
         205                 :                 }
         206             108 :                 ranges.insert(i, CharacterRange(lo, hi));
         207             108 :                 return;
         208                 :             }
         209                 :             // Okay, since we didn't hit the last case, the end of the new range is definitely at or after the begining
         210                 :             // If the new range start at or before the end of the last range, then the overlap (if it starts one after the
         211                 :             // end of the last range they concatenate, which is just as good.
         212             648 :             if (lo <= (ranges[i].end + 1)) {
         213                 :                 // found an intersect! we'll replace this entry in the array.
         214              45 :                 ranges[i].begin = std::min(ranges[i].begin, lo);
         215              45 :                 ranges[i].end = std::max(ranges[i].end, hi);
         216                 :
         217                 :                 // now check if the new range can subsume any subsequent ranges.
         218              45 :                 unsigned next = i+1;
         219                 :                 // each iteration of the loop we will either remove something from the list, or break the loop.
         220              99 :                 while (next < ranges.size()) {
         221              27 :                     if (ranges[next].begin <= (ranges[i].end + 1)) {
         222                 :                         // the next entry now overlaps / concatenates this one.
         223               9 :                         ranges[i].end = std::max(ranges[i].end, ranges[next].end);
         224               9 :                         ranges.remove(next);
         225                 :                     } else
         226              18 :                         break;
         227                 :                 }
         228                 :
         229              45 :                 return;
         230                 :             }
         231                 :         }
         232                 :
         233                 :         // CharacterRange comes after all existing ranges.
         234             771 :         ranges.append(CharacterRange(lo, hi));
         235                 :     }
         236                 :
         237                 :     bool m_isCaseInsensitive;
         238                 :
         239                 :     Vector<UChar> m_matches;
         240                 :     Vector<CharacterRange> m_ranges;
         241                 :     Vector<UChar> m_matchesUnicode;
         242                 :     Vector<CharacterRange> m_rangesUnicode;
         243                 : };
         244                 :
         245                 : class YarrPatternConstructor {
         246                 : public:
         247           24064 :     YarrPatternConstructor(YarrPattern& pattern)
         248                 :         : m_pattern(pattern)
         249                 :         , m_characterClassConstructor(pattern.m_ignoreCase)
         250           24064 :         , m_invertParentheticalAssertion(false)
         251                 :     {
         252           24064 :         m_pattern.m_body = js::OffTheBooks::new_<PatternDisjunction>();
         253           24064 :         m_alternative = m_pattern.m_body->addNewAlternative();
         254           24064 :         m_pattern.m_disjunctions.append(m_pattern.m_body);
         255           24064 :     }
         256                 :
         257           24064 :     ~YarrPatternConstructor()
         258           24064 :     {
         259           24064 :     }
         260                 :
         261               0 :     void reset()
         262                 :     {
         263               0 :         m_pattern.reset();
         264               0 :         m_characterClassConstructor.reset();
         265                 :
         266               0 :         m_pattern.m_body = js::OffTheBooks::new_<PatternDisjunction>();
         267               0 :         m_alternative = m_pattern.m_body->addNewAlternative();
         268               0 :         m_pattern.m_disjunctions.append(m_pattern.m_body);
         269               0 :     }
         270                 :
         271             560 :     void assertionBOL()
         272                 :     {
         273             560 :         if (!m_alternative->m_terms.size() & !m_invertParentheticalAssertion) {
         274             560 :             m_alternative->m_startsWithBOL = true;
         275             560 :             m_alternative->m_containsBOL = true;
         276             560 :             m_pattern.m_containsBOL = true;
         277                 :         }
         278             560 :         m_alternative->m_terms.append(PatternTerm::BOL());
         279             560 :     }
         280            5769 :     void assertionEOL()
         281                 :     {
         282            5769 :         m_alternative->m_terms.append(PatternTerm::EOL());
         283            5769 :     }
         284             423 :     void assertionWordBoundary(bool invert)
         285                 :     {
         286             423 :         m_alternative->m_terms.append(PatternTerm::WordBoundary(invert));
         287             423 :     }
         288                 :
         289           78881 :     void atomPatternCharacter(UChar ch)
         290                 :     {
         291                 :         // We handle case-insensitive checking of unicode characters which do have both
         292                 :         // cases by handling them as if they were defined using a CharacterClass.
         293           78881 :         if (m_pattern.m_ignoreCase && !isASCII(ch) && (Unicode::toUpper(ch) != Unicode::toLower(ch))) {
         294               0 :             atomCharacterClassBegin();
         295               0 :             atomCharacterClassAtom(ch);
         296               0 :             atomCharacterClassEnd();
         297                 :         } else
         298           78881 :             m_alternative->m_terms.append(PatternTerm(ch));
         299           78881 :     }
         300                 :
         301            5364 :     void atomBuiltInCharacterClass(BuiltInCharacterClassID classID, bool invert)
         302                 :     {
         303            5364 :         switch (classID) {
         304                 :         case DigitClassID:
         305             324 :             m_alternative->m_terms.append(PatternTerm(m_pattern.digitsCharacterClass(), invert));
         306             324 :             break;
         307                 :         case SpaceClassID:
         308             675 :             m_alternative->m_terms.append(PatternTerm(m_pattern.spacesCharacterClass(), invert));
         309             675 :             break;
         310                 :         case WordClassID:
         311             126 :             m_alternative->m_terms.append(PatternTerm(m_pattern.wordcharCharacterClass(), invert));
         312             126 :             break;
         313                 :         case NewlineClassID:
         314            4239 :             m_alternative->m_terms.append(PatternTerm(m_pattern.newlineCharacterClass(), invert));
         315            4239 :             break;
         316                 :         }
         317            5364 :     }
         318                 :
         319            1041 :     void atomCharacterClassBegin(bool invert = false)
         320                 :     {
         321            1041 :         m_invertCharacterClass = invert;
         322            1041 :     }
         323                 :
         324            1989 :     void atomCharacterClassAtom(UChar ch)
         325                 :     {
         326            1989 :         m_characterClassConstructor.putChar(ch);
         327            1989 :     }
         328                 :
         329             249 :     void atomCharacterClassRange(UChar begin, UChar end)
         330                 :     {
         331             249 :         m_characterClassConstructor.putRange(begin, end);
         332             249 :     }
         333                 :
         334             234 :     void atomCharacterClassBuiltIn(BuiltInCharacterClassID classID, bool invert)
         335                 :     {
         336             234 :         ASSERT(classID != NewlineClassID);
         337                 :
         338             234 :         switch (classID) {
         339                 :         case DigitClassID:
         340              18 :             m_characterClassConstructor.append(invert ? m_pattern.nondigitsCharacterClass() : m_pattern.digitsCharacterClass());
         341              18 :             break;
         342                 :
         343                 :         case SpaceClassID:
         344             108 :             m_characterClassConstructor.append(invert ? m_pattern.nonspacesCharacterClass() : m_pattern.spacesCharacterClass());
         345             108 :             break;
         346                 :
         347                 :         case WordClassID:
         348             108 :             m_characterClassConstructor.append(invert ? m_pattern.nonwordcharCharacterClass() : m_pattern.wordcharCharacterClass());
         349             108 :             break;
         350                 :
         351                 :         default:
         352               0 :             ASSERT_NOT_REACHED();
         353                 :         }
         354             234 :     }
         355                 :
         356            1041 :     void atomCharacterClassEnd()
         357                 :     {
         358            1041 :         CharacterClass* newCharacterClass = m_characterClassConstructor.charClass();
         359            1041 :         m_pattern.m_userCharacterClasses.append(newCharacterClass);
         360            1041 :         m_alternative->m_terms.append(PatternTerm(newCharacterClass, m_invertCharacterClass));
         361            1041 :     }
         362                 :
         363           20513 :     void atomParenthesesSubpatternBegin(bool capture = true)
         364                 :     {
         365           20513 :         unsigned subpatternId = m_pattern.m_numSubpatterns + 1;
         366           20513 :         if (capture)
         367           19854 :             m_pattern.m_numSubpatterns++;
         368                 :
         369           20513 :         PatternDisjunction* parenthesesDisjunction = js::OffTheBooks::new_<PatternDisjunction>(m_alternative);
         370           20513 :         m_pattern.m_disjunctions.append(parenthesesDisjunction);
         371           20513 :         m_alternative->m_terms.append(PatternTerm(PatternTerm::TypeParenthesesSubpattern, subpatternId, parenthesesDisjunction, capture, false));
         372           20513 :         m_alternative = parenthesesDisjunction->addNewAlternative();
         373           20513 :     }
         374                 :
         375              72 :     void atomParentheticalAssertionBegin(bool invert = false)
         376                 :     {
         377              72 :         PatternDisjunction* parenthesesDisjunction = js::OffTheBooks::new_<PatternDisjunction>(m_alternative);
         378              72 :         m_pattern.m_disjunctions.append(parenthesesDisjunction);
         379              72 :         m_alternative->m_terms.append(PatternTerm(PatternTerm::TypeParentheticalAssertion, m_pattern.m_numSubpatterns + 1, parenthesesDisjunction, false, invert));
         380              72 :         m_alternative = parenthesesDisjunction->addNewAlternative();
         381              72 :         m_invertParentheticalAssertion = invert;
         382              72 :     }
         383                 :
         384           20585 :     void atomParenthesesEnd()
         385                 :     {
         386           20585 :         ASSERT(m_alternative->m_parent);
         387           20585 :         ASSERT(m_alternative->m_parent->m_parent);
         388                 :
         389           20585 :         PatternDisjunction* parenthesesDisjunction = m_alternative->m_parent;
         390           20585 :         m_alternative = m_alternative->m_parent->m_parent;
         391                 :
         392           20585 :         PatternTerm& lastTerm = m_alternative->lastTerm();
         393                 :
         394           20585 :         unsigned numParenAlternatives = parenthesesDisjunction->m_alternatives.size();
         395           20585 :         unsigned numBOLAnchoredAlts = 0;
         396                 :
         397           43951 :         for (unsigned i = 0; i < numParenAlternatives; i++) {
         398                 :             // Bubble up BOL flags
         399           23366 :             if (parenthesesDisjunction->m_alternatives[i]->m_startsWithBOL)
         400             126 :                 numBOLAnchoredAlts++;
         401                 :         }
         402                 :
         403           20585 :         if (numBOLAnchoredAlts) {
         404             126 :             m_alternative->m_containsBOL = true;
         405                 :             // If all the alternatives in parens start with BOL, then so does this one
         406             126 :             if (numBOLAnchoredAlts == numParenAlternatives)
         407               0 :                 m_alternative->m_startsWithBOL = true;
         408                 :         }
         409                 :
         410           20585 :         lastTerm.parentheses.lastSubpatternId = m_pattern.m_numSubpatterns;
         411           20585 :         m_invertParentheticalAssertion = false;
         412           20585 :     }
         413                 :
         414              18 :     void atomBackReference(unsigned subpatternId)
         415                 :     {
         416              18 :         ASSERT(subpatternId);
         417              18 :         m_pattern.m_containsBackreferences = true;
         418              18 :         m_pattern.m_maxBackReference = std::max(m_pattern.m_maxBackReference, subpatternId);
         419                 :
         420              18 :         if (subpatternId > m_pattern.m_numSubpatterns) {
         421               0 :             m_alternative->m_terms.append(PatternTerm::ForwardReference());
         422               0 :             return;
         423                 :         }
         424                 :
         425              18 :         PatternAlternative* currentAlternative = m_alternative;
         426              18 :         ASSERT(currentAlternative);
         427                 :
         428                 :         // Note to self: if we waited until the AST was baked, we could also remove forwards refs
         429              54 :         while ((currentAlternative = currentAlternative->m_parent->m_parent)) {
         430              18 :             PatternTerm& term = currentAlternative->lastTerm();
         431              18 :             ASSERT((term.type == PatternTerm::TypeParenthesesSubpattern) || (term.type == PatternTerm::TypeParentheticalAssertion));
         432                 :
         433              18 :             if ((term.type == PatternTerm::TypeParenthesesSubpattern) && term.capture() && (subpatternId == term.parentheses.subpatternId)) {
         434               0 :                 m_alternative->m_terms.append(PatternTerm::ForwardReference());
         435               0 :                 return;
         436                 :             }
         437                 :         }
         438                 :
         439              18 :         m_alternative->m_terms.append(PatternTerm(subpatternId));
         440                 :     }
         441                 :
         442                 :     // deep copy the argument disjunction.  If filterStartsWithBOL is true,
         443                 :     // skip alternatives with m_startsWithBOL set true.
         444             974 :     PatternDisjunction* copyDisjunction(PatternDisjunction* disjunction, bool filterStartsWithBOL = false)
         445                 :     {
         446             974 :         PatternDisjunction* newDisjunction = 0;
         447            2569 :         for (unsigned alt = 0; alt < disjunction->m_alternatives.size(); ++alt) {
         448            1595 :             PatternAlternative* alternative = disjunction->m_alternatives[alt];
         449            1595 :             if (!filterStartsWithBOL || !alternative->m_startsWithBOL) {
         450            1035 :                 if (!newDisjunction) {
         451             576 :                     newDisjunction = js::OffTheBooks::new_<PatternDisjunction>();
         452             576 :                     newDisjunction->m_parent = disjunction->m_parent;
         453                 :                 }
         454            1035 :                 PatternAlternative* newAlternative = newDisjunction->addNewAlternative();
         455            4167 :                 for (unsigned i = 0; i < alternative->m_terms.size(); ++i)
         456            3132 :                     newAlternative->m_terms.append(copyTerm(alternative->m_terms[i], filterStartsWithBOL));
         457                 :             }
         458                 :         }
         459                 :
         460             974 :         if (newDisjunction)
         461             576 :             m_pattern.m_disjunctions.append(newDisjunction);
         462             974 :         return newDisjunction;
         463                 :     }
         464                 :
         465            4119 :     PatternTerm copyTerm(PatternTerm& term, bool filterStartsWithBOL = false)
         466                 :     {
         467            4119 :         if ((term.type != PatternTerm::TypeParenthesesSubpattern) && (term.type != PatternTerm::TypeParentheticalAssertion))
         468            3696 :             return PatternTerm(term);
         469                 :
         470             423 :         PatternTerm termCopy = term;
         471             423 :         termCopy.parentheses.disjunction = copyDisjunction(termCopy.parentheses.disjunction, filterStartsWithBOL);
         472             423 :         return termCopy;
         473                 :     }
         474                 :
         475            2715 :     void quantifyAtom(unsigned min, unsigned max, bool greedy)
         476                 :     {
         477            2715 :         ASSERT(min <= max);
         478            2715 :         ASSERT(m_alternative->m_terms.size());
         479                 :
         480            2715 :         if (!max) {
         481               0 :             m_alternative->removeLastTerm();
         482               0 :             return;
         483                 :         }
         484                 :
         485            2715 :         PatternTerm& term = m_alternative->lastTerm();
         486            2715 :         ASSERT(term.type > PatternTerm::TypeAssertionWordBoundary);
         487            2715 :         ASSERT((term.quantityCount == 1) && (term.quantityType == QuantifierFixedCount));
         488                 :
         489                 :         // For any assertion with a zero minimum, not matching is valid and has no effect,
         490                 :         // remove it.  Otherwise, we need to match as least once, but there is no point
         491                 :         // matching more than once, so remove the quantifier.  It is not entirely clear
         492                 :         // from the spec whether or not this behavior is correct, but I believe this
         493                 :         // matches Firefox. :-/
         494            2715 :         if (term.type == PatternTerm::TypeParentheticalAssertion) {
         495               0 :             if (!min)
         496               0 :                 m_alternative->removeLastTerm();
         497               0 :             return;
         498                 :         }
         499                 :
         500            2715 :         if (min == 0)
         501            1719 :             term.quantify(max, greedy   ? QuantifierGreedy : QuantifierNonGreedy);
         502             996 :         else if (min == max)
         503               9 :             term.quantify(min, QuantifierFixedCount);
         504                 :         else {
         505             987 :             term.quantify(min, QuantifierFixedCount);
         506             987 :             m_alternative->m_terms.append(copyTerm(term));
         507                 :             // NOTE: this term is interesting from an analysis perspective, in that it can be ignored.....
         508             987 :             m_alternative->lastTerm().quantify((max == quantifyInfinite) ? max : max - min, greedy ? QuantifierGreedy : QuantifierNonGreedy);
         509             987 :             if (m_alternative->lastTerm().type == PatternTerm::TypeParenthesesSubpattern)
         510             108 :                 m_alternative->lastTerm().parentheses.isCopy = true;
         511                 :         }
         512                 :     }
         513                 :
         514            3555 :     void disjunction()
         515                 :     {
         516            3555 :         m_alternative = m_alternative->m_parent->addNewAlternative();
         517            3555 :     }
         518                 :
         519           49239 :     ErrorCode setupAlternativeOffsets(PatternAlternative* alternative, unsigned currentCallFrameSize, unsigned initialInputPosition, unsigned *callFrameSizeOut)
         520                 :     {
         521           49239 :         alternative->m_hasFixedSize = true;
         522           49239 :         unsigned currentInputPosition = initialInputPosition;
         523                 :
         524          165999 :         for (unsigned i = 0; i < alternative->m_terms.size(); ++i) {
         525          116760 :             PatternTerm& term = alternative->m_terms[i];
         526                 :
         527          116760 :             switch (term.type) {
         528                 :             case PatternTerm::TypeAssertionBOL:
         529                 :             case PatternTerm::TypeAssertionEOL:
         530                 :             case PatternTerm::TypeAssertionWordBoundary:
         531            6878 :                 term.inputPosition = currentInputPosition;
         532            6878 :                 break;
         533                 :
         534                 :             case PatternTerm::TypeBackReference:
         535              18 :                 term.inputPosition = currentInputPosition;
         536              18 :                 term.frameLocation = currentCallFrameSize;
         537              18 :                 currentCallFrameSize += YarrStackSpaceForBackTrackInfoBackReference;
         538              18 :                 alternative->m_hasFixedSize = false;
         539              18 :                 break;
         540                 :
         541                 :             case PatternTerm::TypeForwardReference:
         542               0 :                 break;
         543                 :
         544                 :             case PatternTerm::TypePatternCharacter:
         545           80906 :                 term.inputPosition = currentInputPosition;
         546           80906 :                 if (term.quantityType != QuantifierFixedCount) {
         547             279 :                     term.frameLocation = currentCallFrameSize;
         548             279 :                     currentCallFrameSize += YarrStackSpaceForBackTrackInfoPatternCharacter;
         549             279 :                     alternative->m_hasFixedSize = false;
         550                 :                 } else
         551           80627 :                     currentInputPosition += term.quantityCount;
         552           80906 :                 break;
         553                 :
         554                 :             case PatternTerm::TypeCharacterClass:
         555            7950 :                 term.inputPosition = currentInputPosition;
         556            7950 :                 if (term.quantityType != QuantifierFixedCount) {
         557            2166 :                     term.frameLocation = currentCallFrameSize;
         558            2166 :                     currentCallFrameSize += YarrStackSpaceForBackTrackInfoCharacterClass;
         559            2166 :                     alternative->m_hasFixedSize = false;
         560                 :                 } else
         561            5784 :                     currentInputPosition += term.quantityCount;
         562            7950 :                 break;
         563                 :
         564                 :             case PatternTerm::TypeParenthesesSubpattern:
         565                 :                 // Note: for fixed once parentheses we will ensure at least the minimum is available; others are on their own.
         566           20936 :                 term.frameLocation = currentCallFrameSize;
         567           20936 :                 if (term.quantityCount == 1 && !term.parentheses.isCopy) {
         568           20684 :                     if (term.quantityType != QuantifierFixedCount)
         569             279 :                         currentCallFrameSize += YarrStackSpaceForBackTrackInfoParenthesesOnce;
         570           20684 :                     if (ErrorCode error = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition, &currentCallFrameSize))
         571               0 :                         return error;
         572                 :                     // If quantity is fixed, then pre-check its minimum size.
         573           20684 :                     if (term.quantityType == QuantifierFixedCount)
         574           20405 :                         currentInputPosition += term.parentheses.disjunction->m_minimumSize;
         575           20684 :                     term.inputPosition = currentInputPosition;
         576             252 :                 } else if (term.parentheses.isTerminal) {
         577              54 :                     currentCallFrameSize += YarrStackSpaceForBackTrackInfoParenthesesTerminal;
         578              54 :                     if (ErrorCode error = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize, currentInputPosition, &currentCallFrameSize))
         579               0 :                         return error;
         580              54 :                     term.inputPosition = currentInputPosition;
         581                 :                 } else {
         582             198 :                     term.inputPosition = currentInputPosition;
         583                 :                     unsigned dummy;
         584             198 :                     if (ErrorCode error = setupDisjunctionOffsets(term.parentheses.disjunction, BASE_FRAME_SIZE, currentInputPosition, &dummy))
         585               0 :                         return error;
         586             198 :                     currentCallFrameSize += YarrStackSpaceForBackTrackInfoParentheses;
         587                 :                 }
         588                 :                 // Fixed count of 1 could be accepted, if they have a fixed size *AND* if all alternatives are of the same length.
         589           20936 :                 alternative->m_hasFixedSize = false;
         590           20936 :                 break;
         591                 :
         592                 :             case PatternTerm::TypeParentheticalAssertion:
         593              72 :                 term.inputPosition = currentInputPosition;
         594              72 :                 term.frameLocation = currentCallFrameSize;
         595              72 :                 if (ErrorCode error = setupDisjunctionOffsets(term.parentheses.disjunction, currentCallFrameSize + YarrStackSpaceForBackTrackInfoParentheticalAssertion, currentInputPosition, &currentCallFrameSize))
         596               0 :                     return error;
         597              72 :                 break;
         598                 :             }
         599                 :         }
         600                 :
         601           49239 :         alternative->m_minimumSize = currentInputPosition - initialInputPosition;
         602           49239 :         *callFrameSizeOut = currentCallFrameSize;
         603           49239 :         return NoError;
         604                 :     }
         605                 :
         606           45072 :     ErrorCode setupDisjunctionOffsets(PatternDisjunction* disjunction, unsigned initialCallFrameSize, unsigned initialInputPosition, unsigned *maximumCallFrameSizeOut)
         607                 :     {
         608           45072 :         if ((disjunction != m_pattern.m_body) && (disjunction->m_alternatives.size() > 1))
         609            2556 :             initialCallFrameSize += YarrStackSpaceForBackTrackInfoAlternative;
         610                 :
         611           45072 :         unsigned minimumInputSize = UINT_MAX;
         612           45072 :         unsigned maximumCallFrameSize = 0;
         613           45072 :         bool hasFixedSize = true;
         614                 :
         615           94311 :         for (unsigned alt = 0; alt < disjunction->m_alternatives.size(); ++alt) {
         616           49239 :             PatternAlternative* alternative = disjunction->m_alternatives[alt];
         617                 :             unsigned currentAlternativeCallFrameSize;
         618           49239 :             if (ErrorCode error = setupAlternativeOffsets(alternative, initialCallFrameSize, initialInputPosition, &currentAlternativeCallFrameSize))
         619               0 :                 return error;
         620           49239 :             minimumInputSize = std::min(minimumInputSize, alternative->m_minimumSize);
         621           49239 :             maximumCallFrameSize = std::max(maximumCallFrameSize, currentAlternativeCallFrameSize);
         622           49239 :             hasFixedSize &= alternative->m_hasFixedSize;
         623                 :         }
         624                 :
         625           45072 :         if (minimumInputSize == UINT_MAX)
         626               0 :             return PatternTooLarge;
         627                 :
         628           45072 :         ASSERT(maximumCallFrameSize >= initialCallFrameSize);
         629                 :
         630           45072 :         disjunction->m_hasFixedSize = hasFixedSize;
         631           45072 :         disjunction->m_minimumSize = minimumInputSize;
         632           45072 :         disjunction->m_callFrameSize = maximumCallFrameSize;
         633           45072 :         *maximumCallFrameSizeOut = maximumCallFrameSize;
         634           45072 :         return NoError;
         635                 :     }
         636                 :
         637           24064 :     ErrorCode setupOffsets()
         638                 :     {
         639                 :         unsigned dummy;
         640           24064 :         return setupDisjunctionOffsets(m_pattern.m_body, BASE_FRAME_SIZE, 0, &dummy);
         641                 :     }
         642                 :
         643                 :     // This optimization identifies sets of parentheses that we will never need to backtrack.
         644                 :     // In these cases we do not need to store state from prior iterations.
         645                 :     // We can presently avoid backtracking for:
         646                 :     //   * where the parens are at the end of the regular expression (last term in any of the
         647                 :     //     alternatives of the main body disjunction).
         648                 :     //   * where the parens are non-capturing, and quantified unbounded greedy (*).
         649                 :     //   * where the parens do not contain any capturing subpatterns.
         650           24064 :     void checkForTerminalParentheses()
         651                 :     {
         652                 :         // This check is much too crude; should be just checking whether the candidate
         653                 :         // node contains nested capturing subpatterns, not the whole expression!
         654           24064 :         if (m_pattern.m_numSubpatterns)
         655           19044 :             return;
         656                 :
         657            5020 :         Vector<PatternAlternative*>& alternatives = m_pattern.m_body->m_alternatives;
         658           10535 :         for (size_t i = 0; i < alternatives.size(); ++i) {
         659            5515 :             Vector<PatternTerm>& terms = alternatives[i]->m_terms;
         660            5515 :             if (terms.size()) {
         661            5497 :                 PatternTerm& term = terms.last();
         662            5542 :                 if (term.type == PatternTerm::TypeParenthesesSubpattern
         663                 :                     && term.quantityType == QuantifierGreedy
         664                 :                     && term.quantityCount == quantifyInfinite
         665              45 :                     && !term.capture())
         666              45 :                     term.parentheses.isTerminal = true;
         667                 :             }
         668                 :         }
         669                 :     }
         670                 :
         671           24064 :     void optimizeBOL()
         672                 :     {
         673                 :         // Look for expressions containing beginning of line (^) anchoring and unroll them.
         674                 :         // e.g. /^a|^b|c/ becomes /^a|^b|c/ which is executed once followed by /c/ which loops
         675                 :         // This code relies on the parsing code tagging alternatives with m_containsBOL and
         676                 :         // m_startsWithBOL and rolling those up to containing alternatives.
         677                 :         // At this point, this is only valid for non-multiline expressions.
         678           24064 :         PatternDisjunction* disjunction = m_pattern.m_body;
         679                 :
         680           24064 :         if (!m_pattern.m_containsBOL || m_pattern.m_multiline)
         681           23513 :             return;
         682                 :
         683             551 :         PatternDisjunction* loopDisjunction = copyDisjunction(disjunction, true);
         684                 :
         685                 :         // Set alternatives in disjunction to "onceThrough"
         686            1138 :         for (unsigned alt = 0; alt < disjunction->m_alternatives.size(); ++alt)
         687             587 :             disjunction->m_alternatives[alt]->setOnceThrough();
         688                 :
         689             551 :         if (loopDisjunction) {
         690                 :             // Move alternatives from loopDisjunction to disjunction
         691             306 :             for (unsigned alt = 0; alt < loopDisjunction->m_alternatives.size(); ++alt)
         692             153 :                 disjunction->m_alternatives.append(loopDisjunction->m_alternatives[alt]);
         693                 :
         694             153 :             loopDisjunction->m_alternatives.clear();
         695                 :         }
         696                 :     }
         697                 :
         698                 : private:
         699                 :     YarrPattern& m_pattern;
         700                 :     PatternAlternative* m_alternative;
         701                 :     CharacterClassConstructor m_characterClassConstructor;
         702                 :     bool m_invertCharacterClass;
         703                 :     bool m_invertParentheticalAssertion;
         704                 : };
         705                 :
         706           24064 : ErrorCode YarrPattern::compile(const UString& patternString)
         707                 : {
         708           48128 :     YarrPatternConstructor constructor(*this);
         709                 :
         710           24064 :     if (ErrorCode error = parse(constructor, patternString))
         711               0 :         return error;
         712                 :
         713                 :     // If the pattern contains illegal backreferences reset & reparse.
         714                 :     // Quoting Netscape's "What's new in JavaScript 1.2",
         715                 :     //      "Note: if the number of left parentheses is less than the number specified
         716                 :     //       in \#, the \# is taken as an octal escape as described in the next row."
         717           24064 :     if (containsIllegalBackReference()) {
         718               0 :         unsigned numSubpatterns = m_numSubpatterns;
         719                 :
         720               0 :         constructor.reset();
         721                 : #if !ASSERT_DISABLED
         722                 :         ErrorCode error =
         723                 : #endif
         724               0 :             parse(constructor, patternString, numSubpatterns);
         725                 :
         726               0 :         ASSERT(!error);
         727               0 :         ASSERT(numSubpatterns == m_numSubpatterns);
         728                 :     }
         729                 :
         730           24064 :     constructor.checkForTerminalParentheses();
         731           24064 :     constructor.optimizeBOL();
         732                 :
         733           24064 :     if (ErrorCode error = constructor.setupOffsets())
         734               0 :         return error;
         735                 :
         736           24064 :     return NoError;
         737                 : }
         738                 :
         739           24064 : YarrPattern::YarrPattern(const UString& pattern, bool ignoreCase, bool multiline, ErrorCode* error)
         740                 :     : m_ignoreCase(ignoreCase)
         741                 :     , m_multiline(multiline)
         742                 :     , m_containsBackreferences(false)
         743                 :     , m_containsBOL(false)
         744                 :     , m_numSubpatterns(0)
         745                 :     , m_maxBackReference(0)
         746                 :     , newlineCached(0)
         747                 :     , digitsCached(0)
         748                 :     , spacesCached(0)
         749                 :     , wordcharCached(0)
         750                 :     , nondigitsCached(0)
         751                 :     , nonspacesCached(0)
         752           24064 :     , nonwordcharCached(0)
         753                 : {
         754           24064 :     *error = compile(pattern);
         755           24064 : }
         756                 :
         757                 : } }

Generated by: LCOV version 1.7