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