blob: 05df90eede55992f406eed635d6e9bbc357b98ae [file] [log] [blame]
msaboff@apple.comd1844412011-09-03 00:41:32 +00001/*
2 * Copyright (C) 2011 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
mhahnenberg@apple.com765a7de2013-04-15 23:17:51 +000024#include "APIShims.h"
eric@webkit.orgfa5d72e2012-03-21 23:18:20 +000025#include <wtf/CurrentTime.h>
msaboff@apple.comd1844412011-09-03 00:41:32 +000026#include "InitializeThreading.h"
27#include "JSGlobalObject.h"
fpizlo@apple.coma4b4cbe2013-01-12 04:47:03 +000028#include "Operations.h"
msaboff@apple.comd1844412011-09-03 00:41:32 +000029#include <errno.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
benjamin@webkit.orgcff06e42012-08-30 21:23:51 +000033#include <wtf/text/StringBuilder.h>
msaboff@apple.comd1844412011-09-03 00:41:32 +000034
35#if !OS(WINDOWS)
36#include <unistd.h>
37#endif
38
39#if HAVE(SYS_TIME_H)
40#include <sys/time.h>
41#endif
42
43#if COMPILER(MSVC) && !OS(WINCE)
44#include <crtdbg.h>
45#include <mmsystem.h>
46#include <windows.h>
47#endif
48
msaboff@apple.comd1844412011-09-03 00:41:32 +000049const int MaxLineLength = 100 * 1024;
50
51using namespace JSC;
52using namespace WTF;
53
commit-queue@webkit.orga2e15982011-12-15 12:01:30 +000054struct CommandLine {
55 CommandLine()
msaboff@apple.comd1844412011-09-03 00:41:32 +000056 : interactive(false)
57 , verbose(false)
58 {
59 }
60
61 bool interactive;
62 bool verbose;
benjamin@webkit.orgcff06e42012-08-30 21:23:51 +000063 Vector<String> arguments;
64 Vector<String> files;
msaboff@apple.comd1844412011-09-03 00:41:32 +000065};
66
67class StopWatch {
68public:
69 void start();
70 void stop();
71 long getElapsedMS(); // call stop() first
72
73private:
74 double m_startTime;
75 double m_stopTime;
76};
77
78void StopWatch::start()
79{
commit-queue@webkit.orgf15eb3d2013-08-14 00:51:25 +000080 m_startTime = monotonicallyIncreasingTime();
msaboff@apple.comd1844412011-09-03 00:41:32 +000081}
82
83void StopWatch::stop()
84{
commit-queue@webkit.orgf15eb3d2013-08-14 00:51:25 +000085 m_stopTime = monotonicallyIncreasingTime();
msaboff@apple.comd1844412011-09-03 00:41:32 +000086}
87
88long StopWatch::getElapsedMS()
89{
90 return static_cast<long>((m_stopTime - m_startTime) * 1000);
91}
92
93struct RegExpTest {
94 RegExpTest()
95 : offset(0)
96 , result(0)
97 {
98 }
99
benjamin@webkit.orgcff06e42012-08-30 21:23:51 +0000100 String subject;
msaboff@apple.comd1844412011-09-03 00:41:32 +0000101 int offset;
102 int result;
103 Vector<int, 32> expectVector;
104};
105
106class GlobalObject : public JSGlobalObject {
107private:
ggaren@apple.com9a9a4b52013-04-18 19:32:17 +0000108 GlobalObject(VM&, Structure*, const Vector<String>& arguments);
msaboff@apple.comd1844412011-09-03 00:41:32 +0000109
110public:
111 typedef JSGlobalObject Base;
112
ggaren@apple.com9a9a4b52013-04-18 19:32:17 +0000113 static GlobalObject* create(VM& vm, Structure* structure, const Vector<String>& arguments)
msaboff@apple.comd1844412011-09-03 00:41:32 +0000114 {
ggaren@apple.com9a9a4b52013-04-18 19:32:17 +0000115 GlobalObject* globalObject = new (NotNull, allocateCell<GlobalObject>(vm.heap)) GlobalObject(vm, structure, arguments);
116 vm.heap.addFinalizer(globalObject, destroy);
mhahnenberg@apple.com30738a72012-10-03 17:51:28 +0000117 return globalObject;
msaboff@apple.comd1844412011-09-03 00:41:32 +0000118 }
mhahnenberg@apple.comb039de02011-11-03 18:20:08 +0000119
fpizlo@apple.com10ae2d02013-08-14 02:41:47 +0000120 DECLARE_INFO;
mhahnenberg@apple.comb039de02011-11-03 18:20:08 +0000121
mhahnenberg@apple.com30738a72012-10-03 17:51:28 +0000122 static const bool needsDestructor = false;
123
ggaren@apple.com9a9a4b52013-04-18 19:32:17 +0000124 static Structure* createStructure(VM& vm, JSValue prototype)
mhahnenberg@apple.comb039de02011-11-03 18:20:08 +0000125 {
fpizlo@apple.com10ae2d02013-08-14 02:41:47 +0000126 return Structure::create(vm, 0, prototype, TypeInfo(GlobalObjectType, StructureFlags), info());
mhahnenberg@apple.comb039de02011-11-03 18:20:08 +0000127 }
msaboff@apple.com2cc41502011-09-12 22:17:53 +0000128
129protected:
ggaren@apple.com9a9a4b52013-04-18 19:32:17 +0000130 void finishCreation(VM& vm, const Vector<String>& arguments)
msaboff@apple.com2cc41502011-09-12 22:17:53 +0000131 {
ggaren@apple.com9a9a4b52013-04-18 19:32:17 +0000132 Base::finishCreation(vm);
msaboff@apple.com2cc41502011-09-12 22:17:53 +0000133 UNUSED_PARAM(arguments);
134 }
msaboff@apple.comd1844412011-09-03 00:41:32 +0000135};
136
mhahnenberg@apple.comb039de02011-11-03 18:20:08 +0000137const ClassInfo GlobalObject::s_info = { "global", &JSGlobalObject::s_info, 0, ExecState::globalObjectTable, CREATE_METHOD_TABLE(GlobalObject) };
138
ggaren@apple.com9a9a4b52013-04-18 19:32:17 +0000139GlobalObject::GlobalObject(VM& vm, Structure* structure, const Vector<String>& arguments)
140 : JSGlobalObject(vm, structure)
msaboff@apple.comd1844412011-09-03 00:41:32 +0000141{
ggaren@apple.com9a9a4b52013-04-18 19:32:17 +0000142 finishCreation(vm, arguments);
msaboff@apple.comd1844412011-09-03 00:41:32 +0000143}
144
145// Use SEH for Release builds only to get rid of the crash report dialog
146// (luckily the same tests fail in Release and Debug builds so far). Need to
147// be in a separate main function because the realMain function requires object
148// unwinding.
149
150#if COMPILER(MSVC) && !COMPILER(INTEL) && !defined(_DEBUG) && !OS(WINCE)
151#define TRY __try {
152#define EXCEPT(x) } __except (EXCEPTION_EXECUTE_HANDLER) { x; }
153#else
154#define TRY
155#define EXCEPT(x)
156#endif
157
ggaren@apple.comc143e902012-04-28 20:51:27 +0000158int realMain(int argc, char** argv);
msaboff@apple.comd1844412011-09-03 00:41:32 +0000159
160int main(int argc, char** argv)
161{
162#if OS(WINDOWS)
163#if !OS(WINCE)
164 // Cygwin calls ::SetErrorMode(SEM_FAILCRITICALERRORS), which we will inherit. This is bad for
165 // testing/debugging, as it causes the post-mortem debugger not to be invoked. We reset the
166 // error mode here to work around Cygwin's behavior. See <http://webkit.org/b/55222>.
167 ::SetErrorMode(0);
168#endif
169
170#if defined(_DEBUG)
171 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
172 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
173 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
174 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
175 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
176 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
177#endif
178
179 timeBeginPeriod(1);
180#endif
181
ggaren@apple.com9a9a4b52013-04-18 19:32:17 +0000182 // Initialize JSC before getting VM.
msaboff@apple.comd1844412011-09-03 00:41:32 +0000183 JSC::initializeThreading();
184
185 // We can't use destructors in the following code because it uses Windows
186 // Structured Exception Handling
187 int res = 0;
msaboff@apple.comd1844412011-09-03 00:41:32 +0000188 TRY
ggaren@apple.comc143e902012-04-28 20:51:27 +0000189 res = realMain(argc, argv);
msaboff@apple.comd1844412011-09-03 00:41:32 +0000190 EXCEPT(res = 3)
msaboff@apple.comd1844412011-09-03 00:41:32 +0000191 return res;
192}
193
ggaren@apple.com9a9a4b52013-04-18 19:32:17 +0000194static bool testOneRegExp(VM& vm, RegExp* regexp, RegExpTest* regExpTest, bool verbose, unsigned int lineNumber)
msaboff@apple.comd1844412011-09-03 00:41:32 +0000195{
196 bool result = true;
197 Vector<int, 32> outVector;
198 outVector.resize(regExpTest->expectVector.size());
ggaren@apple.com9a9a4b52013-04-18 19:32:17 +0000199 int matchResult = regexp->match(vm, regExpTest->subject, regExpTest->offset, outVector);
msaboff@apple.comd1844412011-09-03 00:41:32 +0000200
201 if (matchResult != regExpTest->result) {
202 result = false;
203 if (verbose)
204 printf("Line %d: results mismatch - expected %d got %d\n", lineNumber, regExpTest->result, matchResult);
205 } else if (matchResult != -1) {
206 if (outVector.size() != regExpTest->expectVector.size()) {
207 result = false;
208 if (verbose)
209 printf("Line %d: output vector size mismatch - expected %lu got %lu\n", lineNumber, regExpTest->expectVector.size(), outVector.size());
210 } else if (outVector.size() % 2) {
211 result = false;
212 if (verbose)
213 printf("Line %d: output vector size is odd (%lu), should be even\n", lineNumber, outVector.size());
214 } else {
215 // Check in pairs since the first value of the pair could be -1 in which case the second doesn't matter.
216 size_t pairCount = outVector.size() / 2;
217 for (size_t i = 0; i < pairCount; ++i) {
218 size_t startIndex = i*2;
219 if (outVector[startIndex] != regExpTest->expectVector[startIndex]) {
220 result = false;
221 if (verbose)
222 printf("Line %d: output vector mismatch at index %lu - expected %d got %d\n", lineNumber, startIndex, regExpTest->expectVector[startIndex], outVector[startIndex]);
223 }
224 if ((i > 0) && (regExpTest->expectVector[startIndex] != -1) && (outVector[startIndex+1] != regExpTest->expectVector[startIndex+1])) {
225 result = false;
226 if (verbose)
227 printf("Line %d: output vector mismatch at index %lu - expected %d got %d\n", lineNumber, startIndex+1, regExpTest->expectVector[startIndex+1], outVector[startIndex+1]);
228 }
229 }
230 }
231 }
232
233 return result;
234}
235
benjamin@webkit.orgcff06e42012-08-30 21:23:51 +0000236static int scanString(char* buffer, int bufferLength, StringBuilder& builder, char termChar)
msaboff@apple.comd1844412011-09-03 00:41:32 +0000237{
238 bool escape = false;
239
240 for (int i = 0; i < bufferLength; ++i) {
241 UChar c = buffer[i];
242
243 if (escape) {
244 switch (c) {
245 case '0':
246 c = '\0';
247 break;
248 case 'a':
249 c = '\a';
250 break;
251 case 'b':
252 c = '\b';
253 break;
254 case 'f':
255 c = '\f';
256 break;
257 case 'n':
258 c = '\n';
259 break;
260 case 'r':
261 c = '\r';
262 break;
263 case 't':
264 c = '\t';
265 break;
266 case 'v':
267 c = '\v';
268 break;
269 case '\\':
270 c = '\\';
271 break;
272 case '?':
273 c = '\?';
274 break;
275 case 'u':
276 if ((i + 4) >= bufferLength)
277 return -1;
278 unsigned int charValue;
279 if (sscanf(buffer+i+1, "%04x", &charValue) != 1)
280 return -1;
281 c = static_cast<UChar>(charValue);
282 i += 4;
283 break;
284 }
285
286 builder.append(c);
287 escape = false;
288 } else {
289 if (c == termChar)
290 return i;
291
292 if (c == '\\')
293 escape = true;
294 else
295 builder.append(c);
296 }
297 }
298
299 return -1;
300}
301
ggaren@apple.com9a9a4b52013-04-18 19:32:17 +0000302static RegExp* parseRegExpLine(VM& vm, char* line, int lineLength)
msaboff@apple.comd1844412011-09-03 00:41:32 +0000303{
benjamin@webkit.orgcff06e42012-08-30 21:23:51 +0000304 StringBuilder pattern;
msaboff@apple.comd1844412011-09-03 00:41:32 +0000305
306 if (line[0] != '/')
307 return 0;
308
309 int i = scanString(line + 1, lineLength - 1, pattern, '/') + 1;
310
311 if ((i >= lineLength) || (line[i] != '/'))
312 return 0;
313
314 ++i;
315
ggaren@apple.com9a9a4b52013-04-18 19:32:17 +0000316 return RegExp::create(vm, pattern.toString(), regExpFlags(line + i));
msaboff@apple.comd1844412011-09-03 00:41:32 +0000317}
318
319static RegExpTest* parseTestLine(char* line, int lineLength)
320{
benjamin@webkit.orgcff06e42012-08-30 21:23:51 +0000321 StringBuilder subjectString;
msaboff@apple.comd1844412011-09-03 00:41:32 +0000322
323 if ((line[0] != ' ') || (line[1] != '"'))
324 return 0;
325
326 int i = scanString(line + 2, lineLength - 2, subjectString, '"') + 2;
327
328 if ((i >= (lineLength - 2)) || (line[i] != '"') || (line[i+1] != ',') || (line[i+2] != ' '))
329 return 0;
330
331 i += 3;
332
333 int offset;
334
335 if (sscanf(line + i, "%d, ", &offset) != 1)
336 return 0;
337
338 while (line[i] && line[i] != ' ')
339 ++i;
340
341 ++i;
342
343 int matchResult;
344
345 if (sscanf(line + i, "%d, ", &matchResult) != 1)
346 return 0;
347
348 while (line[i] && line[i] != ' ')
349 ++i;
350
351 ++i;
352
353 if (line[i++] != '(')
354 return 0;
355
356 int start, end;
357
358 RegExpTest* result = new RegExpTest();
359
benjamin@webkit.orgcff06e42012-08-30 21:23:51 +0000360 result->subject = subjectString.toString();
msaboff@apple.comd1844412011-09-03 00:41:32 +0000361 result->offset = offset;
362 result->result = matchResult;
363
364 while (line[i] && line[i] != ')') {
365 if (sscanf(line + i, "%d, %d", &start, &end) != 2) {
366 delete result;
367 return 0;
368 }
369
370 result->expectVector.append(start);
371 result->expectVector.append(end);
372
373 while (line[i] && (line[i] != ',') && (line[i] != ')'))
374 i++;
375 i++;
376 while (line[i] && (line[i] != ',') && (line[i] != ')'))
377 i++;
378
379 if (line[i] == ')')
380 break;
381 if (!line[i] || (line[i] != ',')) {
382 delete result;
383 return 0;
384 }
385 i++;
386 }
387
388 return result;
389}
390
benjamin@webkit.orgcff06e42012-08-30 21:23:51 +0000391static bool runFromFiles(GlobalObject* globalObject, const Vector<String>& files, bool verbose)
msaboff@apple.comd1844412011-09-03 00:41:32 +0000392{
benjamin@webkit.orgcff06e42012-08-30 21:23:51 +0000393 String script;
394 String fileName;
msaboff@apple.comd1844412011-09-03 00:41:32 +0000395 Vector<char> scriptBuffer;
396 unsigned tests = 0;
397 unsigned failures = 0;
398 char* lineBuffer = new char[MaxLineLength + 1];
399
ggaren@apple.com9a9a4b52013-04-18 19:32:17 +0000400 VM& vm = globalObject->vm();
msaboff@apple.comd1844412011-09-03 00:41:32 +0000401
402 bool success = true;
403 for (size_t i = 0; i < files.size(); i++) {
404 FILE* testCasesFile = fopen(files[i].utf8().data(), "rb");
405
406 if (!testCasesFile) {
407 printf("Unable to open test data file \"%s\"\n", files[i].utf8().data());
408 continue;
409 }
410
411 RegExp* regexp = 0;
412 size_t lineLength = 0;
413 char* linePtr = 0;
414 unsigned int lineNumber = 0;
415
416 while ((linePtr = fgets(&lineBuffer[0], MaxLineLength, testCasesFile))) {
417 lineLength = strlen(linePtr);
418 if (linePtr[lineLength - 1] == '\n') {
419 linePtr[lineLength - 1] = '\0';
420 --lineLength;
421 }
422 ++lineNumber;
423
424 if (linePtr[0] == '#')
425 continue;
426
427 if (linePtr[0] == '/') {
ggaren@apple.com9a9a4b52013-04-18 19:32:17 +0000428 regexp = parseRegExpLine(vm, linePtr, lineLength);
msaboff@apple.comd1844412011-09-03 00:41:32 +0000429 } else if (linePtr[0] == ' ') {
430 RegExpTest* regExpTest = parseTestLine(linePtr, lineLength);
431
432 if (regexp && regExpTest) {
433 ++tests;
ggaren@apple.com9a9a4b52013-04-18 19:32:17 +0000434 if (!testOneRegExp(vm, regexp, regExpTest, verbose, lineNumber)) {
msaboff@apple.comd1844412011-09-03 00:41:32 +0000435 failures++;
436 printf("Failure on line %u\n", lineNumber);
437 }
438 }
439
440 if (regExpTest)
441 delete regExpTest;
442 }
443 }
444
445 fclose(testCasesFile);
446 }
447
448 if (failures)
449 printf("%u tests run, %u failures\n", tests, failures);
450 else
451 printf("%u tests passed\n", tests);
452
453 delete[] lineBuffer;
454
ggaren@apple.com9a9a4b52013-04-18 19:32:17 +0000455 vm.dumpSampleData(globalObject->globalExec());
msaboff@apple.comd1844412011-09-03 00:41:32 +0000456#if ENABLE(REGEXP_TRACING)
ggaren@apple.com9a9a4b52013-04-18 19:32:17 +0000457 vm.dumpRegExpTrace();
msaboff@apple.comd1844412011-09-03 00:41:32 +0000458#endif
459 return success;
460}
461
462#define RUNNING_FROM_XCODE 0
463
ggaren@apple.comc143e902012-04-28 20:51:27 +0000464static NO_RETURN void printUsageStatement(bool help = false)
msaboff@apple.comd1844412011-09-03 00:41:32 +0000465{
466 fprintf(stderr, "Usage: regexp_test [options] file\n");
467 fprintf(stderr, " -h|--help Prints this help message\n");
468 fprintf(stderr, " -v|--verbose Verbose output\n");
469
msaboff@apple.comd1844412011-09-03 00:41:32 +0000470 exit(help ? EXIT_SUCCESS : EXIT_FAILURE);
471}
472
ggaren@apple.comc143e902012-04-28 20:51:27 +0000473static void parseArguments(int argc, char** argv, CommandLine& options)
msaboff@apple.comd1844412011-09-03 00:41:32 +0000474{
475 int i = 1;
476 for (; i < argc; ++i) {
477 const char* arg = argv[i];
478 if (!strcmp(arg, "-h") || !strcmp(arg, "--help"))
ggaren@apple.comc143e902012-04-28 20:51:27 +0000479 printUsageStatement(true);
msaboff@apple.comd1844412011-09-03 00:41:32 +0000480 if (!strcmp(arg, "-v") || !strcmp(arg, "--verbose"))
481 options.verbose = true;
482 else
483 options.files.append(argv[i]);
484 }
485
486 for (; i < argc; ++i)
487 options.arguments.append(argv[i]);
488}
489
ggaren@apple.comc143e902012-04-28 20:51:27 +0000490int realMain(int argc, char** argv)
msaboff@apple.comd1844412011-09-03 00:41:32 +0000491{
ggaren@apple.com9a9a4b52013-04-18 19:32:17 +0000492 VM* vm = VM::create(LargeHeap).leakRef();
493 APIEntryShim shim(vm);
ggaren@apple.comc143e902012-04-28 20:51:27 +0000494
commit-queue@webkit.orga2e15982011-12-15 12:01:30 +0000495 CommandLine options;
ggaren@apple.comc143e902012-04-28 20:51:27 +0000496 parseArguments(argc, argv, options);
msaboff@apple.comd1844412011-09-03 00:41:32 +0000497
ggaren@apple.com9a9a4b52013-04-18 19:32:17 +0000498 GlobalObject* globalObject = GlobalObject::create(*vm, GlobalObject::createStructure(*vm, jsNull()), options.arguments);
msaboff@apple.comd1844412011-09-03 00:41:32 +0000499 bool success = runFromFiles(globalObject, options.files, options.verbose);
500
501 return success ? 0 : 3;
502}