source: webkit/trunk/Source/JavaScriptCore/jsc.cpp@ 86785

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

2011-05-18 Oliver Hunt <[email protected]>

Reviewed by Sam Weinig.

JSGlobalObject and some others do GC allocation during initialization, which can cause heap corruption
https://bugs.webkit.org/show_bug.cgi?id=61090

Remove the Structure-free JSGlobalObject constructor and instead always
pass the structure into the JSGlobalObject constructor.
Stop DebuggerActivation creating a new structure every time, and simply
use a single shared structure held by the GlobalData.

  • API/JSContextRef.cpp:
  • debugger/DebuggerActivation.cpp: (JSC::DebuggerActivation::DebuggerActivation):
  • jsc.cpp: (GlobalObject::GlobalObject): (functionRun): (jscmain):
  • runtime/JSGlobalData.cpp: (JSC::JSGlobalData::JSGlobalData): (JSC::JSGlobalData::clearBuiltinStructures):
  • runtime/JSGlobalData.h:
  • runtime/JSGlobalObject.h:

2011-05-18 Oliver Hunt <[email protected]>

Reviewed by Sam Weinig.

JSGlobalObject and some others do GC allocation during initialization, which can cause heap corruption
https://bugs.webkit.org/show_bug.cgi?id=61090

Rather than having Constructor objects create their structure
as part of initialisation, we now pass their expected structure
in as an argument. This required fixing the few custom Constructors
and the code generator.

  • bindings/js/JSAudioConstructor.cpp: (WebCore::JSAudioConstructor::JSAudioConstructor):
  • bindings/js/JSAudioConstructor.h:
  • bindings/js/JSDOMGlobalObject.h: (WebCore::getDOMConstructor): Pass the Constructor objects structure in as an argument
  • bindings/js/JSImageConstructor.cpp: (WebCore::JSImageConstructor::JSImageConstructor):
  • bindings/js/JSImageConstructor.h:
  • bindings/js/JSOptionConstructor.cpp: (WebCore::JSOptionConstructor::JSOptionConstructor):
  • bindings/js/JSOptionConstructor.h:
  • bindings/scripts/CodeGeneratorJS.pm:
  • Property svn:eol-style set to native
File size: 18.9 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 "ExceptionHelpers.h"
29#include "InitializeThreading.h"
30#include "JSArray.h"
31#include "JSFunction.h"
32#include "JSLock.h"
33#include "JSString.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 EncodedJSValue JSC_HOST_CALL functionPrint(ExecState*);
75static EncodedJSValue JSC_HOST_CALL functionDebug(ExecState*);
76static EncodedJSValue JSC_HOST_CALL functionGC(ExecState*);
77static EncodedJSValue JSC_HOST_CALL functionVersion(ExecState*);
78static EncodedJSValue JSC_HOST_CALL functionRun(ExecState*);
79static EncodedJSValue JSC_HOST_CALL functionLoad(ExecState*);
80static EncodedJSValue JSC_HOST_CALL functionCheckSyntax(ExecState*);
81static EncodedJSValue JSC_HOST_CALL functionReadline(ExecState*);
82static NO_RETURN_WITH_VALUE EncodedJSValue JSC_HOST_CALL functionQuit(ExecState*);
83
84#if ENABLE(SAMPLING_FLAGS)
85static EncodedJSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState*);
86static EncodedJSValue JSC_HOST_CALL functionClearSamplingFlags(ExecState*);
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(JSGlobalData&, Structure*, 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(JSGlobalData& globalData, Structure* structure, const Vector<UString>& arguments)
151 : JSGlobalObject(globalData, structure)
152{
153 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 1, Identifier(globalExec(), "debug"), functionDebug));
154 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 1, Identifier(globalExec(), "print"), functionPrint));
155 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 0, Identifier(globalExec(), "quit"), functionQuit));
156 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 0, Identifier(globalExec(), "gc"), functionGC));
157 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 1, Identifier(globalExec(), "version"), functionVersion));
158 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 1, Identifier(globalExec(), "run"), functionRun));
159 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 1, Identifier(globalExec(), "load"), functionLoad));
160 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 1, Identifier(globalExec(), "checkSyntax"), functionCheckSyntax));
161 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 0, Identifier(globalExec(), "readline"), functionReadline));
162
163#if ENABLE(SAMPLING_FLAGS)
164 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 1, Identifier(globalExec(), "setSamplingFlags"), functionSetSamplingFlags));
165 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 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(globalExec()->globalData(), Identifier(globalExec(), "arguments"), array);
172}
173
174EncodedJSValue JSC_HOST_CALL functionPrint(ExecState* exec)
175{
176 for (unsigned i = 0; i < exec->argumentCount(); ++i) {
177 if (i)
178 putchar(' ');
179
180 printf("%s", exec->argument(i).toString(exec).utf8().data());
181 }
182
183 putchar('\n');
184 fflush(stdout);
185 return JSValue::encode(jsUndefined());
186}
187
188EncodedJSValue JSC_HOST_CALL functionDebug(ExecState* exec)
189{
190 fprintf(stderr, "--> %s\n", exec->argument(0).toString(exec).utf8().data());
191 return JSValue::encode(jsUndefined());
192}
193
194EncodedJSValue JSC_HOST_CALL functionGC(ExecState* exec)
195{
196 JSLock lock(SilenceAssertionsOnly);
197 exec->heap()->collectAllGarbage();
198 return JSValue::encode(jsUndefined());
199}
200
201EncodedJSValue JSC_HOST_CALL functionVersion(ExecState*)
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 JSValue::encode(jsUndefined());
206}
207
208EncodedJSValue JSC_HOST_CALL functionRun(ExecState* exec)
209{
210 UString fileName = exec->argument(0).toString(exec);
211 Vector<char> script;
212 if (!fillBufferWithContentsOfFile(fileName, script))
213 return JSValue::encode(throwError(exec, createError(exec, "Could not open file.")));
214
215 GlobalObject* globalObject = new (&exec->globalData()) GlobalObject(exec->globalData(), GlobalObject::createStructure(exec->globalData(), jsNull()), Vector<UString>());
216
217 StopWatch stopWatch;
218 stopWatch.start();
219 evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(script.data(), fileName));
220 stopWatch.stop();
221
222 return JSValue::encode(jsNumber(stopWatch.getElapsedMS()));
223}
224
225EncodedJSValue JSC_HOST_CALL functionLoad(ExecState* exec)
226{
227 UString fileName = exec->argument(0).toString(exec);
228 Vector<char> script;
229 if (!fillBufferWithContentsOfFile(fileName, script))
230 return JSValue::encode(throwError(exec, createError(exec, "Could not open file.")));
231
232 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
233 Completion result = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(script.data(), fileName));
234 if (result.complType() == Throw)
235 throwError(exec, result.value());
236 return JSValue::encode(result.value());
237}
238
239EncodedJSValue JSC_HOST_CALL functionCheckSyntax(ExecState* exec)
240{
241 UString fileName = exec->argument(0).toString(exec);
242 Vector<char> script;
243 if (!fillBufferWithContentsOfFile(fileName, script))
244 return JSValue::encode(throwError(exec, createError(exec, "Could not open file.")));
245
246 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
247
248 StopWatch stopWatch;
249 stopWatch.start();
250 Completion result = checkSyntax(globalObject->globalExec(), makeSource(script.data(), fileName));
251 stopWatch.stop();
252
253 if (result.complType() == Throw)
254 throwError(exec, result.value());
255 return JSValue::encode(jsNumber(stopWatch.getElapsedMS()));
256}
257
258#if ENABLE(SAMPLING_FLAGS)
259EncodedJSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState* exec)
260{
261 for (unsigned i = 0; i < exec->argumentCount(); ++i) {
262 unsigned flag = static_cast<unsigned>(exec->argument(i).toNumber(exec));
263 if ((flag >= 1) && (flag <= 32))
264 SamplingFlags::setFlag(flag);
265 }
266 return JSValue::encode(jsNull());
267}
268
269EncodedJSValue JSC_HOST_CALL functionClearSamplingFlags(ExecState* exec)
270{
271 for (unsigned i = 0; i < exec->argumentCount(); ++i) {
272 unsigned flag = static_cast<unsigned>(exec->argument(i).toNumber(exec));
273 if ((flag >= 1) && (flag <= 32))
274 SamplingFlags::clearFlag(flag);
275 }
276 return JSValue::encode(jsNull());
277}
278#endif
279
280EncodedJSValue JSC_HOST_CALL functionReadline(ExecState* exec)
281{
282 Vector<char, 256> line;
283 int c;
284 while ((c = getchar()) != EOF) {
285 // FIXME: Should we also break on \r?
286 if (c == '\n')
287 break;
288 line.append(c);
289 }
290 line.append('\0');
291 return JSValue::encode(jsString(exec, line.data()));
292}
293
294EncodedJSValue JSC_HOST_CALL functionQuit(ExecState* exec)
295{
296 // Technically, destroying the heap in the middle of JS execution is a no-no,
297 // but we want to maintain compatibility with the Mozilla test suite, so
298 // we pretend that execution has terminated to avoid ASSERTs, then tear down the heap.
299 exec->globalData().dynamicGlobalObject = 0;
300
301 cleanupGlobalData(&exec->globalData());
302 exit(EXIT_SUCCESS);
303
304#if COMPILER(MSVC) && OS(WINCE)
305 // Without this, Visual Studio will complain that this method does not return a value.
306 return JSValue::encode(jsUndefined());
307#endif
308}
309
310// Use SEH for Release builds only to get rid of the crash report dialog
311// (luckily the same tests fail in Release and Debug builds so far). Need to
312// be in a separate main function because the jscmain function requires object
313// unwinding.
314
315#if COMPILER(MSVC) && !defined(_DEBUG) && !OS(WINCE)
316#define TRY __try {
317#define EXCEPT(x) } __except (EXCEPTION_EXECUTE_HANDLER) { x; }
318#else
319#define TRY
320#define EXCEPT(x)
321#endif
322
323int jscmain(int argc, char** argv, JSGlobalData*);
324
325int main(int argc, char** argv)
326{
327#if OS(WINDOWS)
328#if !OS(WINCE)
329 // Cygwin calls ::SetErrorMode(SEM_FAILCRITICALERRORS), which we will inherit. This is bad for
330 // testing/debugging, as it causes the post-mortem debugger not to be invoked. We reset the
331 // error mode here to work around Cygwin's behavior. See <http://webkit.org/b/55222>.
332 ::SetErrorMode(0);
333#endif
334
335#if defined(_DEBUG)
336 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
337 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
338 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
339 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
340 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
341 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
342#endif
343
344 timeBeginPeriod(1);
345#endif
346
347#if PLATFORM(QT)
348 QCoreApplication app(argc, argv);
349#endif
350
351 // Initialize JSC before getting JSGlobalData.
352 JSC::initializeThreading();
353
354 // We can't use destructors in the following code because it uses Windows
355 // Structured Exception Handling
356 int res = 0;
357 JSGlobalData* globalData = JSGlobalData::create(ThreadStackTypeLarge).leakRef();
358 TRY
359 res = jscmain(argc, argv, globalData);
360 EXCEPT(res = 3)
361
362 cleanupGlobalData(globalData);
363 return res;
364}
365
366static void cleanupGlobalData(JSGlobalData* globalData)
367{
368 JSLock lock(SilenceAssertionsOnly);
369 globalData->clearBuiltinStructures();
370 globalData->heap.destroy();
371 globalData->deref();
372}
373
374static bool runWithScripts(GlobalObject* globalObject, const Vector<Script>& scripts, bool dump)
375{
376 UString script;
377 UString fileName;
378 Vector<char> scriptBuffer;
379
380 if (dump)
381 BytecodeGenerator::setDumpsGeneratedCode(true);
382
383 JSGlobalData& globalData = globalObject->globalData();
384
385#if ENABLE(SAMPLING_FLAGS)
386 SamplingFlags::start();
387#endif
388
389 bool success = true;
390 for (size_t i = 0; i < scripts.size(); i++) {
391 if (scripts[i].isFile) {
392 fileName = scripts[i].argument;
393 if (!fillBufferWithContentsOfFile(fileName, scriptBuffer))
394 return false; // fail early so we can catch missing files
395 script = scriptBuffer.data();
396 } else {
397 script = scripts[i].argument;
398 fileName = "[Command Line]";
399 }
400
401 globalData.startSampling();
402
403 Completion completion = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(script, fileName));
404 success = success && completion.complType() != Throw;
405 if (dump) {
406 if (completion.complType() == Throw)
407 printf("Exception: %s\n", completion.value().toString(globalObject->globalExec()).utf8().data());
408 else
409 printf("End: %s\n", completion.value().toString(globalObject->globalExec()).utf8().data());
410 }
411
412 globalData.stopSampling();
413 globalObject->globalExec()->clearException();
414 }
415
416#if ENABLE(SAMPLING_FLAGS)
417 SamplingFlags::stop();
418#endif
419 globalData.dumpSampleData(globalObject->globalExec());
420#if ENABLE(SAMPLING_COUNTERS)
421 AbstractSamplingCounter::dump();
422#endif
423#if ENABLE(REGEXP_TRACING)
424 globalData.dumpRegExpTrace();
425#endif
426 return success;
427}
428
429#define RUNNING_FROM_XCODE 0
430
431static void runInteractive(GlobalObject* globalObject)
432{
433 while (true) {
434#if HAVE(READLINE) && !RUNNING_FROM_XCODE
435 char* line = readline(interactivePrompt);
436 if (!line)
437 break;
438 if (line[0])
439 add_history(line);
440 Completion completion = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(line, interpreterName));
441 free(line);
442#else
443 printf("%s", interactivePrompt);
444 Vector<char, 256> line;
445 int c;
446 while ((c = getchar()) != EOF) {
447 // FIXME: Should we also break on \r?
448 if (c == '\n')
449 break;
450 line.append(c);
451 }
452 if (line.isEmpty())
453 break;
454 line.append('\0');
455 Completion completion = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(line.data(), interpreterName));
456#endif
457 if (completion.complType() == Throw)
458 printf("Exception: %s\n", completion.value().toString(globalObject->globalExec()).utf8().data());
459 else
460 printf("%s\n", completion.value().toString(globalObject->globalExec()).utf8().data());
461
462 globalObject->globalExec()->clearException();
463 }
464 printf("\n");
465}
466
467static NO_RETURN void printUsageStatement(JSGlobalData* globalData, bool help = false)
468{
469 fprintf(stderr, "Usage: jsc [options] [files] [-- arguments]\n");
470 fprintf(stderr, " -d Dumps bytecode (debug builds only)\n");
471 fprintf(stderr, " -e Evaluate argument as script code\n");
472 fprintf(stderr, " -f Specifies a source file (deprecated)\n");
473 fprintf(stderr, " -h|--help Prints this help message\n");
474 fprintf(stderr, " -i Enables interactive mode (default if no files are specified)\n");
475#if HAVE(SIGNAL_H)
476 fprintf(stderr, " -s Installs signal handlers that exit on a crash (Unix platforms only)\n");
477#endif
478
479 cleanupGlobalData(globalData);
480 exit(help ? EXIT_SUCCESS : EXIT_FAILURE);
481}
482
483static void parseArguments(int argc, char** argv, Options& options, JSGlobalData* globalData)
484{
485 int i = 1;
486 for (; i < argc; ++i) {
487 const char* arg = argv[i];
488 if (!strcmp(arg, "-f")) {
489 if (++i == argc)
490 printUsageStatement(globalData);
491 options.scripts.append(Script(true, argv[i]));
492 continue;
493 }
494 if (!strcmp(arg, "-e")) {
495 if (++i == argc)
496 printUsageStatement(globalData);
497 options.scripts.append(Script(false, argv[i]));
498 continue;
499 }
500 if (!strcmp(arg, "-i")) {
501 options.interactive = true;
502 continue;
503 }
504 if (!strcmp(arg, "-d")) {
505 options.dump = true;
506 continue;
507 }
508 if (!strcmp(arg, "-s")) {
509#if HAVE(SIGNAL_H)
510 signal(SIGILL, _exit);
511 signal(SIGFPE, _exit);
512 signal(SIGBUS, _exit);
513 signal(SIGSEGV, _exit);
514#endif
515 continue;
516 }
517 if (!strcmp(arg, "--")) {
518 ++i;
519 break;
520 }
521 if (!strcmp(arg, "-h") || !strcmp(arg, "--help"))
522 printUsageStatement(globalData, true);
523 options.scripts.append(Script(true, argv[i]));
524 }
525
526 if (options.scripts.isEmpty())
527 options.interactive = true;
528
529 for (; i < argc; ++i)
530 options.arguments.append(argv[i]);
531}
532
533int jscmain(int argc, char** argv, JSGlobalData* globalData)
534{
535 JSLock lock(SilenceAssertionsOnly);
536
537 Options options;
538 parseArguments(argc, argv, options, globalData);
539
540 GlobalObject* globalObject = new (globalData) GlobalObject(*globalData, GlobalObject::createStructure(*globalData, jsNull()), options.arguments);
541 bool success = runWithScripts(globalObject, options.scripts, options.dump);
542 if (options.interactive && success)
543 runInteractive(globalObject);
544
545 return success ? 0 : 3;
546}
547
548static bool fillBufferWithContentsOfFile(const UString& fileName, Vector<char>& buffer)
549{
550 FILE* f = fopen(fileName.utf8().data(), "r");
551 if (!f) {
552 fprintf(stderr, "Could not open file: %s\n", fileName.utf8().data());
553 return false;
554 }
555
556 size_t bufferSize = 0;
557 size_t bufferCapacity = 1024;
558
559 buffer.resize(bufferCapacity);
560
561 while (!feof(f) && !ferror(f)) {
562 bufferSize += fread(buffer.data() + bufferSize, 1, bufferCapacity - bufferSize, f);
563 if (bufferSize == bufferCapacity) { // guarantees space for trailing '\0'
564 bufferCapacity *= 2;
565 buffer.resize(bufferCapacity);
566 }
567 }
568 fclose(f);
569 buffer[bufferSize] = '\0';
570
571 if (buffer[0] == '#' && buffer[1] == '!')
572 buffer[0] = buffer[1] = '/';
573
574 return true;
575}
Note: See TracBrowser for help on using the repository browser.