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

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

Revert 44221.

  • Property svn:eol-style set to native
File size: 18.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 "JSFunction.h"
30#include "JSLock.h"
31#include "PrototypeFunction.h"
32#include "SamplingTool.h"
33#include <math.h>
34#include <stdio.h>
35#include <stdlib.h>
36#include <string.h>
37
38#if !PLATFORM(WIN_OS)
39#include <unistd.h>
40#endif
41
42#if HAVE(READLINE)
43#include <readline/history.h>
44#include <readline/readline.h>
45#endif
46
47#if HAVE(SYS_TIME_H)
48#include <sys/time.h>
49#endif
50
51#if PLATFORM(UNIX)
52#include <signal.h>
53#endif
54
55#if COMPILER(MSVC) && !PLATFORM(WIN_CE)
56#include <crtdbg.h>
57#include <windows.h>
58#include <mmsystem.h>
59#endif
60
61#if PLATFORM(QT)
62#include <QCoreApplication>
63#include <QDateTime>
64#endif
65
66using namespace JSC;
67using namespace WTF;
68
69static void cleanupGlobalData(JSGlobalData*);
70static bool fillBufferWithContentsOfFile(const UString& fileName, Vector<char>& buffer);
71
72static JSValue JSC_HOST_CALL functionPrint(ExecState*, JSObject*, JSValue, const ArgList&);
73static JSValue JSC_HOST_CALL functionDebug(ExecState*, JSObject*, JSValue, const ArgList&);
74static JSValue JSC_HOST_CALL functionGC(ExecState*, JSObject*, JSValue, const ArgList&);
75static JSValue JSC_HOST_CALL functionVersion(ExecState*, JSObject*, JSValue, const ArgList&);
76static JSValue JSC_HOST_CALL functionRun(ExecState*, JSObject*, JSValue, const ArgList&);
77static JSValue JSC_HOST_CALL functionLoad(ExecState*, JSObject*, JSValue, const ArgList&);
78static JSValue JSC_HOST_CALL functionReadline(ExecState*, JSObject*, JSValue, const ArgList&);
79static NO_RETURN JSValue JSC_HOST_CALL functionQuit(ExecState*, JSObject*, JSValue, const ArgList&);
80
81#if ENABLE(SAMPLING_FLAGS)
82static JSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState*, JSObject*, JSValue, const ArgList&);
83static JSValue JSC_HOST_CALL functionClearSamplingFlags(ExecState*, JSObject*, JSValue, const ArgList&);
84#endif
85
86struct Script {
87 bool isFile;
88 char *argument;
89
90 Script(bool isFile, char *argument)
91 : isFile(isFile)
92 , argument(argument)
93 {
94 }
95};
96
97struct Options {
98 Options()
99 : interactive(false)
100 , dump(false)
101 {
102 }
103
104 bool interactive;
105 bool dump;
106 Vector<Script> scripts;
107 Vector<UString> arguments;
108};
109
110static const char interactivePrompt[] = "> ";
111static const UString interpreterName("Interpreter");
112
113class StopWatch {
114public:
115 void start();
116 void stop();
117 long getElapsedMS(); // call stop() first
118
119private:
120#if PLATFORM(QT)
121 uint m_startTime;
122 uint m_stopTime;
123#elif PLATFORM(WIN_OS)
124 DWORD m_startTime;
125 DWORD m_stopTime;
126#else
127 // Windows does not have timeval, disabling this class for now (bug 7399)
128 timeval m_startTime;
129 timeval m_stopTime;
130#endif
131};
132
133void StopWatch::start()
134{
135#if PLATFORM(QT)
136 QDateTime t = QDateTime::currentDateTime();
137 m_startTime = t.toTime_t() * 1000 + t.time().msec();
138#elif PLATFORM(WIN_OS)
139 m_startTime = timeGetTime();
140#else
141 gettimeofday(&m_startTime, 0);
142#endif
143}
144
145void StopWatch::stop()
146{
147#if PLATFORM(QT)
148 QDateTime t = QDateTime::currentDateTime();
149 m_stopTime = t.toTime_t() * 1000 + t.time().msec();
150#elif PLATFORM(WIN_OS)
151 m_stopTime = timeGetTime();
152#else
153 gettimeofday(&m_stopTime, 0);
154#endif
155}
156
157long StopWatch::getElapsedMS()
158{
159#if PLATFORM(WIN_OS) || PLATFORM(QT)
160 return m_stopTime - m_startTime;
161#else
162 timeval elapsedTime;
163 timersub(&m_stopTime, &m_startTime, &elapsedTime);
164
165 return elapsedTime.tv_sec * 1000 + lroundf(elapsedTime.tv_usec / 1000.0f);
166#endif
167}
168
169class GlobalObject : public JSGlobalObject {
170public:
171 GlobalObject(const Vector<UString>& arguments);
172 virtual UString className() const { return "global"; }
173};
174COMPILE_ASSERT(!IsInteger<GlobalObject>::value, WTF_IsInteger_GlobalObject_false);
175ASSERT_CLASS_FITS_IN_CELL(GlobalObject);
176
177GlobalObject::GlobalObject(const Vector<UString>& arguments)
178 : JSGlobalObject()
179{
180 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "debug"), functionDebug));
181 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "print"), functionPrint));
182 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 0, Identifier(globalExec(), "quit"), functionQuit));
183 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 0, Identifier(globalExec(), "gc"), functionGC));
184 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "version"), functionVersion));
185 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "run"), functionRun));
186 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "load"), functionLoad));
187 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 0, Identifier(globalExec(), "readline"), functionReadline));
188
189#if ENABLE(SAMPLING_FLAGS)
190 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "setSamplingFlags"), functionSetSamplingFlags));
191 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "clearSamplingFlags"), functionClearSamplingFlags));
192#endif
193
194 JSObject* array = constructEmptyArray(globalExec());
195 for (size_t i = 0; i < arguments.size(); ++i)
196 array->put(globalExec(), i, jsString(globalExec(), arguments[i]));
197 putDirect(Identifier(globalExec(), "arguments"), array);
198}
199
200JSValue JSC_HOST_CALL functionPrint(ExecState* exec, JSObject*, JSValue, const ArgList& args)
201{
202 for (unsigned i = 0; i < args.size(); ++i) {
203 if (i != 0)
204 putchar(' ');
205
206 printf("%s", args.at(i).toString(exec).UTF8String().c_str());
207 }
208
209 putchar('\n');
210 fflush(stdout);
211 return jsUndefined();
212}
213
214JSValue JSC_HOST_CALL functionDebug(ExecState* exec, JSObject*, JSValue, const ArgList& args)
215{
216 fprintf(stderr, "--> %s\n", args.at(0).toString(exec).UTF8String().c_str());
217 return jsUndefined();
218}
219
220JSValue JSC_HOST_CALL functionGC(ExecState* exec, JSObject*, JSValue, const ArgList&)
221{
222 JSLock lock(false);
223 exec->heap()->collect();
224 return jsUndefined();
225}
226
227JSValue JSC_HOST_CALL functionVersion(ExecState*, JSObject*, JSValue, const ArgList&)
228{
229 // We need this function for compatibility with the Mozilla JS tests but for now
230 // we don't actually do any version-specific handling
231 return jsUndefined();
232}
233
234JSValue JSC_HOST_CALL functionRun(ExecState* exec, JSObject*, JSValue, const ArgList& args)
235{
236 StopWatch stopWatch;
237 UString fileName = args.at(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->lexicalGlobalObject();
243
244 stopWatch.start();
245 evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(script.data(), fileName));
246 stopWatch.stop();
247
248 return jsNumber(globalObject->globalExec(), stopWatch.getElapsedMS());
249}
250
251JSValue JSC_HOST_CALL functionLoad(ExecState* exec, JSObject* o, JSValue v, const ArgList& args)
252{
253 UNUSED_PARAM(o);
254 UNUSED_PARAM(v);
255 UString fileName = args.at(0).toString(exec);
256 Vector<char> script;
257 if (!fillBufferWithContentsOfFile(fileName, script))
258 return throwError(exec, GeneralError, "Could not open file.");
259
260 JSGlobalObject* globalObject = exec->lexicalGlobalObject();
261 Completion result = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(script.data(), fileName));
262 if (result.complType() == Throw)
263 exec->setException(result.value());
264 return result.value();
265}
266
267#if ENABLE(SAMPLING_FLAGS)
268JSValue JSC_HOST_CALL functionSetSamplingFlags(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::setFlag(flag);
274 }
275 return jsNull();
276}
277
278JSValue JSC_HOST_CALL functionClearSamplingFlags(ExecState* exec, JSObject*, JSValue, const ArgList& args)
279{
280 for (unsigned i = 0; i < args.size(); ++i) {
281 unsigned flag = static_cast<unsigned>(args.at(i).toNumber(exec));
282 if ((flag >= 1) && (flag <= 32))
283 SamplingFlags::clearFlag(flag);
284 }
285 return jsNull();
286}
287#endif
288
289JSValue JSC_HOST_CALL functionReadline(ExecState* exec, JSObject*, JSValue, const ArgList&)
290{
291 Vector<char, 256> line;
292 int c;
293 while ((c = getchar()) != EOF) {
294 // FIXME: Should we also break on \r?
295 if (c == '\n')
296 break;
297 line.append(c);
298 }
299 line.append('\0');
300 return jsString(exec, line.data());
301}
302
303JSValue JSC_HOST_CALL functionQuit(ExecState* exec, JSObject*, JSValue, const ArgList&)
304{
305 cleanupGlobalData(&exec->globalData());
306 exit(EXIT_SUCCESS);
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) && PLATFORM(WIN_OS)
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) && !PLATFORM(WIN_CE)
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(false);
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#if ENABLE(OPCODE_SAMPLING)
375 Interpreter* interpreter = globalObject->globalData()->interpreter;
376 interpreter->setSampler(new SamplingTool(interpreter));
377 interpreter->sampler()->setup();
378#endif
379#if ENABLE(SAMPLING_FLAGS)
380 SamplingFlags::start();
381#endif
382
383 bool success = true;
384 for (size_t i = 0; i < scripts.size(); i++) {
385 if (scripts[i].isFile) {
386 fileName = scripts[i].argument;
387 if (!fillBufferWithContentsOfFile(fileName, scriptBuffer))
388 return false; // fail early so we can catch missing files
389 script = scriptBuffer.data();
390 } else {
391 script = scripts[i].argument;
392 fileName = "[Command Line]";
393 }
394
395#if ENABLE(SAMPLING_THREAD)
396 SamplingThread::start();
397#endif
398
399 Completion completion = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(script, fileName));
400 success = success && completion.complType() != Throw;
401 if (dump) {
402 if (completion.complType() == Throw)
403 printf("Exception: %s\n", completion.value().toString(globalObject->globalExec()).ascii());
404 else
405 printf("End: %s\n", completion.value().toString(globalObject->globalExec()).ascii());
406 }
407
408#if ENABLE(SAMPLING_THREAD)
409 SamplingThread::stop();
410#endif
411
412 globalObject->globalExec()->clearException();
413 }
414
415#if ENABLE(SAMPLING_FLAGS)
416 SamplingFlags::stop();
417#endif
418#if ENABLE(OPCODE_SAMPLING)
419 interpreter->sampler()->dump(globalObject->globalExec());
420 delete interpreter->sampler();
421#endif
422#if ENABLE(SAMPLING_COUNTERS)
423 AbstractSamplingCounter::dump();
424#endif
425 return success;
426}
427
428static
429#if !HAVE(READLINE)
430NO_RETURN
431#endif
432void runInteractive(GlobalObject* globalObject)
433{
434 while (true) {
435#if HAVE(READLINE)
436 char* line = readline(interactivePrompt);
437 if (!line)
438 break;
439 if (line[0])
440 add_history(line);
441 Completion completion = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(line, interpreterName));
442 free(line);
443#else
444 puts(interactivePrompt);
445 Vector<char, 256> line;
446 int c;
447 while ((c = getchar()) != EOF) {
448 // FIXME: Should we also break on \r?
449 if (c == '\n')
450 break;
451 line.append(c);
452 }
453 line.append('\0');
454 Completion completion = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(line.data(), interpreterName));
455#endif
456 if (completion.complType() == Throw)
457 printf("Exception: %s\n", completion.value().toString(globalObject->globalExec()).ascii());
458 else
459 printf("%s\n", completion.value().toString(globalObject->globalExec()).UTF8String().c_str());
460
461 globalObject->globalExec()->clearException();
462 }
463 printf("\n");
464}
465
466static NO_RETURN void printUsageStatement(JSGlobalData* globalData, bool help = false)
467{
468 fprintf(stderr, "Usage: jsc [options] [files] [-- arguments]\n");
469 fprintf(stderr, " -d Dumps bytecode (debug builds only)\n");
470 fprintf(stderr, " -e Evaluate argument as script code\n");
471 fprintf(stderr, " -f Specifies a source file (deprecated)\n");
472 fprintf(stderr, " -h|--help Prints this help message\n");
473 fprintf(stderr, " -i Enables interactive mode (default if no files are specified)\n");
474 fprintf(stderr, " -s Installs signal handlers that exit on a crash (Unix platforms only)\n");
475
476 cleanupGlobalData(globalData);
477 exit(help ? EXIT_SUCCESS : EXIT_FAILURE);
478}
479
480static void parseArguments(int argc, char** argv, Options& options, JSGlobalData* globalData)
481{
482 int i = 1;
483 for (; i < argc; ++i) {
484 const char* arg = argv[i];
485 if (strcmp(arg, "-f") == 0) {
486 if (++i == argc)
487 printUsageStatement(globalData);
488 options.scripts.append(Script(true, argv[i]));
489 continue;
490 }
491 if (strcmp(arg, "-e") == 0) {
492 if (++i == argc)
493 printUsageStatement(globalData);
494 options.scripts.append(Script(false, argv[i]));
495 continue;
496 }
497 if (strcmp(arg, "-h") == 0 || strcmp(arg, "--help") == 0) {
498 printUsageStatement(globalData, true);
499 }
500 if (strcmp(arg, "-i") == 0) {
501 options.interactive = true;
502 continue;
503 }
504 if (strcmp(arg, "-d") == 0) {
505 options.dump = true;
506 continue;
507 }
508 if (strcmp(arg, "-s") == 0) {
509#if PLATFORM(UNIX)
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, "--") == 0) {
518 ++i;
519 break;
520 }
521 options.scripts.append(Script(true, argv[i]));
522 }
523
524 if (options.scripts.isEmpty())
525 options.interactive = true;
526
527 for (; i < argc; ++i)
528 options.arguments.append(argv[i]);
529}
530
531int jscmain(int argc, char** argv, JSGlobalData* globalData)
532{
533 JSLock lock(false);
534
535 Options options;
536 parseArguments(argc, argv, options, globalData);
537
538 GlobalObject* globalObject = new (globalData) GlobalObject(options.arguments);
539 bool success = runWithScripts(globalObject, options.scripts, options.dump);
540 if (options.interactive && success)
541 runInteractive(globalObject);
542
543 return success ? 0 : 3;
544}
545
546static bool fillBufferWithContentsOfFile(const UString& fileName, Vector<char>& buffer)
547{
548 FILE* f = fopen(fileName.UTF8String().c_str(), "r");
549 if (!f) {
550 fprintf(stderr, "Could not open file: %s\n", fileName.UTF8String().c_str());
551 return false;
552 }
553
554 size_t buffer_size = 0;
555 size_t buffer_capacity = 1024;
556
557 buffer.resize(buffer_capacity);
558
559 while (!feof(f) && !ferror(f)) {
560 buffer_size += fread(buffer.data() + buffer_size, 1, buffer_capacity - buffer_size, f);
561 if (buffer_size == buffer_capacity) { // guarantees space for trailing '\0'
562 buffer_capacity *= 2;
563 buffer.resize(buffer_capacity);
564 }
565 }
566 fclose(f);
567 buffer[buffer_size] = '\0';
568
569 return true;
570}
Note: See TracBrowser for help on using the repository browser.