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

Last change on this file since 115579 was 115579, checked in by [email protected], 13 years ago

Clarified JSGlobalData (JavaScript VM) lifetime
https://bugs.webkit.org/show_bug.cgi?id=85142

Reviewed by Anders Carlsson.

Source/JavaScriptCore:

This was so confusing that I didn't feel like I could reason about
memory lifetime in the heap without fixing it.

The rules are:

(1) JSGlobalData owns the virtual machine and all memory in it.

(2) Deleting a JSGlobalData frees the virtual machine and all memory
in it.

(Caveat emptor: if you delete the virtual machine while you're running
JIT code or accessing GC objects, you're gonna have a bad time.)

(I opted not to make arbitrary sub-objects keep the virtual machine
alive automatically because:

(a) doing that right would be complex and slow;

(b) in the case of an exiting thread or process, there's no
clear way to give the garbage collector a chance to try again
later;

(c) continuing to run the garbage collector after we've been
asked to shut down the virtual machine seems rude;

(d) we've never really supported that feature, anyway.)

(3) Normal ref-counting will do. No need to call a battery of
specialty functions to tear down a JSGlobalData. Its foibles
notwithstanding, C++ does in fact know how to execute destructors in
order.

  • API/JSContextRef.cpp:

(JSGlobalContextCreate): Removed compatibility shim for older
operating systems because it's no longer used.

(JSGlobalContextRelease): Now that we can rely on JSGlobalData to "do
the right thing", this code is much simpler. We still have one special
case to notify the garbage collector if we're removing the last
reference to the global object, since this can improve memory behavior.

  • heap/CopiedSpace.cpp:

(JSC::CopiedSpace::freeAllBlocks):

  • heap/CopiedSpace.h:

(CopiedSpace): Renamed "destroy" => "freeAllBlocks" because true
destruction-time behaviors should be limited to our C++ destructor.

  • heap/Heap.cpp:

(JSC::Heap::~Heap):
(JSC):
(JSC::Heap::lastChanceToFinalize):

  • heap/Heap.h:

(Heap):
(JSC::Heap::heap): Renamed "destroy" => "lastChanceToFinalize" because
true destruction-time behaviors should be limited to our C++
destructor.

Reorganized the code, putting code that must run before any objects
get torn down into lastChanceToFinalize, and code that just tears down
objects into our destructor.

  • heap/Local.h:

(JSC::LocalStack::LocalStack):
(JSC::LocalStack::push):
(LocalStack): See rule (2).

  • jsc.cpp:

(functionQuit):
(main):
(printUsageStatement):
(parseArguments):
(jscmain):

  • testRegExp.cpp:

(main):
(printUsageStatement):
(parseArguments):
(realMain): See rule (3).

I removed the feature of ensuring orderly tear-down when calling quit()
or running in --help mode because it didn't seem very useful and
making it work with Windows structured exception handling and
NO_RETURN didn't seem like a fun way to spend a Saturday.

  • runtime/JSGlobalData.h:
  • runtime/JSGlobalData.cpp:

(JSC::JSGlobalData::JSGlobalData): Moved heap to be the first data
member in JSGlobalData to ensure that it's destructed last, so other
objects that reference it destruct without crashing. This allowed me
to remove clearBuiltinStructures() altogether, and helped guarantee
rule (3).

(JSC::JSGlobalData::~JSGlobalData): Explicitly call
lastChanceToFinalize() at the head of our destructor to ensure that
all pending finalizers run while the virtual machine is still in a
valid state. Trying to resurrect (re-ref) the virtual machine at this
point is not valid, but all other operations are.

Changed a null to a 0xbbadbeef to clarify just how bad this beef is.

  • runtime/JSGlobalObject.cpp:

(JSC::JSGlobalObject::init):

  • runtime/JSGlobalObject.h:

(JSGlobalObject):
(JSC::JSGlobalObject::globalData): See rule (3).

Source/WebCore:

  • bindings/js/WorkerScriptController.cpp:

(WebCore::WorkerScriptController::~WorkerScriptController): Slightly
simpler than before. We can't just rely on our default destructor
because we need to hold the JSLock when we tear down the VM.

  • bridge/NP_jsobject.cpp:

(_NPN_InvokeDefault):
(_NPN_Invoke):
(_NPN_Evaluate):
(_NPN_Construct): Don't RefPtr<> the JSGlobalData because it makes it
seem like you know something the rest of our code doesn't know. The
plugin JSGlobalData is immortal, anyway.

I also removed some timeout checker related code because that feature
doesn't work anymore, so it was effectively dead code.

Source/WebKit/mac:

  • Plugins/Hosted/NetscapePluginInstanceProxy.mm:

(WebKit::NetscapePluginInstanceProxy::invoke):
(WebKit::NetscapePluginInstanceProxy::invokeDefault):
(WebKit::NetscapePluginInstanceProxy::construct):

  • Property svn:eol-style set to native
File size: 14.0 KB
Line 
1/*
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
24#include <wtf/CurrentTime.h>
25#include "InitializeThreading.h"
26#include "JSGlobalObject.h"
27#include "UStringBuilder.h"
28#include <errno.h>
29#include <stdio.h>
30#include <stdlib.h>
31#include <string.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) && !OS(WINCE)
42#include <crtdbg.h>
43#include <mmsystem.h>
44#include <windows.h>
45#endif
46
47#if PLATFORM(QT)
48#include <QCoreApplication>
49#include <QDateTime>
50#endif
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<UString> arguments;
67 Vector<UString> 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 = currentTime();
84}
85
86void StopWatch::stop()
87{
88 m_stopTime = currentTime();
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 UString subject;
104 int offset;
105 int result;
106 Vector<int, 32> expectVector;
107};
108
109class GlobalObject : public JSGlobalObject {
110private:
111 GlobalObject(JSGlobalData&, Structure*, const Vector<UString>& arguments);
112
113public:
114 typedef JSGlobalObject Base;
115
116 static GlobalObject* create(JSGlobalData& globalData, Structure* structure, const Vector<UString>& arguments)
117 {
118 return new (NotNull, allocateCell<GlobalObject>(globalData.heap)) GlobalObject(globalData, structure, arguments);
119 }
120
121 static const ClassInfo s_info;
122
123 static Structure* createStructure(JSGlobalData& globalData, JSValue prototype)
124 {
125 return Structure::create(globalData, 0, prototype, TypeInfo(GlobalObjectType, StructureFlags), &s_info);
126 }
127
128protected:
129 void finishCreation(JSGlobalData& globalData, const Vector<UString>& arguments)
130 {
131 Base::finishCreation(globalData);
132 UNUSED_PARAM(arguments);
133 }
134};
135
136COMPILE_ASSERT(!IsInteger<GlobalObject>::value, WTF_IsInteger_GlobalObject_false);
137ASSERT_CLASS_FITS_IN_CELL(GlobalObject);
138
139const ClassInfo GlobalObject::s_info = { "global", &JSGlobalObject::s_info, 0, ExecState::globalObjectTable, CREATE_METHOD_TABLE(GlobalObject) };
140
141GlobalObject::GlobalObject(JSGlobalData& globalData, Structure* structure, const Vector<UString>& arguments)
142 : JSGlobalObject(globalData, structure)
143{
144 finishCreation(globalData, arguments);
145}
146
147// Use SEH for Release builds only to get rid of the crash report dialog
148// (luckily the same tests fail in Release and Debug builds so far). Need to
149// be in a separate main function because the realMain function requires object
150// unwinding.
151
152#if COMPILER(MSVC) && !COMPILER(INTEL) && !defined(_DEBUG) && !OS(WINCE)
153#define TRY __try {
154#define EXCEPT(x) } __except (EXCEPTION_EXECUTE_HANDLER) { x; }
155#else
156#define TRY
157#define EXCEPT(x)
158#endif
159
160int realMain(int argc, char** argv);
161
162int main(int argc, char** argv)
163{
164#if OS(WINDOWS)
165#if !OS(WINCE)
166 // Cygwin calls ::SetErrorMode(SEM_FAILCRITICALERRORS), which we will inherit. This is bad for
167 // testing/debugging, as it causes the post-mortem debugger not to be invoked. We reset the
168 // error mode here to work around Cygwin's behavior. See <http://webkit.org/b/55222>.
169 ::SetErrorMode(0);
170#endif
171
172#if defined(_DEBUG)
173 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
174 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
175 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
176 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
177 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
178 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
179#endif
180
181 timeBeginPeriod(1);
182#endif
183
184#if PLATFORM(QT)
185 QCoreApplication app(argc, argv);
186#endif
187
188 // Initialize JSC before getting JSGlobalData.
189 JSC::initializeThreading();
190
191 // We can't use destructors in the following code because it uses Windows
192 // Structured Exception Handling
193 int res = 0;
194 TRY
195 res = realMain(argc, argv);
196 EXCEPT(res = 3)
197 return res;
198}
199
200static bool testOneRegExp(JSGlobalData& globalData, RegExp* regexp, RegExpTest* regExpTest, bool verbose, unsigned int lineNumber)
201{
202 bool result = true;
203 Vector<int, 32> outVector;
204 outVector.resize(regExpTest->expectVector.size());
205 int matchResult = regexp->match(globalData, regExpTest->subject, regExpTest->offset, outVector);
206
207 if (matchResult != regExpTest->result) {
208 result = false;
209 if (verbose)
210 printf("Line %d: results mismatch - expected %d got %d\n", lineNumber, regExpTest->result, matchResult);
211 } else if (matchResult != -1) {
212 if (outVector.size() != regExpTest->expectVector.size()) {
213 result = false;
214 if (verbose)
215 printf("Line %d: output vector size mismatch - expected %lu got %lu\n", lineNumber, regExpTest->expectVector.size(), outVector.size());
216 } else if (outVector.size() % 2) {
217 result = false;
218 if (verbose)
219 printf("Line %d: output vector size is odd (%lu), should be even\n", lineNumber, outVector.size());
220 } else {
221 // Check in pairs since the first value of the pair could be -1 in which case the second doesn't matter.
222 size_t pairCount = outVector.size() / 2;
223 for (size_t i = 0; i < pairCount; ++i) {
224 size_t startIndex = i*2;
225 if (outVector[startIndex] != regExpTest->expectVector[startIndex]) {
226 result = false;
227 if (verbose)
228 printf("Line %d: output vector mismatch at index %lu - expected %d got %d\n", lineNumber, startIndex, regExpTest->expectVector[startIndex], outVector[startIndex]);
229 }
230 if ((i > 0) && (regExpTest->expectVector[startIndex] != -1) && (outVector[startIndex+1] != regExpTest->expectVector[startIndex+1])) {
231 result = false;
232 if (verbose)
233 printf("Line %d: output vector mismatch at index %lu - expected %d got %d\n", lineNumber, startIndex+1, regExpTest->expectVector[startIndex+1], outVector[startIndex+1]);
234 }
235 }
236 }
237 }
238
239 return result;
240}
241
242static int scanString(char* buffer, int bufferLength, UStringBuilder& 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(JSGlobalData& globalData, char* line, int lineLength)
309{
310 UStringBuilder pattern;
311
312 if (line[0] != '/')
313 return 0;
314
315 int i = scanString(line + 1, lineLength - 1, pattern, '/') + 1;
316
317 if ((i >= lineLength) || (line[i] != '/'))
318 return 0;
319
320 ++i;
321
322 return RegExp::create(globalData, pattern.toUString(), regExpFlags(line + i));
323}
324
325static RegExpTest* parseTestLine(char* line, int lineLength)
326{
327 UStringBuilder subjectString;
328
329 if ((line[0] != ' ') || (line[1] != '"'))
330 return 0;
331
332 int i = scanString(line + 2, lineLength - 2, subjectString, '"') + 2;
333
334 if ((i >= (lineLength - 2)) || (line[i] != '"') || (line[i+1] != ',') || (line[i+2] != ' '))
335 return 0;
336
337 i += 3;
338
339 int offset;
340
341 if (sscanf(line + i, "%d, ", &offset) != 1)
342 return 0;
343
344 while (line[i] && line[i] != ' ')
345 ++i;
346
347 ++i;
348
349 int matchResult;
350
351 if (sscanf(line + i, "%d, ", &matchResult) != 1)
352 return 0;
353
354 while (line[i] && line[i] != ' ')
355 ++i;
356
357 ++i;
358
359 if (line[i++] != '(')
360 return 0;
361
362 int start, end;
363
364 RegExpTest* result = new RegExpTest();
365
366 result->subject = subjectString.toUString();
367 result->offset = offset;
368 result->result = matchResult;
369
370 while (line[i] && line[i] != ')') {
371 if (sscanf(line + i, "%d, %d", &start, &end) != 2) {
372 delete result;
373 return 0;
374 }
375
376 result->expectVector.append(start);
377 result->expectVector.append(end);
378
379 while (line[i] && (line[i] != ',') && (line[i] != ')'))
380 i++;
381 i++;
382 while (line[i] && (line[i] != ',') && (line[i] != ')'))
383 i++;
384
385 if (line[i] == ')')
386 break;
387 if (!line[i] || (line[i] != ',')) {
388 delete result;
389 return 0;
390 }
391 i++;
392 }
393
394 return result;
395}
396
397static bool runFromFiles(GlobalObject* globalObject, const Vector<UString>& files, bool verbose)
398{
399 UString script;
400 UString fileName;
401 Vector<char> scriptBuffer;
402 unsigned tests = 0;
403 unsigned failures = 0;
404 char* lineBuffer = new char[MaxLineLength + 1];
405
406 JSGlobalData& globalData = globalObject->globalData();
407
408 bool success = true;
409 for (size_t i = 0; i < files.size(); i++) {
410 FILE* testCasesFile = fopen(files[i].utf8().data(), "rb");
411
412 if (!testCasesFile) {
413 printf("Unable to open test data file \"%s\"\n", files[i].utf8().data());
414 continue;
415 }
416
417 RegExp* regexp = 0;
418 size_t lineLength = 0;
419 char* linePtr = 0;
420 unsigned int lineNumber = 0;
421
422 while ((linePtr = fgets(&lineBuffer[0], MaxLineLength, testCasesFile))) {
423 lineLength = strlen(linePtr);
424 if (linePtr[lineLength - 1] == '\n') {
425 linePtr[lineLength - 1] = '\0';
426 --lineLength;
427 }
428 ++lineNumber;
429
430 if (linePtr[0] == '#')
431 continue;
432
433 if (linePtr[0] == '/') {
434 regexp = parseRegExpLine(globalData, linePtr, lineLength);
435 } else if (linePtr[0] == ' ') {
436 RegExpTest* regExpTest = parseTestLine(linePtr, lineLength);
437
438 if (regexp && regExpTest) {
439 ++tests;
440 if (!testOneRegExp(globalData, regexp, regExpTest, verbose, lineNumber)) {
441 failures++;
442 printf("Failure on line %u\n", lineNumber);
443 }
444 }
445
446 if (regExpTest)
447 delete regExpTest;
448 }
449 }
450
451 fclose(testCasesFile);
452 }
453
454 if (failures)
455 printf("%u tests run, %u failures\n", tests, failures);
456 else
457 printf("%u tests passed\n", tests);
458
459 delete[] lineBuffer;
460
461 globalData.dumpSampleData(globalObject->globalExec());
462#if ENABLE(REGEXP_TRACING)
463 globalData.dumpRegExpTrace();
464#endif
465 return success;
466}
467
468#define RUNNING_FROM_XCODE 0
469
470static NO_RETURN void printUsageStatement(bool help = false)
471{
472 fprintf(stderr, "Usage: regexp_test [options] file\n");
473 fprintf(stderr, " -h|--help Prints this help message\n");
474 fprintf(stderr, " -v|--verbose Verbose output\n");
475
476 exit(help ? EXIT_SUCCESS : EXIT_FAILURE);
477}
478
479static void parseArguments(int argc, char** argv, CommandLine& options)
480{
481 int i = 1;
482 for (; i < argc; ++i) {
483 const char* arg = argv[i];
484 if (!strcmp(arg, "-h") || !strcmp(arg, "--help"))
485 printUsageStatement(true);
486 if (!strcmp(arg, "-v") || !strcmp(arg, "--verbose"))
487 options.verbose = true;
488 else
489 options.files.append(argv[i]);
490 }
491
492 for (; i < argc; ++i)
493 options.arguments.append(argv[i]);
494}
495
496int realMain(int argc, char** argv)
497{
498 JSLock lock(SilenceAssertionsOnly);
499
500 RefPtr<JSGlobalData> globalData = JSGlobalData::create(ThreadStackTypeLarge, LargeHeap);
501
502 CommandLine options;
503 parseArguments(argc, argv, options);
504
505 GlobalObject* globalObject = GlobalObject::create(*globalData, GlobalObject::createStructure(*globalData, jsNull()), options.arguments);
506 bool success = runFromFiles(globalObject, options.files, options.verbose);
507
508 return success ? 0 : 3;
509}
Note: See TracBrowser for help on using the repository browser.