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

Last change on this file since 262083 was 261755, checked in by Ross Kirsling, 5 years ago

[IWYU] Remove unnecessary includes from JSC implementation files
https://bugs.webkit.org/show_bug.cgi?id=211867

Reviewed by Keith Miller.

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