3d1410c15e40113b5fc412c93ae895744887aae7
[WebKit-https.git] / Source / JavaScriptCore / runtime / RegExpInlines.h
1 /*
2  *  Copyright (C) 1999-2001, 2004 Harri Porten (porten@kde.org)
3  *  Copyright (c) 2007, 2008, 2016 Apple Inc. All rights reserved.
4  *  Copyright (C) 2009 Torch Mobile, Inc.
5  *  Copyright (C) 2010 Peter Varga (pvarga@inf.u-szeged.hu), University of Szeged
6  *
7  *  This library is free software; you can redistribute it and/or
8  *  modify it under the terms of the GNU Lesser General Public
9  *  License as published by the Free Software Foundation; either
10  *  version 2 of the License, or (at your option) any later version.
11  *
12  *  This library is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  *  Lesser General Public License for more details.
16  *
17  *  You should have received a copy of the GNU Lesser General Public
18  *  License along with this library; if not, write to the Free Software
19  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
20  *
21  */
22
23 #pragma once
24
25 #include "RegExp.h"
26 #include "JSCInlines.h"
27 #include "Yarr.h"
28 #include "YarrInterpreter.h"
29 #include "YarrJIT.h"
30
31 #define REGEXP_FUNC_TEST_DATA_GEN 0
32
33 #if REGEXP_FUNC_TEST_DATA_GEN
34 #include <stdio.h>
35 #include <stdlib.h>
36 #include <string.h>
37 #endif
38
39 namespace JSC {
40
41 #if REGEXP_FUNC_TEST_DATA_GEN
42 class RegExpFunctionalTestCollector {
43     // This class is not thread safe.
44 protected:
45     static const char* const s_fileName;
46
47 public:
48     static RegExpFunctionalTestCollector* get();
49
50     ~RegExpFunctionalTestCollector();
51
52     void outputOneTest(RegExp*, String, int, int*, int);
53     void clearRegExp(RegExp* regExp)
54     {
55         if (regExp == m_lastRegExp)
56             m_lastRegExp = 0;
57     }
58
59 private:
60     RegExpFunctionalTestCollector();
61
62     void outputEscapedString(const String&, bool escapeSlash = false);
63
64     static RegExpFunctionalTestCollector* s_instance;
65     FILE* m_file;
66     RegExp* m_lastRegExp;
67 };
68 #endif // REGEXP_FUNC_TEST_DATA_GEN
69
70 ALWAYS_INLINE bool RegExp::hasCodeFor(Yarr::YarrCharSize charSize)
71 {
72     if (hasCode()) {
73 #if ENABLE(YARR_JIT)
74         if (m_state != JITCode)
75             return true;
76         if ((charSize == Yarr::Char8) && (m_regExpJITCode.has8BitCode()))
77             return true;
78         if ((charSize == Yarr::Char16) && (m_regExpJITCode.has16BitCode()))
79             return true;
80 #else
81         UNUSED_PARAM(charSize);
82         return true;
83 #endif
84     }
85     return false;
86 }
87
88 ALWAYS_INLINE void RegExp::compileIfNecessary(VM& vm, Yarr::YarrCharSize charSize)
89 {
90     if (hasCodeFor(charSize))
91         return;
92
93     compile(&vm, charSize);
94 }
95
96 template<typename VectorType>
97 ALWAYS_INLINE int RegExp::matchInline(VM& vm, const String& s, unsigned startOffset, VectorType& ovector)
98 {
99 #if ENABLE(REGEXP_TRACING)
100     m_rtMatchCallCount++;
101     m_rtMatchTotalSubjectStringLen += (double)(s.length() - startOffset);
102 #endif
103
104     ASSERT(m_state != ParseError);
105     compileIfNecessary(vm, s.is8Bit() ? Yarr::Char8 : Yarr::Char16);
106
107     int offsetVectorSize = (m_numSubpatterns + 1) * 2;
108     ovector.resize(offsetVectorSize);
109     int* offsetVector = ovector.data();
110
111     int result;
112 #if ENABLE(YARR_JIT)
113     if (m_state == JITCode) {
114         if (s.is8Bit())
115             result = m_regExpJITCode.execute(s.characters8(), startOffset, s.length(), offsetVector).start;
116         else
117             result = m_regExpJITCode.execute(s.characters16(), startOffset, s.length(), offsetVector).start;
118 #if ENABLE(YARR_JIT_DEBUG)
119         matchCompareWithInterpreter(s, startOffset, offsetVector, result);
120 #endif
121     } else
122 #endif
123         result = Yarr::interpret(m_regExpBytecode.get(), s, startOffset, reinterpret_cast<unsigned*>(offsetVector));
124
125     // FIXME: The YARR engine should handle unsigned or size_t length matches.
126     // The YARR Interpreter is "unsigned" clean, while the YARR JIT hasn't been addressed.
127     // The offset vector handling needs to change as well.
128     // Right now we convert a match where the offsets overflowed into match failure.
129     // There are two places in WebCore that call the interpreter directly that need to
130     // have their offsets changed to int as well. They are yarr/RegularExpression.cpp
131     // and inspector/ContentSearchUtilities.cpp
132     if (s.length() > INT_MAX) {
133         bool overflowed = false;
134
135         if (result < -1)
136             overflowed = true;
137
138         for (unsigned i = 0; i <= m_numSubpatterns; i++) {
139             if ((offsetVector[i*2] < -1) || ((offsetVector[i*2] >= 0) && (offsetVector[i*2+1] < -1))) {
140                 overflowed = true;
141                 offsetVector[i*2] = -1;
142                 offsetVector[i*2+1] = -1;
143             }
144         }
145
146         if (overflowed)
147             result = -1;
148     }
149
150     ASSERT(result >= -1);
151
152 #if REGEXP_FUNC_TEST_DATA_GEN
153     RegExpFunctionalTestCollector::get()->outputOneTest(this, s, startOffset, offsetVector, result);
154 #endif
155
156 #if ENABLE(REGEXP_TRACING)
157     if (result != -1)
158         m_rtMatchFoundCount++;
159 #endif
160
161     return result;
162 }
163
164 ALWAYS_INLINE bool RegExp::hasMatchOnlyCodeFor(Yarr::YarrCharSize charSize)
165 {
166     if (hasCode()) {
167 #if ENABLE(YARR_JIT)
168         if (m_state != JITCode)
169             return true;
170         if ((charSize == Yarr::Char8) && (m_regExpJITCode.has8BitCodeMatchOnly()))
171             return true;
172         if ((charSize == Yarr::Char16) && (m_regExpJITCode.has16BitCodeMatchOnly()))
173             return true;
174 #else
175         UNUSED_PARAM(charSize);
176         return true;
177 #endif
178     }
179
180     return false;
181 }
182
183 ALWAYS_INLINE void RegExp::compileIfNecessaryMatchOnly(VM& vm, Yarr::YarrCharSize charSize)
184 {
185     if (hasMatchOnlyCodeFor(charSize))
186         return;
187
188     compileMatchOnly(&vm, charSize);
189 }
190
191 ALWAYS_INLINE MatchResult RegExp::matchInline(VM& vm, const String& s, unsigned startOffset)
192 {
193 #if ENABLE(REGEXP_TRACING)
194     m_rtMatchOnlyCallCount++;
195     m_rtMatchOnlyTotalSubjectStringLen += (double)(s.length() - startOffset);
196 #endif
197
198     ASSERT(m_state != ParseError);
199     compileIfNecessaryMatchOnly(vm, s.is8Bit() ? Yarr::Char8 : Yarr::Char16);
200
201 #if ENABLE(YARR_JIT)
202     if (m_state == JITCode) {
203         MatchResult result = s.is8Bit() ?
204             m_regExpJITCode.execute(s.characters8(), startOffset, s.length()) :
205             m_regExpJITCode.execute(s.characters16(), startOffset, s.length());
206 #if ENABLE(REGEXP_TRACING)
207         if (!result)
208             m_rtMatchOnlyFoundCount++;
209 #endif
210         return result;
211     }
212 #endif
213
214     int offsetVectorSize = (m_numSubpatterns + 1) * 2;
215     int* offsetVector;
216     Vector<int, 32> nonReturnedOvector;
217     nonReturnedOvector.grow(offsetVectorSize);
218     offsetVector = nonReturnedOvector.data();
219     int r = Yarr::interpret(m_regExpBytecode.get(), s, startOffset, reinterpret_cast<unsigned*>(offsetVector));
220 #if REGEXP_FUNC_TEST_DATA_GEN
221     RegExpFunctionalTestCollector::get()->outputOneTest(this, s, startOffset, offsetVector, result);
222 #endif
223
224     if (r >= 0) {
225 #if ENABLE(REGEXP_TRACING)
226         m_rtMatchOnlyFoundCount++;
227 #endif
228         return MatchResult(r, reinterpret_cast<unsigned*>(offsetVector)[1]);
229     }
230
231     return MatchResult::failed();
232 }
233
234 } // namespace JSC