source: webkit/trunk/Source/JavaScriptCore/testRegExp.cpp@ 198364

Last change on this file since 198364 was 198364, checked in by [email protected], 9 years ago

Replace all of the various non-working and non-compiling sampling profiler hacks with a single super hack
https://bugs.webkit.org/show_bug.cgi?id=155561

Reviewed by Saam Barati.

Source/JavaScriptCore:

A VM needs some internal profiling hacks in addition to the profiler(s) that the user sees, because
you can squeeze out more fidelity if you're willing to make some kind of deal with the devil. Prior
to this change JSC had a bunch of these:

  • CodeBlock sampling profiler
  • Bytecode sampling profiler
  • Sampling flags
  • Sampling regions
  • Some other stuff

I tried using these recently. They didn't even build. Initially I fixed that, but then I found that
these profilers had some serious bugs that made them report bogus results - like underreporting the
time spent in regions of code by more than 2x.

Part of the problem here is that a profiler loses fidelity as it gains power. The more general it
tries to be, the more code gets executed on the hot path for the profiler, which increasingly
perturbs the results. I believe that's the reason for the underreporting - code ran sufficiently
slower, and in a sufficiently different way when profiling, that the results were just wrong.

This change attacks this problem directly by replacing all of the diverse profiling hacks with just
one, which I call the SuperSampler. It consists of exactly one counter. When enabled, the sampler
will periodically print (via dataLog()) the percentage of samples that saw a non-zero count. Because
it's so simple, it gives better accuracy. This comes about in two ways:

  • It runs at a lower rate. That's fine since it's only checking one flag. You don't need a high rate for just one flag.


  • The fact that there is only *one* flag means that the user must choose a hypothesis about what is slow. This turns the problem of profiling into a hypothesis testing problem, which is an inherently less flaky kind of experiment to run.


The SuperSampler is enabled with a runtime flag rather than a compile-time flag, so it's much less
likely to break. That also means that you can enable it without rebuilding the universe. The old
samplers all had ENABLE flags in Platform.h, which was rather unfortunate for compile times.

SuperSampler supports both JIT and C++ users. C++ users should use SuperSamplerScope. The default
idiom is to create one and pass "true" to it. You can disable a scope by passing "false" instead.
This patch puts a bunch of scopes in places I care about. I think it's probably OK if people check in
these deactivated scopes. That makes it convenient to retest things we've tested previously.

  • CMakeLists.txt:
  • JavaScriptCore.xcodeproj/project.pbxproj:
  • bytecode/SamplingTool.cpp: Removed.
  • bytecode/SamplingTool.h: Removed.
  • bytecode/SuperSampler.cpp: Added.

(JSC::initializeSuperSampler):
(JSC::printSuperSamplerState):

  • bytecode/SuperSampler.h: Added.

(JSC::SuperSamplerScope::SuperSamplerScope):
(JSC::SuperSamplerScope::~SuperSamplerScope):

  • bytecompiler/BytecodeGenerator.cpp:

(JSC::BytecodeGenerator::generate):

  • bytecompiler/NodesCodegen.cpp:
  • dfg/DFGAbstractInterpreterInlines.h:

(JSC::DFG::AbstractInterpreter<AbstractStateType>::forAllValues):
(JSC::DFG::AbstractInterpreter<AbstractStateType>::clobberStructures):

  • dfg/DFGArgumentsEliminationPhase.cpp:

(JSC::DFG::performArgumentsElimination):

  • dfg/DFGBackwardsPropagationPhase.cpp:

(JSC::DFG::performBackwardsPropagation):

  • dfg/DFGByteCodeParser.cpp:

(JSC::DFG::parse):

  • dfg/DFGCFAPhase.cpp:

(JSC::DFG::performCFA):

  • dfg/DFGCFGSimplificationPhase.cpp:

(JSC::DFG::performCFGSimplification):

  • dfg/DFGCPSRethreadingPhase.cpp:

(JSC::DFG::CPSRethreadingPhase::freeUnnecessaryNodes):
(JSC::DFG::CPSRethreadingPhase::canonicalizeLocalsInBlocks):
(JSC::DFG::CPSRethreadingPhase::propagatePhis):
(JSC::DFG::performCPSRethreading):

  • dfg/DFGCSEPhase.cpp:

(JSC::DFG::performLocalCSE):
(JSC::DFG::performGlobalCSE):

  • dfg/DFGCleanUpPhase.cpp:

(JSC::DFG::performCleanUp):

  • dfg/DFGConstantFoldingPhase.cpp:

(JSC::DFG::performConstantFolding):

  • dfg/DFGConstantHoistingPhase.cpp:

(JSC::DFG::performConstantHoisting):

  • dfg/DFGCriticalEdgeBreakingPhase.cpp:

(JSC::DFG::performCriticalEdgeBreaking):

  • dfg/DFGDCEPhase.cpp:

(JSC::DFG::performDCE):

  • dfg/DFGDriver.cpp:

(JSC::DFG::compileImpl):

  • dfg/DFGFixupPhase.cpp:

(JSC::DFG::performFixup):

  • dfg/DFGGraph.cpp:

(JSC::DFG::Graph::dethread):

  • dfg/DFGIntegerCheckCombiningPhase.cpp:

(JSC::DFG::performIntegerCheckCombining):

  • dfg/DFGIntegerRangeOptimizationPhase.cpp:

(JSC::DFG::performIntegerRangeOptimization):

  • dfg/DFGInvalidationPointInjectionPhase.cpp:

(JSC::DFG::performInvalidationPointInjection):

  • dfg/DFGJITCompiler.cpp:

(JSC::DFG::JITCompiler::compile):
(JSC::DFG::JITCompiler::compileFunction):

  • dfg/DFGLICMPhase.cpp:

(JSC::DFG::performLICM):

  • dfg/DFGLiveCatchVariablePreservationPhase.cpp:

(JSC::DFG::performLiveCatchVariablePreservationPhase):

  • dfg/DFGLivenessAnalysisPhase.cpp:

(JSC::DFG::performLivenessAnalysis):

  • dfg/DFGLoopPreHeaderCreationPhase.cpp:

(JSC::DFG::performLoopPreHeaderCreation):

  • dfg/DFGMaximalFlushInsertionPhase.cpp:

(JSC::DFG::performMaximalFlushInsertion):

  • dfg/DFGMovHintRemovalPhase.cpp:

(JSC::DFG::performMovHintRemoval):

  • dfg/DFGOSRAvailabilityAnalysisPhase.cpp:

(JSC::DFG::performOSRAvailabilityAnalysis):

  • dfg/DFGOSREntrypointCreationPhase.cpp:

(JSC::DFG::performOSREntrypointCreation):

  • dfg/DFGOSRExitCompiler.cpp:
  • dfg/DFGObjectAllocationSinkingPhase.cpp:

(JSC::DFG::performObjectAllocationSinking):

  • dfg/DFGOperations.cpp:
  • dfg/DFGPhantomInsertionPhase.cpp:

(JSC::DFG::performPhantomInsertion):

  • dfg/DFGPlan.cpp:

(JSC::DFG::Plan::compileInThread):

  • dfg/DFGPredictionInjectionPhase.cpp:

(JSC::DFG::performPredictionInjection):

  • dfg/DFGPredictionPropagationPhase.cpp:

(JSC::DFG::performPredictionPropagation):

  • dfg/DFGPutStackSinkingPhase.cpp:

(JSC::DFG::performPutStackSinking):

  • dfg/DFGSSAConversionPhase.cpp:

(JSC::DFG::performSSAConversion):

  • dfg/DFGSSALoweringPhase.cpp:

(JSC::DFG::performSSALowering):

  • dfg/DFGSpeculativeJIT64.cpp:

(JSC::DFG::SpeculativeJIT::compile):

  • dfg/DFGStackLayoutPhase.cpp:

(JSC::DFG::performStackLayout):

  • dfg/DFGStaticExecutionCountEstimationPhase.cpp:

(JSC::DFG::performStaticExecutionCountEstimation):

  • dfg/DFGStoreBarrierInsertionPhase.cpp:

(JSC::DFG::performFastStoreBarrierInsertion):
(JSC::DFG::performGlobalStoreBarrierInsertion):

  • dfg/DFGStrengthReductionPhase.cpp:

(JSC::DFG::performStrengthReduction):

  • dfg/DFGStructureAbstractValue.cpp:

(JSC::DFG::StructureAbstractValue::assertIsRegistered):
(JSC::DFG::StructureAbstractValue::clobber):
(JSC::DFG::StructureAbstractValue::observeTransition):
(JSC::DFG::StructureAbstractValue::observeTransitions):
(JSC::DFG::StructureAbstractValue::add):
(JSC::DFG::StructureAbstractValue::merge):
(JSC::DFG::StructureAbstractValue::mergeSlow):
(JSC::DFG::StructureAbstractValue::mergeNotTop):
(JSC::DFG::StructureAbstractValue::filter):
(JSC::DFG::StructureAbstractValue::filterSlow):
(JSC::DFG::StructureAbstractValue::contains):
(JSC::DFG::StructureAbstractValue::isSubsetOf):
(JSC::DFG::StructureAbstractValue::isSupersetOf):
(JSC::DFG::StructureAbstractValue::overlaps):
(JSC::DFG::StructureAbstractValue::equalsSlow):

  • dfg/DFGStructureRegistrationPhase.cpp:

(JSC::DFG::performStructureRegistration):

  • dfg/DFGTierUpCheckInjectionPhase.cpp:

(JSC::DFG::performTierUpCheckInjection):

  • dfg/DFGTypeCheckHoistingPhase.cpp:

(JSC::DFG::performTypeCheckHoisting):

  • dfg/DFGUnificationPhase.cpp:

(JSC::DFG::performUnification):

  • dfg/DFGVarargsForwardingPhase.cpp:

(JSC::DFG::performVarargsForwarding):

  • dfg/DFGVirtualRegisterAllocationPhase.cpp:

(JSC::DFG::performVirtualRegisterAllocation):

  • dfg/DFGWatchpointCollectionPhase.cpp:

(JSC::DFG::performWatchpointCollection):

  • dynbench.cpp:
  • ftl/FTLLowerDFGToB3.cpp:

(JSC::FTL::DFG::LowerDFGToB3::compileRegExpExec):
(JSC::FTL::DFG::LowerDFGToB3::compileRegExpTest):
(JSC::FTL::DFG::LowerDFGToB3::compileStringReplace):
(JSC::FTL::DFG::LowerDFGToB3::compileGetRegExpObjectLastIndex):

  • ftl/FTLOSRExitCompiler.cpp:

(JSC::FTL::compileFTLOSRExit):

  • ftl/FTLOutput.cpp:

(JSC::FTL::Output::store):
(JSC::FTL::Output::absolute):
(JSC::FTL::Output::incrementSuperSamplerCount):
(JSC::FTL::Output::decrementSuperSamplerCount):

  • ftl/FTLOutput.h:

(JSC::FTL::Output::baseIndex):
(JSC::FTL::Output::load8SignExt32):
(JSC::FTL::Output::load8ZeroExt32):
(JSC::FTL::Output::anchor):
(JSC::FTL::Output::absolute): Deleted.

  • heap/Heap.cpp:

(JSC::Heap::markRoots):
(JSC::Heap::collectAndSweep):
(JSC::Heap::collectImpl):
(JSC::Heap::zombifyDeadObjects):

  • heap/MarkedBlock.cpp:

(JSC::MarkedBlock::specializedSweep):

  • interpreter/Interpreter.cpp:

(JSC::setupVarargsFrameAndSetThis):
(JSC::Interpreter::Interpreter):
(JSC::Interpreter::initialize):
(JSC::checkedReturn):
(JSC::Interpreter::execute):
(JSC::Interpreter::executeCall):
(JSC::Interpreter::executeConstruct):
(JSC::Interpreter::debug):
(JSC::SamplingScope::SamplingScope): Deleted.
(JSC::SamplingScope::~SamplingScope): Deleted.
(JSC::Interpreter::enableSampler): Deleted.
(JSC::Interpreter::dumpSampleData): Deleted.
(JSC::Interpreter::startSampling): Deleted.
(JSC::Interpreter::stopSampling): Deleted.

  • interpreter/Interpreter.h:

(JSC::Interpreter::isCallBytecode):
(JSC::Interpreter::sampler): Deleted.

  • jit/AssemblyHelpers.cpp:

(JSC::AssemblyHelpers::branchIfNotFastTypedArray):
(JSC::AssemblyHelpers::incrementSuperSamplerCount):
(JSC::AssemblyHelpers::decrementSuperSamplerCount):
(JSC::AssemblyHelpers::purifyNaN):

  • jit/AssemblyHelpers.h:
  • jit/JIT.cpp:
  • jit/JIT.h:
  • jit/JITArithmetic.cpp:
  • jit/JITArithmetic32_64.cpp:
  • jit/JITCall.cpp:
  • jit/JITCall32_64.cpp:
  • jit/JITOperations.cpp:
  • jit/JITPropertyAccess.cpp:
  • jit/JITPropertyAccess32_64.cpp:
  • jsc.cpp:

(runWithScripts):
(jscmain):

  • parser/Nodes.cpp:
  • parser/Parser.h:

(JSC::parse):

  • runtime/Executable.h:
  • runtime/InitializeThreading.cpp:

(JSC::initializeThreading):

  • runtime/Options.h:
  • runtime/RegExpCachedResult.h:
  • runtime/RegExpMatchesArray.h:

(JSC::createRegExpMatchesArray):

  • runtime/StringPrototype.cpp:

(JSC::removeUsingRegExpSearch):
(JSC::stringProtoFuncSubstring):

  • runtime/VM.cpp:

(JSC::VM::resetDateCache):
(JSC::VM::whenIdle):
(JSC::VM::deleteAllCode):
(JSC::VM::addSourceProviderCache):
(JSC::VM::startSampling): Deleted.
(JSC::VM::stopSampling): Deleted.
(JSC::VM::dumpSampleData): Deleted.

  • runtime/VM.h:

(JSC::VM::regExpCache):

  • testRegExp.cpp:

(runFromFiles):

  • yarr/YarrInterpreter.cpp:

(JSC::Yarr::interpret):

Source/WebCore:

No new tests because no new behavior.

  • platform/audio/ios/MediaSessionManagerIOS.mm:
  • platform/graphics/avfoundation/objc/SourceBufferPrivateAVFObjC.mm:

Source/WTF:

This patch replaces all of our various ad hoc profiling hacks with a single ad hoc profiling hack.
That needs to be able to sleep a thread, so I added a portable way to do it.

This also removes a bunch of ENABLE flags for all of the old non-working hacks.

  • wtf/CurrentTime.cpp:

(WTF::currentCPUTime):
(WTF::sleep):

  • wtf/CurrentTime.h:

(WTF::sleepMS):

  • wtf/Platform.h:
  • Property svn:eol-style set to native
File size: 15.5 KB
Line 
1/*
2 * Copyright (C) 2011, 2015 Apple Inc. All rights reserved.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 *
19 */
20
21#include "config.h"
22#include "RegExp.h"
23
24#include <wtf/CurrentTime.h>
25#include "InitializeThreading.h"
26#include "JSCInlines.h"
27#include "JSGlobalObject.h"
28#include <errno.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.h>
32#include <wtf/text/StringBuilder.h>
33
34#if !OS(WINDOWS)
35#include <unistd.h>
36#endif
37
38#if HAVE(SYS_TIME_H)
39#include <sys/time.h>
40#endif
41
42#if COMPILER(MSVC)
43#include <crtdbg.h>
44#include <mmsystem.h>
45#include <windows.h>
46#endif
47
48const int MaxLineLength = 100 * 1024;
49
50using namespace JSC;
51using namespace WTF;
52
53struct CommandLine {
54 CommandLine()
55 : interactive(false)
56 , verbose(false)
57 {
58 }
59
60 bool interactive;
61 bool verbose;
62 Vector<String> arguments;
63 Vector<String> files;
64};
65
66class StopWatch {
67public:
68 void start();
69 void stop();
70 long getElapsedMS(); // call stop() first
71
72private:
73 double m_startTime;
74 double m_stopTime;
75};
76
77void StopWatch::start()
78{
79 m_startTime = monotonicallyIncreasingTime();
80}
81
82void StopWatch::stop()
83{
84 m_stopTime = monotonicallyIncreasingTime();
85}
86
87long StopWatch::getElapsedMS()
88{
89 return static_cast<long>((m_stopTime - m_startTime) * 1000);
90}
91
92struct RegExpTest {
93 RegExpTest()
94 : offset(0)
95 , result(0)
96 {
97 }
98
99 String subject;
100 int offset;
101 int result;
102 Vector<int, 32> expectVector;
103};
104
105class GlobalObject : public JSGlobalObject {
106private:
107 GlobalObject(VM&, Structure*, const Vector<String>& arguments);
108
109public:
110 typedef JSGlobalObject Base;
111
112 static GlobalObject* create(VM& vm, Structure* structure, const Vector<String>& arguments)
113 {
114 GlobalObject* globalObject = new (NotNull, allocateCell<GlobalObject>(vm.heap)) GlobalObject(vm, structure, arguments);
115 vm.heap.addFinalizer(globalObject, destroy);
116 return globalObject;
117 }
118
119 DECLARE_INFO;
120
121 static const bool needsDestructor = false;
122
123 static Structure* createStructure(VM& vm, JSValue prototype)
124 {
125 return Structure::create(vm, 0, prototype, TypeInfo(GlobalObjectType, StructureFlags), info());
126 }
127
128protected:
129 void finishCreation(VM& vm, const Vector<String>& arguments)
130 {
131 Base::finishCreation(vm);
132 UNUSED_PARAM(arguments);
133 }
134};
135
136const ClassInfo GlobalObject::s_info = { "global", &JSGlobalObject::s_info, nullptr, CREATE_METHOD_TABLE(GlobalObject) };
137
138GlobalObject::GlobalObject(VM& vm, Structure* structure, const Vector<String>& arguments)
139 : JSGlobalObject(vm, structure)
140{
141 finishCreation(vm, arguments);
142}
143
144// Use SEH for Release builds only to get rid of the crash report dialog
145// (luckily the same tests fail in Release and Debug builds so far). Need to
146// be in a separate main function because the realMain function requires object
147// unwinding.
148
149#if COMPILER(MSVC) && !defined(_DEBUG)
150#define TRY __try {
151#define EXCEPT(x) } __except (EXCEPTION_EXECUTE_HANDLER) { x; }
152#else
153#define TRY
154#define EXCEPT(x)
155#endif
156
157int realMain(int argc, char** argv);
158
159int main(int argc, char** argv)
160{
161#if OS(WINDOWS)
162#if defined(_M_X64) || defined(__x86_64__)
163 // The VS2013 runtime has a bug where it mis-detects AVX-capable processors
164 // if the feature has been disabled in firmware. This causes us to crash
165 // in some of the math functions. For now, we disable those optimizations
166 // because Microsoft is not going to fix the problem in VS2013.
167 // FIXME: http://webkit.org/b/141449: Remove this workaround when we switch to VS2015+.
168 _set_FMA3_enable(0);
169#endif
170
171 // Cygwin calls ::SetErrorMode(SEM_FAILCRITICALERRORS), which we will inherit. This is bad for
172 // testing/debugging, as it causes the post-mortem debugger not to be invoked. We reset the
173 // error mode here to work around Cygwin's behavior. See <http://webkit.org/b/55222>.
174 ::SetErrorMode(0);
175
176#if defined(_DEBUG)
177 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
178 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
179 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
180 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
181 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
182 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
183#endif
184
185 timeBeginPeriod(1);
186#endif
187
188 // Initialize JSC before getting VM.
189 JSC::initializeThreading();
190
191 // We can't use destructors in the following code because it uses Windows
192 // Structured Exception Handling
193 int res = 0;
194 TRY
195 res = realMain(argc, argv);
196 EXCEPT(res = 3)
197 return res;
198}
199
200static bool testOneRegExp(VM& vm, RegExp* regexp, RegExpTest* regExpTest, bool verbose, unsigned int lineNumber)
201{
202 bool result = true;
203 Vector<int, 32> outVector;
204 outVector.resize(regExpTest->expectVector.size());
205 int matchResult = regexp->match(vm, regExpTest->subject, regExpTest->offset, outVector);
206
207 if (matchResult != regExpTest->result) {
208 result = false;
209 if (verbose)
210 printf("Line %d: results mismatch - expected %d got %d\n", lineNumber, regExpTest->result, matchResult);
211 } else if (matchResult != -1) {
212 if (outVector.size() != regExpTest->expectVector.size()) {
213 result = false;
214 if (verbose) {
215#if OS(WINDOWS)
216 printf("Line %d: output vector size mismatch - expected %Iu got %Iu\n", lineNumber, regExpTest->expectVector.size(), outVector.size());
217#else
218 printf("Line %d: output vector size mismatch - expected %zu got %zu\n", lineNumber, regExpTest->expectVector.size(), outVector.size());
219#endif
220 }
221 } else if (outVector.size() % 2) {
222 result = false;
223 if (verbose) {
224#if OS(WINDOWS)
225 printf("Line %d: output vector size is odd (%Iu), should be even\n", lineNumber, outVector.size());
226#else
227 printf("Line %d: output vector size is odd (%zu), should be even\n", lineNumber, outVector.size());
228#endif
229 }
230 } else {
231 // Check in pairs since the first value of the pair could be -1 in which case the second doesn't matter.
232 size_t pairCount = outVector.size() / 2;
233 for (size_t i = 0; i < pairCount; ++i) {
234 size_t startIndex = i*2;
235 if (outVector[startIndex] != regExpTest->expectVector[startIndex]) {
236 result = false;
237 if (verbose) {
238#if OS(WINDOWS)
239 printf("Line %d: output vector mismatch at index %Iu - expected %d got %d\n", lineNumber, startIndex, regExpTest->expectVector[startIndex], outVector[startIndex]);
240#else
241 printf("Line %d: output vector mismatch at index %zu - expected %d got %d\n", lineNumber, startIndex, regExpTest->expectVector[startIndex], outVector[startIndex]);
242#endif
243 }
244 }
245 if ((i > 0) && (regExpTest->expectVector[startIndex] != -1) && (outVector[startIndex+1] != regExpTest->expectVector[startIndex+1])) {
246 result = false;
247 if (verbose) {
248#if OS(WINDOWS)
249 printf("Line %d: output vector mismatch at index %Iu - expected %d got %d\n", lineNumber, startIndex + 1, regExpTest->expectVector[startIndex + 1], outVector[startIndex + 1]);
250#else
251 printf("Line %d: output vector mismatch at index %zu - expected %d got %d\n", lineNumber, startIndex + 1, regExpTest->expectVector[startIndex + 1], outVector[startIndex + 1]);
252#endif
253 }
254 }
255 }
256 }
257 }
258
259 return result;
260}
261
262static int scanString(char* buffer, int bufferLength, StringBuilder& builder, char termChar)
263{
264 bool escape = false;
265
266 for (int i = 0; i < bufferLength; ++i) {
267 UChar c = buffer[i];
268
269 if (escape) {
270 switch (c) {
271 case '0':
272 c = '\0';
273 break;
274 case 'a':
275 c = '\a';
276 break;
277 case 'b':
278 c = '\b';
279 break;
280 case 'f':
281 c = '\f';
282 break;
283 case 'n':
284 c = '\n';
285 break;
286 case 'r':
287 c = '\r';
288 break;
289 case 't':
290 c = '\t';
291 break;
292 case 'v':
293 c = '\v';
294 break;
295 case '\\':
296 c = '\\';
297 break;
298 case '?':
299 c = '\?';
300 break;
301 case 'u':
302 if ((i + 4) >= bufferLength)
303 return -1;
304 unsigned int charValue;
305 if (sscanf(buffer+i+1, "%04x", &charValue) != 1)
306 return -1;
307 c = static_cast<UChar>(charValue);
308 i += 4;
309 break;
310 }
311
312 builder.append(c);
313 escape = false;
314 } else {
315 if (c == termChar)
316 return i;
317
318 if (c == '\\')
319 escape = true;
320 else
321 builder.append(c);
322 }
323 }
324
325 return -1;
326}
327
328static RegExp* parseRegExpLine(VM& vm, char* line, int lineLength)
329{
330 StringBuilder pattern;
331
332 if (line[0] != '/')
333 return 0;
334
335 int i = scanString(line + 1, lineLength - 1, pattern, '/') + 1;
336
337 if ((i >= lineLength) || (line[i] != '/'))
338 return 0;
339
340 ++i;
341
342 RegExp* r = RegExp::create(vm, pattern.toString(), regExpFlags(line + i));
343 if (r->isValid())
344 return r;
345 return nullptr;
346}
347
348static RegExpTest* parseTestLine(char* line, int lineLength)
349{
350 StringBuilder subjectString;
351
352 if ((line[0] != ' ') || (line[1] != '"'))
353 return 0;
354
355 int i = scanString(line + 2, lineLength - 2, subjectString, '"') + 2;
356
357 if ((i >= (lineLength - 2)) || (line[i] != '"') || (line[i+1] != ',') || (line[i+2] != ' '))
358 return 0;
359
360 i += 3;
361
362 int offset;
363
364 if (sscanf(line + i, "%d, ", &offset) != 1)
365 return 0;
366
367 while (line[i] && line[i] != ' ')
368 ++i;
369
370 ++i;
371
372 int matchResult;
373
374 if (sscanf(line + i, "%d, ", &matchResult) != 1)
375 return 0;
376
377 while (line[i] && line[i] != ' ')
378 ++i;
379
380 ++i;
381
382 if (line[i++] != '(')
383 return 0;
384
385 int start, end;
386
387 RegExpTest* result = new RegExpTest();
388
389 result->subject = subjectString.toString();
390 result->offset = offset;
391 result->result = matchResult;
392
393 while (line[i] && line[i] != ')') {
394 if (sscanf(line + i, "%d, %d", &start, &end) != 2) {
395 delete result;
396 return 0;
397 }
398
399 result->expectVector.append(start);
400 result->expectVector.append(end);
401
402 while (line[i] && (line[i] != ',') && (line[i] != ')'))
403 i++;
404 i++;
405 while (line[i] && (line[i] != ',') && (line[i] != ')'))
406 i++;
407
408 if (line[i] == ')')
409 break;
410 if (!line[i] || (line[i] != ',')) {
411 delete result;
412 return 0;
413 }
414 i++;
415 }
416
417 return result;
418}
419
420static bool runFromFiles(GlobalObject* globalObject, const Vector<String>& files, bool verbose)
421{
422 String script;
423 String fileName;
424 Vector<char> scriptBuffer;
425 unsigned tests = 0;
426 unsigned failures = 0;
427 char* lineBuffer = new char[MaxLineLength + 1];
428
429 VM& vm = globalObject->vm();
430
431 bool success = true;
432 for (size_t i = 0; i < files.size(); i++) {
433 FILE* testCasesFile = fopen(files[i].utf8().data(), "rb");
434
435 if (!testCasesFile) {
436 printf("Unable to open test data file \"%s\"\n", files[i].utf8().data());
437 continue;
438 }
439
440 RegExp* regexp = 0;
441 size_t lineLength = 0;
442 char* linePtr = 0;
443 unsigned int lineNumber = 0;
444
445 while ((linePtr = fgets(&lineBuffer[0], MaxLineLength, testCasesFile))) {
446 lineLength = strlen(linePtr);
447 if (linePtr[lineLength - 1] == '\n') {
448 linePtr[lineLength - 1] = '\0';
449 --lineLength;
450 }
451 ++lineNumber;
452
453 if (linePtr[0] == '#')
454 continue;
455
456 if (linePtr[0] == '/') {
457 regexp = parseRegExpLine(vm, linePtr, lineLength);
458 } else if (linePtr[0] == ' ') {
459 RegExpTest* regExpTest = parseTestLine(linePtr, lineLength);
460
461 if (regexp && regExpTest) {
462 ++tests;
463 if (!testOneRegExp(vm, regexp, regExpTest, verbose, lineNumber)) {
464 failures++;
465 printf("Failure on line %u\n", lineNumber);
466 }
467 }
468
469 if (regExpTest)
470 delete regExpTest;
471 } else if (linePtr[0] == '-') {
472 tests++;
473 regexp = 0; // Reset the live regexp to avoid confusing other subsequent tests
474 bool successfullyParsed = parseRegExpLine(vm, linePtr + 1, lineLength - 1);
475 if (successfullyParsed) {
476 failures++;
477 fprintf(stderr, "Failure on line %u. '%s' is not a valid regexp\n", lineNumber, linePtr + 1);
478 }
479 }
480 }
481
482 fclose(testCasesFile);
483 }
484
485 if (failures)
486 printf("%u tests run, %u failures\n", tests, failures);
487 else
488 printf("%u tests passed\n", tests);
489
490 delete[] lineBuffer;
491
492#if ENABLE(REGEXP_TRACING)
493 vm.dumpRegExpTrace();
494#endif
495 return success;
496}
497
498#define RUNNING_FROM_XCODE 0
499
500static NO_RETURN void printUsageStatement(bool help = false)
501{
502 fprintf(stderr, "Usage: regexp_test [options] file\n");
503 fprintf(stderr, " -h|--help Prints this help message\n");
504 fprintf(stderr, " -v|--verbose Verbose output\n");
505
506 exit(help ? EXIT_SUCCESS : EXIT_FAILURE);
507}
508
509static void parseArguments(int argc, char** argv, CommandLine& options)
510{
511 int i = 1;
512 for (; i < argc; ++i) {
513 const char* arg = argv[i];
514 if (!strcmp(arg, "-h") || !strcmp(arg, "--help"))
515 printUsageStatement(true);
516 if (!strcmp(arg, "-v") || !strcmp(arg, "--verbose"))
517 options.verbose = true;
518 else
519 options.files.append(argv[i]);
520 }
521
522 for (; i < argc; ++i)
523 options.arguments.append(argv[i]);
524}
525
526int realMain(int argc, char** argv)
527{
528 VM* vm = &VM::create(LargeHeap).leakRef();
529 JSLockHolder locker(vm);
530
531 CommandLine options;
532 parseArguments(argc, argv, options);
533
534 GlobalObject* globalObject = GlobalObject::create(*vm, GlobalObject::createStructure(*vm, jsNull()), options.arguments);
535 bool success = runFromFiles(globalObject, options.files, options.verbose);
536
537 return success ? 0 : 3;
538}
539
540#if OS(WINDOWS)
541extern "C" __declspec(dllexport) int WINAPI dllLauncherEntryPoint(int argc, const char* argv[])
542{
543 return main(argc, const_cast<char**>(argv));
544}
545#endif
Note: See TracBrowser for help on using the repository browser.