OSDN Git Service

Merge WebKit at r78450: Initial merge by git.
[android-x86/external-webkit.git] / Source / JavaScriptCore / jsc.cpp
1 /*
2  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
4  *  Copyright (C) 2006 Bjoern Graf (bjoern.graf@gmail.com)
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 "PrototypeFunction.h"
35 #include "SamplingTool.h"
36 #include <math.h>
37 #include <stdio.h>
38 #include <stdlib.h>
39 #include <string.h>
40
41 #if !OS(WINDOWS)
42 #include <unistd.h>
43 #endif
44
45 #if HAVE(READLINE)
46 #include <readline/history.h>
47 #include <readline/readline.h>
48 #endif
49
50 #if HAVE(SYS_TIME_H)
51 #include <sys/time.h>
52 #endif
53
54 #if HAVE(SIGNAL_H)
55 #include <signal.h>
56 #endif
57
58 #if COMPILER(MSVC) && !OS(WINCE)
59 #include <crtdbg.h>
60 #include <mmsystem.h>
61 #include <windows.h>
62 #endif
63
64 #if PLATFORM(QT)
65 #include <QCoreApplication>
66 #include <QDateTime>
67 #endif
68
69 using namespace JSC;
70 using namespace WTF;
71
72 static void cleanupGlobalData(JSGlobalData*);
73 static bool fillBufferWithContentsOfFile(const UString& fileName, Vector<char>& buffer);
74
75 static EncodedJSValue JSC_HOST_CALL functionPrint(ExecState*);
76 static EncodedJSValue JSC_HOST_CALL functionDebug(ExecState*);
77 static EncodedJSValue JSC_HOST_CALL functionGC(ExecState*);
78 static EncodedJSValue JSC_HOST_CALL functionVersion(ExecState*);
79 static EncodedJSValue JSC_HOST_CALL functionRun(ExecState*);
80 static EncodedJSValue JSC_HOST_CALL functionLoad(ExecState*);
81 static EncodedJSValue JSC_HOST_CALL functionCheckSyntax(ExecState*);
82 static EncodedJSValue JSC_HOST_CALL functionReadline(ExecState*);
83 static NO_RETURN_WITH_VALUE EncodedJSValue JSC_HOST_CALL functionQuit(ExecState*);
84
85 #if ENABLE(SAMPLING_FLAGS)
86 static EncodedJSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState*);
87 static EncodedJSValue JSC_HOST_CALL functionClearSamplingFlags(ExecState*);
88 #endif
89
90 struct Script {
91     bool isFile;
92     char* argument;
93
94     Script(bool isFile, char *argument)
95         : isFile(isFile)
96         , argument(argument)
97     {
98     }
99 };
100
101 struct Options {
102     Options()
103         : interactive(false)
104         , dump(false)
105     {
106     }
107
108     bool interactive;
109     bool dump;
110     Vector<Script> scripts;
111     Vector<UString> arguments;
112 };
113
114 static const char interactivePrompt[] = "> ";
115 static const UString interpreterName("Interpreter");
116
117 class StopWatch {
118 public:
119     void start();
120     void stop();
121     long getElapsedMS(); // call stop() first
122
123 private:
124     double m_startTime;
125     double m_stopTime;
126 };
127
128 void StopWatch::start()
129 {
130     m_startTime = currentTime();
131 }
132
133 void StopWatch::stop()
134 {
135     m_stopTime = currentTime();
136 }
137
138 long StopWatch::getElapsedMS()
139 {
140     return static_cast<long>((m_stopTime - m_startTime) * 1000);
141 }
142
143 class GlobalObject : public JSGlobalObject {
144 public:
145     GlobalObject(const Vector<UString>& arguments);
146     virtual UString className() const { return "global"; }
147 };
148 COMPILE_ASSERT(!IsInteger<GlobalObject>::value, WTF_IsInteger_GlobalObject_false);
149 ASSERT_CLASS_FITS_IN_CELL(GlobalObject);
150
151 GlobalObject::GlobalObject(const Vector<UString>& arguments)
152     : JSGlobalObject()
153 {
154     putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), this, prototypeFunctionStructure(), 1, Identifier(globalExec(), "debug"), functionDebug));
155     putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), this, prototypeFunctionStructure(), 1, Identifier(globalExec(), "print"), functionPrint));
156     putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), this, prototypeFunctionStructure(), 0, Identifier(globalExec(), "quit"), functionQuit));
157     putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), this, prototypeFunctionStructure(), 0, Identifier(globalExec(), "gc"), functionGC));
158     putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), this, prototypeFunctionStructure(), 1, Identifier(globalExec(), "version"), functionVersion));
159     putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), this, prototypeFunctionStructure(), 1, Identifier(globalExec(), "run"), functionRun));
160     putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), this, prototypeFunctionStructure(), 1, Identifier(globalExec(), "load"), functionLoad));
161     putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), this, prototypeFunctionStructure(), 1, Identifier(globalExec(), "checkSyntax"), functionCheckSyntax));
162     putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), this, prototypeFunctionStructure(), 0, Identifier(globalExec(), "readline"), functionReadline));
163
164 #if ENABLE(SAMPLING_FLAGS)
165     putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), this, prototypeFunctionStructure(), 1, Identifier(globalExec(), "setSamplingFlags"), functionSetSamplingFlags));
166     putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), this, prototypeFunctionStructure(), 1, Identifier(globalExec(), "clearSamplingFlags"), functionClearSamplingFlags));
167 #endif
168
169     JSObject* array = constructEmptyArray(globalExec());
170     for (size_t i = 0; i < arguments.size(); ++i)
171         array->put(globalExec(), i, jsString(globalExec(), arguments[i]));
172     putDirect(globalExec()->globalData(), Identifier(globalExec(), "arguments"), array);
173 }
174
175 EncodedJSValue JSC_HOST_CALL functionPrint(ExecState* exec)
176 {
177     for (unsigned i = 0; i < exec->argumentCount(); ++i) {
178         if (i)
179             putchar(' ');
180
181         printf("%s", exec->argument(i).toString(exec).utf8().data());
182     }
183
184     putchar('\n');
185     fflush(stdout);
186     return JSValue::encode(jsUndefined());
187 }
188
189 EncodedJSValue JSC_HOST_CALL functionDebug(ExecState* exec)
190 {
191     fprintf(stderr, "--> %s\n", exec->argument(0).toString(exec).utf8().data());
192     return JSValue::encode(jsUndefined());
193 }
194
195 EncodedJSValue JSC_HOST_CALL functionGC(ExecState* exec)
196 {
197     JSLock lock(SilenceAssertionsOnly);
198     exec->heap()->collectAllGarbage();
199     return JSValue::encode(jsUndefined());
200 }
201
202 EncodedJSValue JSC_HOST_CALL functionVersion(ExecState*)
203 {
204     // We need this function for compatibility with the Mozilla JS tests but for now
205     // we don't actually do any version-specific handling
206     return JSValue::encode(jsUndefined());
207 }
208
209 EncodedJSValue JSC_HOST_CALL functionRun(ExecState* exec)
210 {
211     UString fileName = exec->argument(0).toString(exec);
212     Vector<char> script;
213     if (!fillBufferWithContentsOfFile(fileName, script))
214         return JSValue::encode(throwError(exec, createError(exec, "Could not open file.")));
215
216     GlobalObject* globalObject = new (&exec->globalData()) GlobalObject(Vector<UString>());
217
218     StopWatch stopWatch;
219     stopWatch.start();
220     evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(script.data(), fileName));
221     stopWatch.stop();
222
223     return JSValue::encode(jsNumber(stopWatch.getElapsedMS()));
224 }
225
226 EncodedJSValue JSC_HOST_CALL functionLoad(ExecState* exec)
227 {
228     UString fileName = exec->argument(0).toString(exec);
229     Vector<char> script;
230     if (!fillBufferWithContentsOfFile(fileName, script))
231         return JSValue::encode(throwError(exec, createError(exec, "Could not open file.")));
232
233     JSGlobalObject* globalObject = exec->lexicalGlobalObject();
234     Completion result = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(script.data(), fileName));
235     if (result.complType() == Throw)
236         throwError(exec, result.value());
237     return JSValue::encode(result.value());
238 }
239
240 EncodedJSValue JSC_HOST_CALL functionCheckSyntax(ExecState* exec)
241 {
242     UString fileName = exec->argument(0).toString(exec);
243     Vector<char> script;
244     if (!fillBufferWithContentsOfFile(fileName, script))
245         return JSValue::encode(throwError(exec, createError(exec, "Could not open file.")));
246
247     JSGlobalObject* globalObject = exec->lexicalGlobalObject();
248
249     StopWatch stopWatch;
250     stopWatch.start();
251     Completion result = checkSyntax(globalObject->globalExec(), makeSource(script.data(), fileName));
252     stopWatch.stop();
253
254     if (result.complType() == Throw)
255         throwError(exec, result.value());
256     return JSValue::encode(jsNumber(stopWatch.getElapsedMS()));
257 }
258
259 #if ENABLE(SAMPLING_FLAGS)
260 EncodedJSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState* exec)
261 {
262     for (unsigned i = 0; i < exec->argumentCount(); ++i) {
263         unsigned flag = static_cast<unsigned>(exec->argument(i).toNumber(exec));
264         if ((flag >= 1) && (flag <= 32))
265             SamplingFlags::setFlag(flag);
266     }
267     return JSValue::encode(jsNull());
268 }
269
270 EncodedJSValue JSC_HOST_CALL functionClearSamplingFlags(ExecState* exec)
271 {
272     for (unsigned i = 0; i < exec->argumentCount(); ++i) {
273         unsigned flag = static_cast<unsigned>(exec->argument(i).toNumber(exec));
274         if ((flag >= 1) && (flag <= 32))
275             SamplingFlags::clearFlag(flag);
276     }
277     return JSValue::encode(jsNull());
278 }
279 #endif
280
281 EncodedJSValue JSC_HOST_CALL functionReadline(ExecState* exec)
282 {
283     Vector<char, 256> line;
284     int c;
285     while ((c = getchar()) != EOF) {
286         // FIXME: Should we also break on \r? 
287         if (c == '\n')
288             break;
289         line.append(c);
290     }
291     line.append('\0');
292     return JSValue::encode(jsString(exec, line.data()));
293 }
294
295 EncodedJSValue JSC_HOST_CALL functionQuit(ExecState* exec)
296 {
297     // Technically, destroying the heap in the middle of JS execution is a no-no,
298     // but we want to maintain compatibility with the Mozilla test suite, so
299     // we pretend that execution has terminated to avoid ASSERTs, then tear down the heap.
300     exec->globalData().dynamicGlobalObject = 0;
301
302     cleanupGlobalData(&exec->globalData());
303     exit(EXIT_SUCCESS);
304
305 #if COMPILER(MSVC) && OS(WINCE)
306     // Without this, Visual Studio will complain that this method does not return a value.
307     return JSValue::encode(jsUndefined());
308 #endif
309 }
310
311 // Use SEH for Release builds only to get rid of the crash report dialog
312 // (luckily the same tests fail in Release and Debug builds so far). Need to
313 // be in a separate main function because the jscmain function requires object
314 // unwinding.
315
316 #if COMPILER(MSVC) && !defined(_DEBUG) && !OS(WINCE)
317 #define TRY       __try {
318 #define EXCEPT(x) } __except (EXCEPTION_EXECUTE_HANDLER) { x; }
319 #else
320 #define TRY
321 #define EXCEPT(x)
322 #endif
323
324 int jscmain(int argc, char** argv, JSGlobalData*);
325
326 int main(int argc, char** argv)
327 {
328 #if defined(_DEBUG) && OS(WINDOWS)
329     _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
330     _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
331     _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
332     _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
333     _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
334     _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
335 #endif
336
337 #if COMPILER(MSVC) && !OS(WINCE)
338     timeBeginPeriod(1);
339 #endif
340
341 #if PLATFORM(QT)
342     QCoreApplication app(argc, argv);
343 #endif
344
345     // Initialize JSC before getting JSGlobalData.
346     JSC::initializeThreading();
347
348     // We can't use destructors in the following code because it uses Windows
349     // Structured Exception Handling
350     int res = 0;
351     JSGlobalData* globalData = JSGlobalData::create(ThreadStackTypeLarge).leakRef();
352     TRY
353         res = jscmain(argc, argv, globalData);
354     EXCEPT(res = 3)
355
356     cleanupGlobalData(globalData);
357     return res;
358 }
359
360 static void cleanupGlobalData(JSGlobalData* globalData)
361 {
362     JSLock lock(SilenceAssertionsOnly);
363     globalData->heap.destroy();
364     globalData->deref();
365 }
366
367 static bool runWithScripts(GlobalObject* globalObject, const Vector<Script>& scripts, bool dump)
368 {
369     UString script;
370     UString fileName;
371     Vector<char> scriptBuffer;
372
373     if (dump)
374         BytecodeGenerator::setDumpsGeneratedCode(true);
375
376     JSGlobalData& globalData = globalObject->globalData();
377
378 #if ENABLE(SAMPLING_FLAGS)
379     SamplingFlags::start();
380 #endif
381
382     bool success = true;
383     for (size_t i = 0; i < scripts.size(); i++) {
384         if (scripts[i].isFile) {
385             fileName = scripts[i].argument;
386             if (!fillBufferWithContentsOfFile(fileName, scriptBuffer))
387                 return false; // fail early so we can catch missing files
388             script = scriptBuffer.data();
389         } else {
390             script = scripts[i].argument;
391             fileName = "[Command Line]";
392         }
393
394         globalData.startSampling();
395
396         Completion completion = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(script, fileName));
397         success = success && completion.complType() != Throw;
398         if (dump) {
399             if (completion.complType() == Throw)
400                 printf("Exception: %s\n", completion.value().toString(globalObject->globalExec()).utf8().data());
401             else
402                 printf("End: %s\n", completion.value().toString(globalObject->globalExec()).utf8().data());
403         }
404
405         globalData.stopSampling();
406         globalObject->globalExec()->clearException();
407     }
408
409 #if ENABLE(SAMPLING_FLAGS)
410     SamplingFlags::stop();
411 #endif
412     globalData.dumpSampleData(globalObject->globalExec());
413 #if ENABLE(SAMPLING_COUNTERS)
414     AbstractSamplingCounter::dump();
415 #endif
416 #if ENABLE(REGEXP_TRACING)
417     globalData.dumpRegExpTrace();
418 #endif
419     return success;
420 }
421
422 #define RUNNING_FROM_XCODE 0
423
424 static void runInteractive(GlobalObject* globalObject)
425 {
426     while (true) {
427 #if HAVE(READLINE) && !RUNNING_FROM_XCODE
428         char* line = readline(interactivePrompt);
429         if (!line)
430             break;
431         if (line[0])
432             add_history(line);
433         Completion completion = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(line, interpreterName));
434         free(line);
435 #else
436         printf("%s", interactivePrompt);
437         Vector<char, 256> line;
438         int c;
439         while ((c = getchar()) != EOF) {
440             // FIXME: Should we also break on \r? 
441             if (c == '\n')
442                 break;
443             line.append(c);
444         }
445         if (line.isEmpty())
446             break;
447         line.append('\0');
448         Completion completion = evaluate(globalObject->globalExec(), globalObject->globalScopeChain(), makeSource(line.data(), interpreterName));
449 #endif
450         if (completion.complType() == Throw)
451             printf("Exception: %s\n", completion.value().toString(globalObject->globalExec()).utf8().data());
452         else
453             printf("%s\n", completion.value().toString(globalObject->globalExec()).utf8().data());
454
455         globalObject->globalExec()->clearException();
456     }
457     printf("\n");
458 }
459
460 static NO_RETURN void printUsageStatement(JSGlobalData* globalData, bool help = false)
461 {
462     fprintf(stderr, "Usage: jsc [options] [files] [-- arguments]\n");
463     fprintf(stderr, "  -d         Dumps bytecode (debug builds only)\n");
464     fprintf(stderr, "  -e         Evaluate argument as script code\n");
465     fprintf(stderr, "  -f         Specifies a source file (deprecated)\n");
466     fprintf(stderr, "  -h|--help  Prints this help message\n");
467     fprintf(stderr, "  -i         Enables interactive mode (default if no files are specified)\n");
468 #if HAVE(SIGNAL_H)
469     fprintf(stderr, "  -s         Installs signal handlers that exit on a crash (Unix platforms only)\n");
470 #endif
471
472     cleanupGlobalData(globalData);
473     exit(help ? EXIT_SUCCESS : EXIT_FAILURE);
474 }
475
476 static void parseArguments(int argc, char** argv, Options& options, JSGlobalData* globalData)
477 {
478     int i = 1;
479     for (; i < argc; ++i) {
480         const char* arg = argv[i];
481         if (!strcmp(arg, "-f")) {
482             if (++i == argc)
483                 printUsageStatement(globalData);
484             options.scripts.append(Script(true, argv[i]));
485             continue;
486         }
487         if (!strcmp(arg, "-e")) {
488             if (++i == argc)
489                 printUsageStatement(globalData);
490             options.scripts.append(Script(false, argv[i]));
491             continue;
492         }
493         if (!strcmp(arg, "-i")) {
494             options.interactive = true;
495             continue;
496         }
497         if (!strcmp(arg, "-d")) {
498             options.dump = true;
499             continue;
500         }
501         if (!strcmp(arg, "-s")) {
502 #if HAVE(SIGNAL_H)
503             signal(SIGILL, _exit);
504             signal(SIGFPE, _exit);
505             signal(SIGBUS, _exit);
506             signal(SIGSEGV, _exit);
507 #endif
508             continue;
509         }
510         if (!strcmp(arg, "--")) {
511             ++i;
512             break;
513         }
514         if (!strcmp(arg, "-h") || !strcmp(arg, "--help"))
515             printUsageStatement(globalData, true);
516         options.scripts.append(Script(true, argv[i]));
517     }
518
519     if (options.scripts.isEmpty())
520         options.interactive = true;
521
522     for (; i < argc; ++i)
523         options.arguments.append(argv[i]);
524 }
525
526 int jscmain(int argc, char** argv, JSGlobalData* globalData)
527 {
528     JSLock lock(SilenceAssertionsOnly);
529
530     Options options;
531     parseArguments(argc, argv, options, globalData);
532
533     GlobalObject* globalObject = new (globalData) GlobalObject(options.arguments);
534     bool success = runWithScripts(globalObject, options.scripts, options.dump);
535     if (options.interactive && success)
536         runInteractive(globalObject);
537
538     return success ? 0 : 3;
539 }
540
541 static bool fillBufferWithContentsOfFile(const UString& fileName, Vector<char>& buffer)
542 {
543     FILE* f = fopen(fileName.utf8().data(), "r");
544     if (!f) {
545         fprintf(stderr, "Could not open file: %s\n", fileName.utf8().data());
546         return false;
547     }
548
549     size_t bufferSize = 0;
550     size_t bufferCapacity = 1024;
551
552     buffer.resize(bufferCapacity);
553
554     while (!feof(f) && !ferror(f)) {
555         bufferSize += fread(buffer.data() + bufferSize, 1, bufferCapacity - bufferSize, f);
556         if (bufferSize == bufferCapacity) { // guarantees space for trailing '\0'
557             bufferCapacity *= 2;
558             buffer.resize(bufferCapacity);
559         }
560     }
561     fclose(f);
562     buffer[bufferSize] = '\0';
563
564     if (buffer[0] == '#' && buffer[1] == '!')
565         buffer[0] = buffer[1] = '/';
566
567     return true;
568 }