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

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

Print a blank line when exiting the jsc interactive mode to ensure that the shell
prompt will start on a new line.

Reviewed by Sam Weinig.

  • kjs/Shell.cpp:

(runInteractive):

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