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