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

Last change on this file since 41036 was 40663, checked in by [email protected], 16 years ago

2009-02-04 Gavin Barraclough <[email protected]>

Reviewed by Oliver 'the nun' Hunt.

Add -e switch to jsc to enable evaluation of scripts passed on the command line.

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