source: webkit/trunk/JavaScriptCore/kjs/Shell.cpp@ 35906

Last change on this file since 35906 was 35853, checked in by [email protected], 17 years ago

Reviewed by Geoff Garen.

Bring back shared JSGlobalData and implicit locking, because too many clients rely on it.

  • Property svn:eol-style set to native
File size: 15.8 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 "ObjectPrototype.h"
26#include "ObjectConstructor.h"
27
28#include "CodeGenerator.h"
29#include "InitializeThreading.h"
30#include "JSArray.h"
31#include "JSFunction.h"
32#include "JSGlobalObject.h"
33#include "JSLock.h"
34#include "JSObject.h"
35#include "Parser.h"
36#include "PrototypeFunction.h"
37#include "SamplingTool.h"
38#include "collector.h"
39#include "completion.h"
40#include "interpreter.h"
41#include "nodes.h"
42#include "protect.h"
43#include <math.h>
44#include <stdio.h>
45#include <string.h>
46#include <wtf/Assertions.h>
47#include <wtf/HashTraits.h>
48
49#if !PLATFORM(WIN_OS)
50#include <unistd.h>
51#endif
52
53#if HAVE(READLINE)
54#include <readline/history.h>
55#include <readline/readline.h>
56#endif
57
58#if HAVE(SYS_TIME_H)
59#include <sys/time.h>
60#endif
61
62#if PLATFORM(UNIX)
63#include <signal.h>
64#endif
65
66#if COMPILER(MSVC)
67#include <crtdbg.h>
68#include <windows.h>
69#endif
70
71#if PLATFORM(QT)
72#include <QDateTime>
73#endif
74
75using namespace KJS;
76using namespace WTF;
77
78static bool fillBufferWithContentsOfFile(const UString& fileName, Vector<char>& buffer);
79
80static JSValue* functionPrint(ExecState*, JSObject*, JSValue*, const ArgList&);
81static JSValue* functionDebug(ExecState*, JSObject*, JSValue*, const ArgList&);
82static JSValue* functionGC(ExecState*, JSObject*, JSValue*, const ArgList&);
83static JSValue* functionVersion(ExecState*, JSObject*, JSValue*, const ArgList&);
84static JSValue* functionRun(ExecState*, JSObject*, JSValue*, const ArgList&);
85static JSValue* functionLoad(ExecState*, JSObject*, JSValue*, const ArgList&);
86static JSValue* functionReadline(ExecState*, JSObject*, JSValue*, const ArgList&);
87static JSValue* functionQuit(ExecState*, JSObject*, JSValue*, const ArgList&);
88
89struct Options {
90 Options()
91 : interactive(false)
92 , prettyPrint(false)
93 , dump(false)
94 {
95 }
96
97 bool interactive;
98 bool prettyPrint;
99 bool dump;
100 Vector<UString> fileNames;
101 Vector<UString> arguments;
102};
103
104static const char interactivePrompt[] = "> ";
105static const UString interpreterName("Interpreter");
106
107class StopWatch {
108public:
109 void start();
110 void stop();
111 long getElapsedMS(); // call stop() first
112
113private:
114#if PLATFORM(QT)
115 uint m_startTime;
116 uint m_stopTime;
117#elif PLATFORM(WIN_OS)
118 DWORD m_startTime;
119 DWORD m_stopTime;
120#else
121 // Windows does not have timeval, disabling this class for now (bug 7399)
122 timeval m_startTime;
123 timeval m_stopTime;
124#endif
125};
126
127void StopWatch::start()
128{
129#if PLATFORM(QT)
130 QDateTime t = QDateTime::currentDateTime();
131 m_startTime = t.toTime_t() * 1000 + t.time().msec();
132#elif PLATFORM(WIN_OS)
133 m_startTime = timeGetTime();
134#else
135 gettimeofday(&m_startTime, 0);
136#endif
137}
138
139void StopWatch::stop()
140{
141#if PLATFORM(QT)
142 QDateTime t = QDateTime::currentDateTime();
143 m_stopTime = t.toTime_t() * 1000 + t.time().msec();
144#elif PLATFORM(WIN_OS)
145 m_stopTime = timeGetTime();
146#else
147 gettimeofday(&m_stopTime, 0);
148#endif
149}
150
151long StopWatch::getElapsedMS()
152{
153#if PLATFORM(WIN_OS) || PLATFORM(QT)
154 return m_stopTime - m_startTime;
155#else
156 timeval elapsedTime;
157 timersub(&m_stopTime, &m_startTime, &elapsedTime);
158
159 return elapsedTime.tv_sec * 1000 + lroundf(elapsedTime.tv_usec / 1000.0f);
160#endif
161}
162
163class GlobalObject : public JSGlobalObject {
164public:
165 GlobalObject(Vector<UString>& arguments);
166 virtual UString className() const { return "global"; }
167};
168COMPILE_ASSERT(!IsInteger<GlobalObject>::value, WTF_IsInteger_GlobalObject_false);
169ASSERT_CLASS_FITS_IN_CELL(GlobalObject);
170
171GlobalObject::GlobalObject(Vector<UString>& arguments)
172{
173 putDirectFunction(globalExec(), new (globalExec()) PrototypeFunction(globalExec(), functionPrototype(), 1, Identifier(globalExec(), "debug"), functionDebug));
174 putDirectFunction(globalExec(), new (globalExec()) PrototypeFunction(globalExec(), functionPrototype(), 1, Identifier(globalExec(), "print"), functionPrint));
175 putDirectFunction(globalExec(), new (globalExec()) PrototypeFunction(globalExec(), functionPrototype(), 0, Identifier(globalExec(), "quit"), functionQuit));
176 putDirectFunction(globalExec(), new (globalExec()) PrototypeFunction(globalExec(), functionPrototype(), 0, Identifier(globalExec(), "gc"), functionGC));
177 putDirectFunction(globalExec(), new (globalExec()) PrototypeFunction(globalExec(), functionPrototype(), 1, Identifier(globalExec(), "version"), functionVersion));
178 putDirectFunction(globalExec(), new (globalExec()) PrototypeFunction(globalExec(), functionPrototype(), 1, Identifier(globalExec(), "run"), functionRun));
179 putDirectFunction(globalExec(), new (globalExec()) PrototypeFunction(globalExec(), functionPrototype(), 1, Identifier(globalExec(), "load"), functionLoad));
180 putDirectFunction(globalExec(), new (globalExec()) PrototypeFunction(globalExec(), functionPrototype(), 0, Identifier(globalExec(), "readline"), functionReadline));
181
182 JSObject* array = constructEmptyArray(globalExec());
183 for (size_t i = 0; i < arguments.size(); ++i)
184 array->put(globalExec(), i, jsString(globalExec(), arguments[i]));
185 putDirect(Identifier(globalExec(), "arguments"), array);
186
187 Interpreter::setShouldPrintExceptions(true);
188}
189
190JSValue* functionPrint(ExecState* exec, JSObject*, JSValue*, const ArgList& args)
191{
192 for (unsigned i = 0; i < args.size(); ++i) {
193 if (i != 0)
194 putchar(' ');
195
196 printf("%s", args.at(exec, i)->toString(exec).UTF8String().c_str());
197 }
198
199 putchar('\n');
200 fflush(stdout);
201 return jsUndefined();
202}
203
204JSValue* functionDebug(ExecState* exec, JSObject*, JSValue*, const ArgList& args)
205{
206 fprintf(stderr, "--> %s\n", args.at(exec, 0)->toString(exec).UTF8String().c_str());
207 return jsUndefined();
208}
209
210JSValue* functionGC(ExecState* exec, JSObject*, JSValue*, const ArgList&)
211{
212 JSLock lock(false);
213 exec->heap()->collect();
214 return jsUndefined();
215}
216
217JSValue* functionVersion(ExecState*, JSObject*, JSValue*, const ArgList&)
218{
219 // We need this function for compatibility with the Mozilla JS tests but for now
220 // we don't actually do any version-specific handling
221 return jsUndefined();
222}
223
224JSValue* functionRun(ExecState* exec, JSObject*, JSValue*, const ArgList& args)
225{
226 StopWatch stopWatch;
227 UString fileName = args.at(exec, 0)->toString(exec);
228 Vector<char> script;
229 if (!fillBufferWithContentsOfFile(fileName, script))
230 return throwError(exec, GeneralError, "Could not open file.");
231
232 JSGlobalObject* globalObject = exec->dynamicGlobalObject();
233
234 stopWatch.start();
235 Interpreter::evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), fileName, 1, script.data());
236 stopWatch.stop();
237
238 return jsNumber(globalObject->globalExec(), stopWatch.getElapsedMS());
239}
240
241JSValue* functionLoad(ExecState* exec, JSObject*, JSValue*, const ArgList& args)
242{
243 UString fileName = args.at(exec, 0)->toString(exec);
244 Vector<char> script;
245 if (!fillBufferWithContentsOfFile(fileName, script))
246 return throwError(exec, GeneralError, "Could not open file.");
247
248 JSGlobalObject* globalObject = exec->dynamicGlobalObject();
249 Interpreter::evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), fileName, 1, script.data());
250
251 return jsUndefined();
252}
253
254JSValue* functionReadline(ExecState* exec, JSObject*, JSValue*, const ArgList&)
255{
256 Vector<char, 256> line;
257 int c;
258 while ((c = getchar()) != EOF) {
259 // FIXME: Should we also break on \r?
260 if (c == '\n')
261 break;
262 line.append(c);
263 }
264 line.append('\0');
265 return jsString(exec, line.data());
266}
267
268JSValue* functionQuit(ExecState*, JSObject*, JSValue*, const ArgList&)
269{
270 exit(0);
271#if !COMPILER(MSVC)
272 // MSVC knows that exit(0) never returns, so it flags this return statement as unreachable.
273 return jsUndefined();
274#endif
275}
276
277// Use SEH for Release builds only to get rid of the crash report dialog
278// (luckily the same tests fail in Release and Debug builds so far). Need to
279// be in a separate main function because the jscmain function requires object
280// unwinding.
281
282#if COMPILER(MSVC) && !defined(_DEBUG)
283#define TRY __try {
284#define EXCEPT(x) } __except (EXCEPTION_EXECUTE_HANDLER) { x; }
285#else
286#define TRY
287#define EXCEPT(x)
288#endif
289
290int jscmain(int argc, char** argv, JSGlobalData*);
291
292int main(int argc, char** argv)
293{
294#if defined(_DEBUG) && PLATFORM(WIN_OS)
295 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
296 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
297 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
298 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
299 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
300 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
301#endif
302
303 int res = 0;
304 TRY
305 JSGlobalData* globalData = JSGlobalData::create().releaseRef();
306 res = jscmain(argc, argv, globalData);
307 delete globalData;
308 EXCEPT(res = 3)
309 return res;
310}
311
312static bool prettyPrintScript(ExecState* exec, const UString& fileName, const Vector<char>& script)
313{
314 int errLine = 0;
315 UString errMsg;
316 UString scriptUString(script.data());
317 RefPtr<ProgramNode> programNode = exec->parser()->parse<ProgramNode>(exec, fileName, 1, UStringSourceProvider::create(scriptUString), 0, &errLine, &errMsg);
318 if (!programNode) {
319 fprintf(stderr, "%s:%d: %s.\n", fileName.UTF8String().c_str(), errLine, errMsg.UTF8String().c_str());
320 return false;
321 }
322
323 printf("%s\n", programNode->toString().UTF8String().c_str());
324 return true;
325}
326
327static bool runWithScripts(GlobalObject* globalObject, const Vector<UString>& fileNames, bool prettyPrint, bool dump)
328{
329 Vector<char> script;
330
331 if (dump)
332 CodeGenerator::setDumpsGeneratedCode(true);
333
334 bool success = true;
335 for (size_t i = 0; i < fileNames.size(); i++) {
336 UString fileName = fileNames[i];
337
338 if (!fillBufferWithContentsOfFile(fileName, script))
339 return false; // fail early so we can catch missing files
340
341 if (prettyPrint)
342 prettyPrintScript(globalObject->globalExec(), fileName, script);
343 else {
344#if ENABLE(SAMPLING_TOOL)
345 Machine* machine = globalObject->globalData()->machine;
346 machine->m_sampler = new SamplingTool();
347 machine->m_sampler->start();
348#endif
349
350 Completion completion = Interpreter::evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), fileName, 1, script.data());
351 success = success && completion.complType() != Throw;
352 if (dump) {
353 if (success)
354 printf("End: %s\n", completion.value()->toString(globalObject->globalExec()).ascii());
355 else
356 printf("Exception: %s\n", completion.value()->toString(globalObject->globalExec()).ascii());
357 }
358
359#if ENABLE(SAMPLING_TOOL)
360 machine->m_sampler->stop();
361 machine->m_sampler->dump(globalObject->globalExec());
362 delete machine->m_sampler;
363#endif
364 }
365 }
366 return success;
367}
368
369static void runInteractive(GlobalObject* globalObject)
370{
371 while (true) {
372#if HAVE(READLINE)
373 char* line = readline(interactivePrompt);
374 if (!line)
375 break;
376 if (line[0])
377 add_history(line);
378 Completion completion = Interpreter::evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), interpreterName, 1, line);
379 free(line);
380#else
381 puts(interactivePrompt);
382 Vector<char, 256> line;
383 int c;
384 while ((c = getchar()) != EOF) {
385 // FIXME: Should we also break on \r?
386 if (c == '\n')
387 break;
388 line.append(c);
389 }
390 line.append('\0');
391 Completion completion = Interpreter::evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), interpreterName, 1, line.data());
392#endif
393 if (completion.isValueCompletion())
394 printf("%s\n", completion.value()->toString(globalObject->globalExec()).UTF8String().c_str());
395 }
396 printf("\n");
397}
398
399static void printUsageStatement()
400{
401 fprintf(stderr, "Usage: jsc [options] [files] [-- arguments]\n");
402 fprintf(stderr, " -d Dumps bytecode (debug builds only)\n");
403 fprintf(stderr, " -f Specifies a source file (deprecated)\n");
404 fprintf(stderr, " -h|--help Prints this help message\n");
405 fprintf(stderr, " -i Enables interactive mode (default if no files are specified)\n");
406 fprintf(stderr, " -p Prints formatted source code\n");
407 fprintf(stderr, " -s Installs signal handlers that exit on a crash (Unix platforms only)\n");
408 exit(-1);
409}
410
411static void parseArguments(int argc, char** argv, Options& options)
412{
413 int i = 1;
414 for (; i < argc; ++i) {
415 const char* arg = argv[i];
416 if (strcmp(arg, "-f") == 0) {
417 if (++i == argc)
418 printUsageStatement();
419 options.fileNames.append(argv[i]);
420 continue;
421 }
422 if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) {
423 printUsageStatement();
424 }
425 if (strcmp(arg, "-i") == 0) {
426 options.interactive = true;
427 continue;
428 }
429 if (strcmp(arg, "-p") == 0) {
430 options.prettyPrint = true;
431 continue;
432 }
433 if (strcmp(arg, "-d") == 0) {
434 options.dump = true;
435 continue;
436 }
437 if (strcmp(arg, "-s") == 0) {
438#if PLATFORM(UNIX)
439 signal(SIGILL, _exit);
440 signal(SIGFPE, _exit);
441 signal(SIGBUS, _exit);
442 signal(SIGSEGV, _exit);
443#endif
444 continue;
445 }
446 if (strcmp(arg, "--") == 0) {
447 ++i;
448 break;
449 }
450 options.fileNames.append(argv[i]);
451 }
452
453 if (options.fileNames.isEmpty())
454 options.interactive = true;
455
456 for (; i < argc; ++i)
457 options.arguments.append(argv[i]);
458}
459
460int jscmain(int argc, char** argv, JSGlobalData* globalData)
461{
462 KJS::initializeThreading();
463
464 JSLock lock(false);
465
466 Options options;
467 parseArguments(argc, argv, options);
468
469 GlobalObject* globalObject = new (globalData) GlobalObject(options.arguments);
470 bool success = runWithScripts(globalObject, options.fileNames, options.prettyPrint, options.dump);
471 if (options.interactive && success)
472 runInteractive(globalObject);
473
474 return success ? 0 : 3;
475}
476
477static bool fillBufferWithContentsOfFile(const UString& fileName, Vector<char>& buffer)
478{
479 FILE* f = fopen(fileName.UTF8String().c_str(), "r");
480 if (!f) {
481 fprintf(stderr, "Could not open file: %s\n", fileName.UTF8String().c_str());
482 return false;
483 }
484
485 size_t buffer_size = 0;
486 size_t buffer_capacity = 1024;
487
488 buffer.resize(buffer_capacity);
489
490 while (!feof(f) && !ferror(f)) {
491 buffer_size += fread(buffer.data() + buffer_size, 1, buffer_capacity - buffer_size, f);
492 if (buffer_size == buffer_capacity) { // guarantees space for trailing '\0'
493 buffer_capacity *= 2;
494 buffer.resize(buffer_capacity);
495 }
496 }
497 fclose(f);
498 buffer[buffer_size] = '\0';
499
500 return true;
501}
Note: See TracBrowser for help on using the repository browser.