source: webkit/trunk/JavaScriptCore/jsc.cpp@ 54402

Last change on this file since 54402 was 53460, checked in by [email protected], 15 years ago

JavaScriptCore: REGRESSION (52082): Crash on worker thread when reloading http://radnan.public.iastate.edu/procedural/
https://bugs.webkit.org/show_bug.cgi?id=33826

Reviewed by Oliver Hunt.

This bug was caused by a GC-protected object being destroyed early by
Heap::destroy. Clients of the GC protect APIs (reasonably) expect pointers
to GC-protected memory to be valid.

The solution is to do two passes of tear-down in Heap::destroy. The first
pass tears down all unprotected objects. The second pass ASSERTs that all
previously protected objects are now unprotected, and then tears down
all perviously protected objects. These two passes simulate the two passes
that would have been required to free a protected object during normal GC.

  • API/JSContextRef.cpp: Removed some ASSERTs that have moved into Heap.
  • runtime/Collector.cpp:

(JSC::Heap::destroy): Moved ASSERTs to here.
(JSC::Heap::freeBlock): Tidied up the use of didShrink by moving its
setter to the function that does the shrinking.
(JSC::Heap::freeBlocks): Implemented above algorithm.
(JSC::Heap::shrinkBlocks): Tidied up the use of didShrink.

WebCore: REGRESSION (52082): Crash on worker thread when reloading http://radnan.public.iastate.edu/procedural/
https://bugs.webkit.org/show_bug.cgi?id=33826

Reviewed by Oliver Hunt.

Test: fast/workers/worker-gc2.html

  • bindings/js/WorkerScriptController.cpp:

(WebCore::WorkerScriptController::~WorkerScriptController): Removed some
ASSERTs that have moved to JavaScriptCore.

LayoutTests: REGRESSION (52082): Crash on worker thread when reloading http://radnan.public.iastate.edu/procedural/
https://bugs.webkit.org/show_bug.cgi?id=33826

Reviewed by Oliver Hunt.

Added a test for this edge case.

  • fast/workers/resources/worker-gc2.js: Added.

(Dummy):

  • fast/workers/worker-gc2.html: Added.
  • Property svn:eol-style set to native
File size: 18.6 KB
Line 
1/*
2 * Copyright (C) 1999-2000 Harri Porten ([email protected])
3 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
4 * Copyright (C) 2006 Bjoern Graf ([email protected])
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Library General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Library General Public License for more details.
15 *
16 * You should have received a copy of the GNU Library General Public License
17 * along with this library; see the file COPYING.LIB. If not, write to
18 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19 * Boston, MA 02110-1301, USA.
20 *
21 */
22
23#include "config.h"
24
25#include "BytecodeGenerator.h"
26#include "Completion.h"
27#include "CurrentTime.h"
28#include "InitializeThreading.h"
29#include "JSArray.h"
30#include "JSFunction.h"
31#include "JSLock.h"
32#include "JSString.h"
33#include "PrototypeFunction.h"
34#include "SamplingTool.h"
35#include <math.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39
40#if !OS(WINDOWS)
41#include <unistd.h>
42#endif
43
44#if HAVE(READLINE)
45#include <readline/history.h>
46#include <readline/readline.h>
47#endif
48
49#if HAVE(SYS_TIME_H)
50#include <sys/time.h>
51#endif
52
53#if HAVE(SIGNAL_H)
54#include <signal.h>
55#endif
56
57#if COMPILER(MSVC) && !OS(WINCE)
58#include <crtdbg.h>
59#include <mmsystem.h>
60#include <windows.h>
61#endif
62
63#if PLATFORM(QT)
64#include <QCoreApplication>
65#include <QDateTime>
66#endif
67
68using namespace JSC;
69using namespace WTF;
70
71static void cleanupGlobalData(JSGlobalData*);
72static bool fillBufferWithContentsOfFile(const UString& fileName, Vector<char>& buffer);
73
74static JSValue JSC_HOST_CALL functionPrint(ExecState*, JSObject*, JSValue, const ArgList&);
75static JSValue JSC_HOST_CALL functionDebug(ExecState*, JSObject*, JSValue, const ArgList&);
76static JSValue JSC_HOST_CALL functionGC(ExecState*, JSObject*, JSValue, const ArgList&);
77static JSValue JSC_HOST_CALL functionVersion(ExecState*, JSObject*, JSValue, const ArgList&);
78static JSValue JSC_HOST_CALL functionRun(ExecState*, JSObject*, JSValue, const ArgList&);
79static JSValue JSC_HOST_CALL functionLoad(ExecState*, JSObject*, JSValue, const ArgList&);
80static JSValue JSC_HOST_CALL functionCheckSyntax(ExecState*, JSObject*, JSValue, const ArgList&);
81static JSValue JSC_HOST_CALL functionReadline(ExecState*, JSObject*, JSValue, const ArgList&);
82static NO_RETURN JSValue JSC_HOST_CALL functionQuit(ExecState*, JSObject*, JSValue, const ArgList&);
83
84#if ENABLE(SAMPLING_FLAGS)
85static JSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState*, JSObject*, JSValue, const ArgList&);
86static JSValue JSC_HOST_CALL functionClearSamplingFlags(ExecState*, JSObject*, JSValue, const ArgList&);
87#endif
88
89struct Script {
90 bool isFile;
91 char* argument;
92
93 Script(bool isFile, char *argument)
94 : isFile(isFile)
95 , argument(argument)
96 {
97 }
98};
99
100struct Options {
101 Options()
102 : interactive(false)
103 , dump(false)
104 {
105 }
106
107 bool interactive;
108 bool dump;
109 Vector<Script> scripts;
110 Vector<UString> arguments;
111};
112
113static const char interactivePrompt[] = "> ";
114static const UString interpreterName("Interpreter");
115
116class StopWatch {
117public:
118 void start();
119 void stop();
120 long getElapsedMS(); // call stop() first
121
122private:
123 double m_startTime;
124 double m_stopTime;
125};
126
127void StopWatch::start()
128{
129 m_startTime = currentTime();
130}
131
132void StopWatch::stop()
133{
134 m_stopTime = currentTime();
135}
136
137long StopWatch::getElapsedMS()
138{
139 return static_cast<long>((m_stopTime - m_startTime) * 1000);
140}
141
142class GlobalObject : public JSGlobalObject {
143public:
144 GlobalObject(const Vector<UString>& arguments);
145 virtual UString className() const { return "global"; }
146};
147COMPILE_ASSERT(!IsInteger<GlobalObject>::value, WTF_IsInteger_GlobalObject_false);
148ASSERT_CLASS_FITS_IN_CELL(GlobalObject);
149
150GlobalObject::GlobalObject(const Vector<UString>& arguments)
151 : JSGlobalObject()
152{
153 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "debug"), functionDebug));
154 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "print"), functionPrint));
155 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 0, Identifier(globalExec(), "quit"), functionQuit));
156 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 0, Identifier(globalExec(), "gc"), functionGC));
157 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "version"), functionVersion));
158 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "run"), functionRun));
159 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "load"), functionLoad));
160 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "checkSyntax"), functionCheckSyntax));
161 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 0, Identifier(globalExec(), "readline"), functionReadline));
162
163#if ENABLE(SAMPLING_FLAGS)
164 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "setSamplingFlags"), functionSetSamplingFlags));
165 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "clearSamplingFlags"), functionClearSamplingFlags));
166#endif
167
168 JSObject* array = constructEmptyArray(globalExec());
169 for (size_t i = 0; i < arguments.size(); ++i)
170 array->put(globalExec(), i, jsString(globalExec(), arguments[i]));
171 putDirect(Identifier(globalExec(), "arguments"), array);
172}
173
174JSValue JSC_HOST_CALL functionPrint(ExecState* exec, JSObject*, JSValue, const ArgList& args)
175{
176 for (unsigned i = 0; i < args.size(); ++i) {
177 if (i)
178 putchar(' ');
179
180 printf("%s", args.at(i).toString(exec).UTF8String().c_str());
181 }
182
183 putchar('\n');
184 fflush(stdout);
185 return jsUndefined();
186}
187
188JSValue JSC_HOST_CALL functionDebug(ExecState* exec, JSObject*, JSValue, const ArgList& args)
189{
190 fprintf(stderr, "--> %s\n", args.at(0).toString(exec).UTF8String().c_str());
191 return jsUndefined();
192}
193
194JSValue JSC_HOST_CALL functionGC(ExecState* exec, JSObject*, JSValue, const ArgList&)
195{
196 JSLock lock(SilenceAssertionsOnly);
197 exec->heap()->collectAllGarbage();
198 return jsUndefined();
199}
200
201JSValue JSC_HOST_CALL functionVersion(ExecState*, JSObject*, JSValue, const ArgList&)
202{
203 // We need this function for compatibility with the Mozilla JS tests but for now
204 // we don't actually do any version-specific handling
205 return jsUndefined();
206}
207
208JSValue JSC_HOST_CALL functionRun(ExecState* exec, JSObject*, JSValue, const ArgList& args)
209{
210 StopWatch stopWatch;
211 UString fileName = args.at(0).toString(exec);
212 Vector<char> script;
213 if (!fillBufferWithContentsOfFile(fileName, script))
214 return throwError(exec, GeneralError, "Could not open file.");
215
216 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
217
218 stopWatch.start();
219 evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(script.data(), fileName));
220 stopWatch.stop();
221
222 return jsNumber(globalObject->globalExec(), stopWatch.getElapsedMS());
223}
224
225JSValue JSC_HOST_CALL functionLoad(ExecState* exec, JSObject* o, JSValue v, const ArgList& args)
226{
227 UNUSED_PARAM(o);
228 UNUSED_PARAM(v);
229 UString fileName = args.at(0).toString(exec);
230 Vector<char> script;
231 if (!fillBufferWithContentsOfFile(fileName, script))
232 return throwError(exec, GeneralError, "Could not open file.");
233
234 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
235 Completion result = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(script.data(), fileName));
236 if (result.complType() == Throw)
237 exec->setException(result.value());
238 return result.value();
239}
240
241JSValue JSC_HOST_CALL functionCheckSyntax(ExecState* exec, JSObject* o, JSValue v, const ArgList& args)
242{
243 UNUSED_PARAM(o);
244 UNUSED_PARAM(v);
245 UString fileName = args.at(0).toString(exec);
246 Vector<char> script;
247 if (!fillBufferWithContentsOfFile(fileName, script))
248 return throwError(exec, GeneralError, "Could not open file.");
249
250 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
251 Completion result = checkSyntax(globalObject->globalExec(), makeSource(script.data(), fileName));
252 if (result.complType() == Throw)
253 exec->setException(result.value());
254 return result.value();
255}
256
257#if ENABLE(SAMPLING_FLAGS)
258JSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState* exec, JSObject*, JSValue, const ArgList& args)
259{
260 for (unsigned i = 0; i < args.size(); ++i) {
261 unsigned flag = static_cast<unsigned>(args.at(i).toNumber(exec));
262 if ((flag >= 1) && (flag <= 32))
263 SamplingFlags::setFlag(flag);
264 }
265 return jsNull();
266}
267
268JSValue JSC_HOST_CALL functionClearSamplingFlags(ExecState* exec, JSObject*, JSValue, const ArgList& args)
269{
270 for (unsigned i = 0; i < args.size(); ++i) {
271 unsigned flag = static_cast<unsigned>(args.at(i).toNumber(exec));
272 if ((flag >= 1) && (flag <= 32))
273 SamplingFlags::clearFlag(flag);
274 }
275 return jsNull();
276}
277#endif
278
279JSValue JSC_HOST_CALL functionReadline(ExecState* exec, JSObject*, JSValue, const ArgList&)
280{
281 Vector<char, 256> line;
282 int c;
283 while ((c = getchar()) != EOF) {
284 // FIXME: Should we also break on \r?
285 if (c == '\n')
286 break;
287 line.append(c);
288 }
289 line.append('\0');
290 return jsString(exec, line.data());
291}
292
293JSValue JSC_HOST_CALL functionQuit(ExecState* exec, JSObject*, JSValue, const ArgList&)
294{
295 // Technically, destroying the heap in the middle of JS execution is a no-no,
296 // but we want to maintain compatibility with the Mozilla test suite, so
297 // we pretend that execution has terminated to avoid ASSERTs, then tear down the heap.
298 exec->globalData().dynamicGlobalObject = 0;
299
300 cleanupGlobalData(&exec->globalData());
301 exit(EXIT_SUCCESS);
302
303#if COMPILER(MSVC) && OS(WINCE)
304 // Without this, Visual Studio will complain that this method does not return a value.
305 return jsUndefined();
306#endif
307}
308
309// Use SEH for Release builds only to get rid of the crash report dialog
310// (luckily the same tests fail in Release and Debug builds so far). Need to
311// be in a separate main function because the jscmain function requires object
312// unwinding.
313
314#if COMPILER(MSVC) && !defined(_DEBUG)
315#define TRY __try {
316#define EXCEPT(x) } __except (EXCEPTION_EXECUTE_HANDLER) { x; }
317#else
318#define TRY
319#define EXCEPT(x)
320#endif
321
322int jscmain(int argc, char** argv, JSGlobalData*);
323
324int main(int argc, char** argv)
325{
326#if defined(_DEBUG) && OS(WINDOWS)
327 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
328 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
329 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
330 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
331 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
332 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
333#endif
334
335#if COMPILER(MSVC) && !OS(WINCE)
336 timeBeginPeriod(1);
337#endif
338
339#if PLATFORM(QT)
340 QCoreApplication app(argc, argv);
341#endif
342
343 // Initialize JSC before getting JSGlobalData.
344 JSC::initializeThreading();
345
346 // We can't use destructors in the following code because it uses Windows
347 // Structured Exception Handling
348 int res = 0;
349 JSGlobalData* globalData = JSGlobalData::create().releaseRef();
350 TRY
351 res = jscmain(argc, argv, globalData);
352 EXCEPT(res = 3)
353
354 cleanupGlobalData(globalData);
355 return res;
356}
357
358static void cleanupGlobalData(JSGlobalData* globalData)
359{
360 JSLock lock(SilenceAssertionsOnly);
361 globalData->heap.destroy();
362 globalData->deref();
363}
364
365static bool runWithScripts(GlobalObject* globalObject, const Vector<Script>& scripts, bool dump)
366{
367 UString script;
368 UString fileName;
369 Vector<char> scriptBuffer;
370
371 if (dump)
372 BytecodeGenerator::setDumpsGeneratedCode(true);
373
374 JSGlobalData* globalData = globalObject->globalData();
375
376#if ENABLE(SAMPLING_FLAGS)
377 SamplingFlags::start();
378#endif
379
380 bool success = true;
381 for (size_t i = 0; i < scripts.size(); i++) {
382 if (scripts[i].isFile) {
383 fileName = scripts[i].argument;
384 if (!fillBufferWithContentsOfFile(fileName, scriptBuffer))
385 return false; // fail early so we can catch missing files
386 script = scriptBuffer.data();
387 } else {
388 script = scripts[i].argument;
389 fileName = "[Command Line]";
390 }
391
392 globalData->startSampling();
393
394 Completion completion = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(script, fileName));
395 success = success && completion.complType() != Throw;
396 if (dump) {
397 if (completion.complType() == Throw)
398 printf("Exception: %s\n", completion.value().toString(globalObject->globalExec()).ascii());
399 else
400 printf("End: %s\n", completion.value().toString(globalObject->globalExec()).ascii());
401 }
402
403 globalData->stopSampling();
404 globalObject->globalExec()->clearException();
405 }
406
407#if ENABLE(SAMPLING_FLAGS)
408 SamplingFlags::stop();
409#endif
410 globalData->dumpSampleData(globalObject->globalExec());
411#if ENABLE(SAMPLING_COUNTERS)
412 AbstractSamplingCounter::dump();
413#endif
414 return success;
415}
416
417#define RUNNING_FROM_XCODE 0
418
419static void runInteractive(GlobalObject* globalObject)
420{
421 while (true) {
422#if HAVE(READLINE) && !RUNNING_FROM_XCODE
423 char* line = readline(interactivePrompt);
424 if (!line)
425 break;
426 if (line[0])
427 add_history(line);
428 Completion completion = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(line, interpreterName));
429 free(line);
430#else
431 printf("%s", interactivePrompt);
432 Vector<char, 256> line;
433 int c;
434 while ((c = getchar()) != EOF) {
435 // FIXME: Should we also break on \r?
436 if (c == '\n')
437 break;
438 line.append(c);
439 }
440 if (line.isEmpty())
441 break;
442 line.append('\0');
443 Completion completion = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(line.data(), interpreterName));
444#endif
445 if (completion.complType() == Throw)
446 printf("Exception: %s\n", completion.value().toString(globalObject->globalExec()).ascii());
447 else
448 printf("%s\n", completion.value().toString(globalObject->globalExec()).UTF8String().c_str());
449
450 globalObject->globalExec()->clearException();
451 }
452 printf("\n");
453}
454
455static NO_RETURN void printUsageStatement(JSGlobalData* globalData, bool help = false)
456{
457 fprintf(stderr, "Usage: jsc [options] [files] [-- arguments]\n");
458 fprintf(stderr, " -d Dumps bytecode (debug builds only)\n");
459 fprintf(stderr, " -e Evaluate argument as script code\n");
460 fprintf(stderr, " -f Specifies a source file (deprecated)\n");
461 fprintf(stderr, " -h|--help Prints this help message\n");
462 fprintf(stderr, " -i Enables interactive mode (default if no files are specified)\n");
463#if HAVE(SIGNAL_H)
464 fprintf(stderr, " -s Installs signal handlers that exit on a crash (Unix platforms only)\n");
465#endif
466
467 cleanupGlobalData(globalData);
468 exit(help ? EXIT_SUCCESS : EXIT_FAILURE);
469}
470
471static void parseArguments(int argc, char** argv, Options& options, JSGlobalData* globalData)
472{
473 int i = 1;
474 for (; i < argc; ++i) {
475 const char* arg = argv[i];
476 if (!strcmp(arg, "-f")) {
477 if (++i == argc)
478 printUsageStatement(globalData);
479 options.scripts.append(Script(true, argv[i]));
480 continue;
481 }
482 if (!strcmp(arg, "-e")) {
483 if (++i == argc)
484 printUsageStatement(globalData);
485 options.scripts.append(Script(false, argv[i]));
486 continue;
487 }
488 if (!strcmp(arg, "-i")) {
489 options.interactive = true;
490 continue;
491 }
492 if (!strcmp(arg, "-d")) {
493 options.dump = true;
494 continue;
495 }
496 if (!strcmp(arg, "-s")) {
497#if HAVE(SIGNAL_H)
498 signal(SIGILL, _exit);
499 signal(SIGFPE, _exit);
500 signal(SIGBUS, _exit);
501 signal(SIGSEGV, _exit);
502#endif
503 continue;
504 }
505 if (!strcmp(arg, "--")) {
506 ++i;
507 break;
508 }
509 if (!strcmp(arg, "-h") || !strcmp(arg, "--help"))
510 printUsageStatement(globalData, true);
511 options.scripts.append(Script(true, argv[i]));
512 }
513
514 if (options.scripts.isEmpty())
515 options.interactive = true;
516
517 for (; i < argc; ++i)
518 options.arguments.append(argv[i]);
519}
520
521int jscmain(int argc, char** argv, JSGlobalData* globalData)
522{
523 JSLock lock(SilenceAssertionsOnly);
524
525 Options options;
526 parseArguments(argc, argv, options, globalData);
527
528 GlobalObject* globalObject = new (globalData) GlobalObject(options.arguments);
529 bool success = runWithScripts(globalObject, options.scripts, options.dump);
530 if (options.interactive && success)
531 runInteractive(globalObject);
532
533 return success ? 0 : 3;
534}
535
536static bool fillBufferWithContentsOfFile(const UString& fileName, Vector<char>& buffer)
537{
538 FILE* f = fopen(fileName.UTF8String().c_str(), "r");
539 if (!f) {
540 fprintf(stderr, "Could not open file: %s\n", fileName.UTF8String().c_str());
541 return false;
542 }
543
544 size_t bufferSize = 0;
545 size_t bufferCapacity = 1024;
546
547 buffer.resize(bufferCapacity);
548
549 while (!feof(f) && !ferror(f)) {
550 bufferSize += fread(buffer.data() + bufferSize, 1, bufferCapacity - bufferSize, f);
551 if (bufferSize == bufferCapacity) { // guarantees space for trailing '\0'
552 bufferCapacity *= 2;
553 buffer.resize(bufferCapacity);
554 }
555 }
556 fclose(f);
557 buffer[bufferSize] = '\0';
558
559 return true;
560}
Note: See TracBrowser for help on using the repository browser.