FontFaceSet binding does not handle null correctly
[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 <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
48 const int MaxLineLength = 100 * 1024;
49
50 using namespace JSC;
51 using namespace WTF;
52
53 struct 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
66 class StopWatch {
67 public:
68     void start();
69     void stop();
70     long getElapsedMS(); // call stop() first
71
72 private:
73     double m_startTime;
74     double m_stopTime;
75 };
76
77 void StopWatch::start()
78 {
79     m_startTime = monotonicallyIncreasingTime();
80 }
81
82 void StopWatch::stop()
83 {
84     m_stopTime = monotonicallyIncreasingTime();
85 }
86
87 long StopWatch::getElapsedMS()
88 {
89     return static_cast<long>((m_stopTime - m_startTime) * 1000);
90 }
91
92 struct 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
105 class GlobalObject : public JSGlobalObject {
106 private:
107     GlobalObject(VM&, Structure*, const Vector<String>& arguments);
108
109 public:
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
128 protected:
129     void finishCreation(VM& vm, const Vector<String>& arguments)
130     {
131         Base::finishCreation(vm);
132         UNUSED_PARAM(arguments);
133     }
134 };
135
136 const ClassInfo GlobalObject::s_info = { "global", &JSGlobalObject::s_info, nullptr, CREATE_METHOD_TABLE(GlobalObject) };
137
138 GlobalObject::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
157 int realMain(int argc, char** argv);
158
159 int main(int argc, char** argv)
160 {
161 #if OS(WINDOWS)
162     // Cygwin calls ::SetErrorMode(SEM_FAILCRITICALERRORS), which we will inherit. This is bad for
163     // testing/debugging, as it causes the post-mortem debugger not to be invoked. We reset the
164     // error mode here to work around Cygwin's behavior. See <http://webkit.org/b/55222>.
165     ::SetErrorMode(0);
166
167 #if defined(_DEBUG)
168     _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
169     _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
170     _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
171     _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
172     _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
173     _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
174 #endif
175
176     timeBeginPeriod(1);
177 #endif
178
179     // Initialize JSC before getting VM.
180     JSC::initializeThreading();
181
182     // We can't use destructors in the following code because it uses Windows
183     // Structured Exception Handling
184     int res = 0;
185     TRY
186         res = realMain(argc, argv);
187     EXCEPT(res = 3)
188     return res;
189 }
190
191 static bool testOneRegExp(VM& vm, RegExp* regexp, RegExpTest* regExpTest, bool verbose, unsigned int lineNumber)
192 {
193     bool result = true;
194     Vector<int, 32> outVector;
195     outVector.resize(regExpTest->expectVector.size());
196     int matchResult = regexp->match(vm, regExpTest->subject, regExpTest->offset, outVector);
197
198     if (matchResult != regExpTest->result) {
199         result = false;
200         if (verbose)
201             printf("Line %d: results mismatch - expected %d got %d\n", lineNumber, regExpTest->result, matchResult);
202     } else if (matchResult != -1) {
203         if (outVector.size() != regExpTest->expectVector.size()) {
204             result = false;
205             if (verbose) {
206 #if OS(WINDOWS)
207                 printf("Line %d: output vector size mismatch - expected %Iu got %Iu\n", lineNumber, regExpTest->expectVector.size(), outVector.size());
208 #else
209                 printf("Line %d: output vector size mismatch - expected %zu got %zu\n", lineNumber, regExpTest->expectVector.size(), outVector.size());
210 #endif
211             }
212         } else if (outVector.size() % 2) {
213             result = false;
214             if (verbose) {
215 #if OS(WINDOWS)
216                 printf("Line %d: output vector size is odd (%Iu), should be even\n", lineNumber, outVector.size());
217 #else
218                 printf("Line %d: output vector size is odd (%zu), should be even\n", lineNumber, outVector.size());
219 #endif
220             }
221         } else {
222             // Check in pairs since the first value of the pair could be -1 in which case the second doesn't matter.
223             size_t pairCount = outVector.size() / 2;
224             for (size_t i = 0; i < pairCount; ++i) {
225                 size_t startIndex = i*2;
226                 if (outVector[startIndex] != regExpTest->expectVector[startIndex]) {
227                     result = false;
228                     if (verbose) {
229 #if OS(WINDOWS)
230                         printf("Line %d: output vector mismatch at index %Iu - expected %d got %d\n", lineNumber, startIndex, regExpTest->expectVector[startIndex], outVector[startIndex]);
231 #else
232                         printf("Line %d: output vector mismatch at index %zu - expected %d got %d\n", lineNumber, startIndex, regExpTest->expectVector[startIndex], outVector[startIndex]);
233 #endif
234                     }
235                 }
236                 if ((i > 0) && (regExpTest->expectVector[startIndex] != -1) && (outVector[startIndex+1] != regExpTest->expectVector[startIndex+1])) {
237                     result = false;
238                     if (verbose) {
239 #if OS(WINDOWS)
240                         printf("Line %d: output vector mismatch at index %Iu - expected %d got %d\n", lineNumber, startIndex + 1, regExpTest->expectVector[startIndex + 1], outVector[startIndex + 1]);
241 #else
242                         printf("Line %d: output vector mismatch at index %zu - expected %d got %d\n", lineNumber, startIndex + 1, regExpTest->expectVector[startIndex + 1], outVector[startIndex + 1]);
243 #endif
244                     }
245                 }
246             }
247         }
248     }
249
250     return result;
251 }
252
253 static int scanString(char* buffer, int bufferLength, StringBuilder& builder, char termChar)
254 {
255     bool escape = false;
256     
257     for (int i = 0; i < bufferLength; ++i) {
258         UChar c = buffer[i];
259         
260         if (escape) {
261             switch (c) {
262             case '0':
263                 c = '\0';
264                 break;
265             case 'a':
266                 c = '\a';
267                 break;
268             case 'b':
269                 c = '\b';
270                 break;
271             case 'f':
272                 c = '\f';
273                 break;
274             case 'n':
275                 c = '\n';
276                 break;
277             case 'r':
278                 c = '\r';
279                 break;
280             case 't':
281                 c = '\t';
282                 break;
283             case 'v':
284                 c = '\v';
285                 break;
286             case '\\':
287                 c = '\\';
288                 break;
289             case '?':
290                 c = '\?';
291                 break;
292             case 'u':
293                 if ((i + 4) >= bufferLength)
294                     return -1;
295                 unsigned int charValue;
296                 if (sscanf(buffer+i+1, "%04x", &charValue) != 1)
297                     return -1;
298                 c = static_cast<UChar>(charValue);
299                 i += 4;
300                 break;
301             }
302             
303             builder.append(c);
304             escape = false;
305         } else {
306             if (c == termChar)
307                 return i;
308
309             if (c == '\\')
310                 escape = true;
311             else
312                 builder.append(c);
313         }
314     }
315
316     return -1;
317 }
318
319 static RegExp* parseRegExpLine(VM& vm, char* line, int lineLength)
320 {
321     StringBuilder pattern;
322     
323     if (line[0] != '/')
324         return 0;
325
326     int i = scanString(line + 1, lineLength - 1, pattern, '/') + 1;
327
328     if ((i >= lineLength) || (line[i] != '/'))
329         return 0;
330
331     ++i;
332
333     RegExp* r = RegExp::create(vm, pattern.toString(), regExpFlags(line + i));
334     if (r->isValid())
335         return r;
336     return nullptr;
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
436         while ((linePtr = fgets(&lineBuffer[0], MaxLineLength, testCasesFile))) {
437             lineLength = strlen(linePtr);
438             if (linePtr[lineLength - 1] == '\n') {
439                 linePtr[lineLength - 1] = '\0';
440                 --lineLength;
441             }
442             ++lineNumber;
443
444             if (linePtr[0] == '#')
445                 continue;
446
447             if (linePtr[0] == '/') {
448                 regexp = parseRegExpLine(vm, linePtr, lineLength);
449             } else if (linePtr[0] == ' ') {
450                 RegExpTest* regExpTest = parseTestLine(linePtr, lineLength);
451                 
452                 if (regexp && regExpTest) {
453                     ++tests;
454                     if (!testOneRegExp(vm, regexp, regExpTest, verbose, lineNumber)) {
455                         failures++;
456                         printf("Failure on line %u\n", lineNumber);
457                     }
458                 }
459                 
460                 if (regExpTest)
461                     delete regExpTest;
462             } else if (linePtr[0] == '-') {
463                 tests++;
464                 regexp = 0; // Reset the live regexp to avoid confusing other subsequent tests
465                 bool successfullyParsed = parseRegExpLine(vm, linePtr + 1, lineLength - 1);
466                 if (successfullyParsed) {
467                     failures++;
468                     fprintf(stderr, "Failure on line %u. '%s' is not a valid regexp\n", lineNumber, linePtr + 1);
469                 }
470             }
471         }
472         
473         fclose(testCasesFile);
474     }
475
476     if (failures)
477         printf("%u tests run, %u failures\n", tests, failures);
478     else
479         printf("%u tests passed\n", tests);
480
481     delete[] lineBuffer;
482
483 #if ENABLE(REGEXP_TRACING)
484     vm.dumpRegExpTrace();
485 #endif
486     return success;
487 }
488
489 #define RUNNING_FROM_XCODE 0
490
491 static NO_RETURN void printUsageStatement(bool help = false)
492 {
493     fprintf(stderr, "Usage: regexp_test [options] file\n");
494     fprintf(stderr, "  -h|--help  Prints this help message\n");
495     fprintf(stderr, "  -v|--verbose  Verbose output\n");
496
497     exit(help ? EXIT_SUCCESS : EXIT_FAILURE);
498 }
499
500 static void parseArguments(int argc, char** argv, CommandLine& options)
501 {
502     int i = 1;
503     for (; i < argc; ++i) {
504         const char* arg = argv[i];
505         if (!strcmp(arg, "-h") || !strcmp(arg, "--help"))
506             printUsageStatement(true);
507         if (!strcmp(arg, "-v") || !strcmp(arg, "--verbose"))
508             options.verbose = true;
509         else
510             options.files.append(argv[i]);
511     }
512
513     for (; i < argc; ++i)
514         options.arguments.append(argv[i]);
515 }
516
517 int realMain(int argc, char** argv)
518 {
519     VM* vm = &VM::create(LargeHeap).leakRef();
520     JSLockHolder locker(vm);
521
522     CommandLine options;
523     parseArguments(argc, argv, options);
524
525     GlobalObject* globalObject = GlobalObject::create(*vm, GlobalObject::createStructure(*vm, jsNull()), options.arguments);
526     bool success = runFromFiles(globalObject, options.files, options.verbose);
527
528     return success ? 0 : 3;
529 }
530
531 #if OS(WINDOWS)
532 extern "C" __declspec(dllexport) int WINAPI dllLauncherEntryPoint(int argc, const char* argv[])
533 {
534     return main(argc, const_cast<char**>(argv));
535 }
536 #endif