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