ae2995467e582d246f639b5d005a93f262c53131
[WebKit-https.git] / Source / JavaScriptCore / jsc.cpp
1 /*
2  *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3  *  Copyright (C) 2004, 2005, 2006, 2007, 2008, 2012, 2013 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 "APIShims.h"
26 #include "ButterflyInlines.h"
27 #include "BytecodeGenerator.h"
28 #include "CallFrameInlines.h"
29 #include "Completion.h"
30 #include "CopiedSpaceInlines.h"
31 #include "ExceptionHelpers.h"
32 #include "HeapStatistics.h"
33 #include "InitializeThreading.h"
34 #include "Interpreter.h"
35 #include "JSArray.h"
36 #include "JSCTypedArrayStubs.h"
37 #include "JSFunction.h"
38 #include "JSLock.h"
39 #include "JSProxy.h"
40 #include "JSString.h"
41 #include "Operations.h"
42 #include "SamplingTool.h"
43 #include "StackIterator.h"
44 #include "StructureRareDataInlines.h"
45 #include <math.h>
46 #include <stdio.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <wtf/CurrentTime.h>
50 #include <wtf/MainThread.h>
51 #include <wtf/StringPrintStream.h>
52 #include <wtf/text/StringBuilder.h>
53
54 #if !OS(WINDOWS)
55 #include <unistd.h>
56 #endif
57
58 #if HAVE(READLINE)
59 // readline/history.h has a Function typedef which conflicts with the WTF::Function template from WTF/Forward.h
60 // We #define it to something else to avoid this conflict.
61 #define Function ReadlineFunction
62 #include <readline/history.h>
63 #include <readline/readline.h>
64 #undef Function
65 #endif
66
67 #if HAVE(SYS_TIME_H)
68 #include <sys/time.h>
69 #endif
70
71 #if HAVE(SIGNAL_H)
72 #include <signal.h>
73 #endif
74
75 #if COMPILER(MSVC) && !OS(WINCE)
76 #include <crtdbg.h>
77 #include <mmsystem.h>
78 #include <windows.h>
79 #endif
80
81 #if PLATFORM(QT)
82 #include <QCoreApplication>
83 #include <QDateTime>
84 #endif
85
86 #if PLATFORM(IOS)
87 #include <fenv.h>
88 #include <arm/arch.h>
89 #endif
90
91 #if PLATFORM(BLACKBERRY)
92 #include <BlackBerryPlatformLog.h>
93 #endif
94
95 #if PLATFORM(EFL)
96 #include <Ecore.h>
97 #endif
98
99 using namespace JSC;
100 using namespace WTF;
101
102 static bool fillBufferWithContentsOfFile(const String& fileName, Vector<char>& buffer);
103
104 static EncodedJSValue JSC_HOST_CALL functionPrint(ExecState*);
105 static EncodedJSValue JSC_HOST_CALL functionDebug(ExecState*);
106 static EncodedJSValue JSC_HOST_CALL functionDescribe(ExecState*);
107 static EncodedJSValue JSC_HOST_CALL functionJSCStack(ExecState*);
108 static EncodedJSValue JSC_HOST_CALL functionGC(ExecState*);
109 #ifndef NDEBUG
110 static EncodedJSValue JSC_HOST_CALL functionReleaseExecutableMemory(ExecState*);
111 static EncodedJSValue JSC_HOST_CALL functionDumpCallFrame(ExecState*);
112 #endif
113 static EncodedJSValue JSC_HOST_CALL functionVersion(ExecState*);
114 static EncodedJSValue JSC_HOST_CALL functionRun(ExecState*);
115 static EncodedJSValue JSC_HOST_CALL functionLoad(ExecState*);
116 static EncodedJSValue JSC_HOST_CALL functionCheckSyntax(ExecState*);
117 static EncodedJSValue JSC_HOST_CALL functionReadline(ExecState*);
118 static EncodedJSValue JSC_HOST_CALL functionPreciseTime(ExecState*);
119 static NO_RETURN_WITH_VALUE EncodedJSValue JSC_HOST_CALL functionQuit(ExecState*);
120
121 #if ENABLE(SAMPLING_FLAGS)
122 static EncodedJSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState*);
123 static EncodedJSValue JSC_HOST_CALL functionClearSamplingFlags(ExecState*);
124 #endif
125
126 struct Script {
127     bool isFile;
128     char* argument;
129
130     Script(bool isFile, char *argument)
131         : isFile(isFile)
132         , argument(argument)
133     {
134     }
135 };
136
137 class CommandLine {
138 public:
139     CommandLine(int argc, char** argv)
140         : m_interactive(false)
141         , m_dump(false)
142         , m_exitCode(false)
143         , m_profile(false)
144     {
145         parseArguments(argc, argv);
146     }
147
148     bool m_interactive;
149     bool m_dump;
150     bool m_exitCode;
151     Vector<Script> m_scripts;
152     Vector<String> m_arguments;
153     bool m_profile;
154     String m_profilerOutput;
155
156     void parseArguments(int, char**);
157 };
158
159 static const char interactivePrompt[] = ">>> ";
160
161 class StopWatch {
162 public:
163     void start();
164     void stop();
165     long getElapsedMS(); // call stop() first
166
167 private:
168     double m_startTime;
169     double m_stopTime;
170 };
171
172 void StopWatch::start()
173 {
174     m_startTime = monotonicallyIncreasingTime();
175 }
176
177 void StopWatch::stop()
178 {
179     m_stopTime = monotonicallyIncreasingTime();
180 }
181
182 long StopWatch::getElapsedMS()
183 {
184     return static_cast<long>((m_stopTime - m_startTime) * 1000);
185 }
186
187 class GlobalObject : public JSGlobalObject {
188 private:
189     GlobalObject(VM&, Structure*);
190
191 public:
192     typedef JSGlobalObject Base;
193
194     static GlobalObject* create(VM& vm, Structure* structure, const Vector<String>& arguments)
195     {
196         GlobalObject* object = new (NotNull, allocateCell<GlobalObject>(vm.heap)) GlobalObject(vm, structure);
197         object->finishCreation(vm, arguments);
198         vm.heap.addFinalizer(object, destroy);
199         object->setGlobalThis(vm, JSProxy::create(vm, JSProxy::createStructure(vm, object, object->prototype()), object));
200         return object;
201     }
202
203     static const bool needsDestruction = false;
204
205     static const ClassInfo s_info;
206     static const GlobalObjectMethodTable s_globalObjectMethodTable;
207
208     static Structure* createStructure(VM& vm, JSValue prototype)
209     {
210         return Structure::create(vm, 0, prototype, TypeInfo(GlobalObjectType, StructureFlags), &s_info);
211     }
212
213     static bool javaScriptExperimentsEnabled(const JSGlobalObject*) { return true; }
214
215 protected:
216     void finishCreation(VM& vm, const Vector<String>& arguments)
217     {
218         Base::finishCreation(vm);
219         
220         addFunction(vm, "debug", functionDebug, 1);
221         addFunction(vm, "describe", functionDescribe, 1);
222         addFunction(vm, "print", functionPrint, 1);
223         addFunction(vm, "quit", functionQuit, 0);
224         addFunction(vm, "gc", functionGC, 0);
225 #ifndef NDEBUG
226         addFunction(vm, "dumpCallFrame", functionDumpCallFrame, 0);
227         addFunction(vm, "releaseExecutableMemory", functionReleaseExecutableMemory, 0);
228 #endif
229         addFunction(vm, "version", functionVersion, 1);
230         addFunction(vm, "run", functionRun, 1);
231         addFunction(vm, "load", functionLoad, 1);
232         addFunction(vm, "checkSyntax", functionCheckSyntax, 1);
233         addFunction(vm, "jscStack", functionJSCStack, 1);
234         addFunction(vm, "readline", functionReadline, 0);
235         addFunction(vm, "preciseTime", functionPreciseTime, 0);
236 #if ENABLE(SAMPLING_FLAGS)
237         addFunction(vm, "setSamplingFlags", functionSetSamplingFlags, 1);
238         addFunction(vm, "clearSamplingFlags", functionClearSamplingFlags, 1);
239 #endif
240         
241         addConstructableFunction(vm, "Uint8Array", constructJSUint8Array, 1);
242         addConstructableFunction(vm, "Uint8ClampedArray", constructJSUint8ClampedArray, 1);
243         addConstructableFunction(vm, "Uint16Array", constructJSUint16Array, 1);
244         addConstructableFunction(vm, "Uint32Array", constructJSUint32Array, 1);
245         addConstructableFunction(vm, "Int8Array", constructJSInt8Array, 1);
246         addConstructableFunction(vm, "Int16Array", constructJSInt16Array, 1);
247         addConstructableFunction(vm, "Int32Array", constructJSInt32Array, 1);
248         addConstructableFunction(vm, "Float32Array", constructJSFloat32Array, 1);
249         addConstructableFunction(vm, "Float64Array", constructJSFloat64Array, 1);
250
251         JSArray* array = constructEmptyArray(globalExec(), 0);
252         for (size_t i = 0; i < arguments.size(); ++i)
253             array->putDirectIndex(globalExec(), i, jsString(globalExec(), arguments[i]));
254         putDirect(vm, Identifier(globalExec(), "arguments"), array);
255     }
256
257     void addFunction(VM& vm, const char* name, NativeFunction function, unsigned arguments)
258     {
259         Identifier identifier(globalExec(), name);
260         putDirect(vm, identifier, JSFunction::create(globalExec(), this, arguments, identifier.string(), function));
261     }
262     
263     void addConstructableFunction(VM& vm, const char* name, NativeFunction function, unsigned arguments)
264     {
265         Identifier identifier(globalExec(), name);
266         putDirect(vm, identifier, JSFunction::create(globalExec(), this, arguments, identifier.string(), function, NoIntrinsic, function));
267     }
268 };
269
270 COMPILE_ASSERT(!IsInteger<GlobalObject>::value, WTF_IsInteger_GlobalObject_false);
271
272 const ClassInfo GlobalObject::s_info = { "global", &JSGlobalObject::s_info, 0, ExecState::globalObjectTable, CREATE_METHOD_TABLE(GlobalObject) };
273 const GlobalObjectMethodTable GlobalObject::s_globalObjectMethodTable = { &allowsAccessFrom, &supportsProfiling, &supportsRichSourceInfo, &shouldInterruptScript, &javaScriptExperimentsEnabled };
274
275
276 GlobalObject::GlobalObject(VM& vm, Structure* structure)
277     : JSGlobalObject(vm, structure, &s_globalObjectMethodTable)
278 {
279 }
280
281 static inline String stringFromUTF(const char* utf8)
282 {
283     // Find the the first non-ascii character, or nul.
284     const char* pos = utf8;
285     while (*pos > 0)
286         pos++;
287     size_t asciiLength = pos - utf8;
288     
289     // Fast case - string is all ascii.
290     if (!*pos)
291         return String(utf8, asciiLength);
292     
293     // Slow case - contains non-ascii characters, use fromUTF8WithLatin1Fallback.
294     ASSERT(*pos < 0);
295     ASSERT(strlen(utf8) == asciiLength + strlen(pos));
296     return String::fromUTF8WithLatin1Fallback(utf8, asciiLength + strlen(pos));
297 }
298
299 static inline SourceCode jscSource(const char* utf8, const String& filename)
300 {
301     String str = stringFromUTF(utf8);
302     return makeSource(str, filename);
303 }
304
305 EncodedJSValue JSC_HOST_CALL functionPrint(ExecState* exec)
306 {
307     for (unsigned i = 0; i < exec->argumentCount(); ++i) {
308         if (i)
309             putchar(' ');
310
311         printf("%s", exec->argument(i).toString(exec)->value(exec).utf8().data());
312     }
313
314     putchar('\n');
315     fflush(stdout);
316     return JSValue::encode(jsUndefined());
317 }
318
319 #ifndef NDEBUG
320 EncodedJSValue JSC_HOST_CALL functionDumpCallFrame(ExecState* exec)
321 {
322     if (!exec->callerFrame()->hasHostCallFrameFlag())
323         exec->vm().interpreter->dumpCallFrame(exec->callerFrame());
324     return JSValue::encode(jsUndefined());
325 }
326 #endif
327
328 EncodedJSValue JSC_HOST_CALL functionDebug(ExecState* exec)
329 {
330     fprintf(stderr, "--> %s\n", exec->argument(0).toString(exec)->value(exec).utf8().data());
331     return JSValue::encode(jsUndefined());
332 }
333
334 EncodedJSValue JSC_HOST_CALL functionDescribe(ExecState* exec)
335 {
336     fprintf(stderr, "--> %s\n", toCString(exec->argument(0)).data());
337     return JSValue::encode(jsUndefined());
338 }
339
340 EncodedJSValue JSC_HOST_CALL functionJSCStack(ExecState* exec)
341 {
342     StringBuilder trace;
343     trace.appendLiteral("--> Stack trace:\n");
344
345     int i = 0;
346     for (StackIterator iter = exec->begin(); iter != exec->end(); ++iter, ++i)
347         trace.append(String::format("    %i   %s\n", i, iter->toString().utf8().data()));
348     fprintf(stderr, "%s", trace.toString().utf8().data());
349     return JSValue::encode(jsUndefined());
350 }
351
352 EncodedJSValue JSC_HOST_CALL functionGC(ExecState* exec)
353 {
354     JSLockHolder lock(exec);
355     exec->heap()->collectAllGarbage();
356     return JSValue::encode(jsUndefined());
357 }
358
359 #ifndef NDEBUG
360 EncodedJSValue JSC_HOST_CALL functionReleaseExecutableMemory(ExecState* exec)
361 {
362     JSLockHolder lock(exec);
363     exec->vm().releaseExecutableMemory();
364     return JSValue::encode(jsUndefined());
365 }
366 #endif
367
368 EncodedJSValue JSC_HOST_CALL functionVersion(ExecState*)
369 {
370     // We need this function for compatibility with the Mozilla JS tests but for now
371     // we don't actually do any version-specific handling
372     return JSValue::encode(jsUndefined());
373 }
374
375 EncodedJSValue JSC_HOST_CALL functionRun(ExecState* exec)
376 {
377     String fileName = exec->argument(0).toString(exec)->value(exec);
378     Vector<char> script;
379     if (!fillBufferWithContentsOfFile(fileName, script))
380         return JSValue::encode(throwError(exec, createError(exec, "Could not open file.")));
381
382     GlobalObject* globalObject = GlobalObject::create(exec->vm(), GlobalObject::createStructure(exec->vm(), jsNull()), Vector<String>());
383
384     JSValue exception;
385     StopWatch stopWatch;
386     stopWatch.start();
387     evaluate(globalObject->globalExec(), jscSource(script.data(), fileName), JSValue(), &exception);
388     stopWatch.stop();
389
390     if (!!exception) {
391         throwError(globalObject->globalExec(), exception);
392         return JSValue::encode(jsUndefined());
393     }
394     
395     return JSValue::encode(jsNumber(stopWatch.getElapsedMS()));
396 }
397
398 EncodedJSValue JSC_HOST_CALL functionLoad(ExecState* exec)
399 {
400     String fileName = exec->argument(0).toString(exec)->value(exec);
401     Vector<char> script;
402     if (!fillBufferWithContentsOfFile(fileName, script))
403         return JSValue::encode(throwError(exec, createError(exec, "Could not open file.")));
404
405     JSGlobalObject* globalObject = exec->lexicalGlobalObject();
406     
407     JSValue evaluationException;
408     JSValue result = evaluate(globalObject->globalExec(), jscSource(script.data(), fileName), JSValue(), &evaluationException);
409     if (evaluationException)
410         throwError(exec, evaluationException);
411     return JSValue::encode(result);
412 }
413
414 EncodedJSValue JSC_HOST_CALL functionCheckSyntax(ExecState* exec)
415 {
416     String fileName = exec->argument(0).toString(exec)->value(exec);
417     Vector<char> script;
418     if (!fillBufferWithContentsOfFile(fileName, script))
419         return JSValue::encode(throwError(exec, createError(exec, "Could not open file.")));
420
421     JSGlobalObject* globalObject = exec->lexicalGlobalObject();
422
423     StopWatch stopWatch;
424     stopWatch.start();
425
426     JSValue syntaxException;
427     bool validSyntax = checkSyntax(globalObject->globalExec(), jscSource(script.data(), fileName), &syntaxException);
428     stopWatch.stop();
429
430     if (!validSyntax)
431         throwError(exec, syntaxException);
432     return JSValue::encode(jsNumber(stopWatch.getElapsedMS()));
433 }
434
435 #if ENABLE(SAMPLING_FLAGS)
436 EncodedJSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState* exec)
437 {
438     for (unsigned i = 0; i < exec->argumentCount(); ++i) {
439         unsigned flag = static_cast<unsigned>(exec->argument(i).toNumber(exec));
440         if ((flag >= 1) && (flag <= 32))
441             SamplingFlags::setFlag(flag);
442     }
443     return JSValue::encode(jsNull());
444 }
445
446 EncodedJSValue JSC_HOST_CALL functionClearSamplingFlags(ExecState* exec)
447 {
448     for (unsigned i = 0; i < exec->argumentCount(); ++i) {
449         unsigned flag = static_cast<unsigned>(exec->argument(i).toNumber(exec));
450         if ((flag >= 1) && (flag <= 32))
451             SamplingFlags::clearFlag(flag);
452     }
453     return JSValue::encode(jsNull());
454 }
455 #endif
456
457 EncodedJSValue JSC_HOST_CALL functionReadline(ExecState* exec)
458 {
459     Vector<char, 256> line;
460     int c;
461     while ((c = getchar()) != EOF) {
462         // FIXME: Should we also break on \r? 
463         if (c == '\n')
464             break;
465         line.append(c);
466     }
467     line.append('\0');
468     return JSValue::encode(jsString(exec, line.data()));
469 }
470
471 EncodedJSValue JSC_HOST_CALL functionPreciseTime(ExecState*)
472 {
473     return JSValue::encode(jsNumber(currentTime()));
474 }
475
476 EncodedJSValue JSC_HOST_CALL functionQuit(ExecState*)
477 {
478     exit(EXIT_SUCCESS);
479
480 #if COMPILER(MSVC) && OS(WINCE)
481     // Without this, Visual Studio will complain that this method does not return a value.
482     return JSValue::encode(jsUndefined());
483 #endif
484 }
485
486 // Use SEH for Release builds only to get rid of the crash report dialog
487 // (luckily the same tests fail in Release and Debug builds so far). Need to
488 // be in a separate main function because the jscmain function requires object
489 // unwinding.
490
491 #if COMPILER(MSVC) && !COMPILER(INTEL) && !defined(_DEBUG) && !OS(WINCE)
492 #define TRY       __try {
493 #define EXCEPT(x) } __except (EXCEPTION_EXECUTE_HANDLER) { x; }
494 #else
495 #define TRY
496 #define EXCEPT(x)
497 #endif
498
499 int jscmain(int argc, char** argv);
500
501 int main(int argc, char** argv)
502 {
503 #if PLATFORM(IOS)
504     // Enabled IEEE754 denormal support.
505     fenv_t env;
506     fegetenv( &env );
507     env.__fpscr &= ~0x01000000u;
508     fesetenv( &env );
509 #endif
510
511 #if OS(WINDOWS)
512 #if !OS(WINCE)
513     // Cygwin calls ::SetErrorMode(SEM_FAILCRITICALERRORS), which we will inherit. This is bad for
514     // testing/debugging, as it causes the post-mortem debugger not to be invoked. We reset the
515     // error mode here to work around Cygwin's behavior. See <http://webkit.org/b/55222>.
516     ::SetErrorMode(0);
517 #endif
518
519 #if defined(_DEBUG)
520     _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
521     _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
522     _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
523     _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
524     _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
525     _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
526 #endif
527
528     timeBeginPeriod(1);
529 #endif
530
531 #if PLATFORM(BLACKBERRY)
532     // Write all WTF logs to the system log
533     BlackBerry::Platform::setupApplicationLogging("jsc");
534 #endif
535
536 #if PLATFORM(QT)
537     QCoreApplication app(argc, argv);
538 #endif
539
540 #if PLATFORM(EFL)
541     ecore_init();
542 #endif
543
544     // Initialize JSC before getting VM.
545 #if ENABLE(SAMPLING_REGIONS)
546     WTF::initializeMainThread();
547 #endif
548     JSC::initializeThreading();
549
550     // We can't use destructors in the following code because it uses Windows
551     // Structured Exception Handling
552     int res = 0;
553     TRY
554         res = jscmain(argc, argv);
555     EXCEPT(res = 3)
556     if (Options::logHeapStatisticsAtExit())
557         HeapStatistics::reportSuccess();
558
559 #if PLATFORM(EFL)
560     ecore_shutdown();
561 #endif
562
563     return res;
564 }
565
566 static bool runWithScripts(GlobalObject* globalObject, const Vector<Script>& scripts, bool dump)
567 {
568     const char* script;
569     String fileName;
570     Vector<char> scriptBuffer;
571
572     if (dump)
573         JSC::Options::dumpGeneratedBytecodes() = true;
574
575     VM& vm = globalObject->vm();
576
577 #if ENABLE(SAMPLING_FLAGS)
578     SamplingFlags::start();
579 #endif
580
581     bool success = true;
582     for (size_t i = 0; i < scripts.size(); i++) {
583         if (scripts[i].isFile) {
584             fileName = scripts[i].argument;
585             if (!fillBufferWithContentsOfFile(fileName, scriptBuffer))
586                 return false; // fail early so we can catch missing files
587             script = scriptBuffer.data();
588         } else {
589             script = scripts[i].argument;
590             fileName = "[Command Line]";
591         }
592
593         vm.startSampling();
594
595         JSValue evaluationException;
596         JSValue returnValue = evaluate(globalObject->globalExec(), jscSource(script, fileName), JSValue(), &evaluationException);
597         success = success && !evaluationException;
598         if (dump && !evaluationException)
599             printf("End: %s\n", returnValue.toString(globalObject->globalExec())->value(globalObject->globalExec()).utf8().data());
600         if (evaluationException) {
601             printf("Exception: %s\n", evaluationException.toString(globalObject->globalExec())->value(globalObject->globalExec()).utf8().data());
602             Identifier stackID(globalObject->globalExec(), "stack");
603             JSValue stackValue = evaluationException.get(globalObject->globalExec(), stackID);
604             if (!stackValue.isUndefinedOrNull())
605                 printf("%s\n", stackValue.toString(globalObject->globalExec())->value(globalObject->globalExec()).utf8().data());
606         }
607
608         vm.stopSampling();
609         globalObject->globalExec()->clearException();
610     }
611
612 #if ENABLE(SAMPLING_FLAGS)
613     SamplingFlags::stop();
614 #endif
615 #if ENABLE(SAMPLING_REGIONS)
616     SamplingRegion::dump();
617 #endif
618     vm.dumpSampleData(globalObject->globalExec());
619 #if ENABLE(SAMPLING_COUNTERS)
620     AbstractSamplingCounter::dump();
621 #endif
622 #if ENABLE(REGEXP_TRACING)
623     vm.dumpRegExpTrace();
624 #endif
625     return success;
626 }
627
628 #define RUNNING_FROM_XCODE 0
629
630 static void runInteractive(GlobalObject* globalObject)
631 {
632     String interpreterName("Interpreter");
633     
634     bool shouldQuit = false;
635     while (!shouldQuit) {
636 #if HAVE(READLINE) && !RUNNING_FROM_XCODE
637         ParserError error;
638         String source;
639         do {
640             error = ParserError();
641             char* line = readline(source.isEmpty() ? interactivePrompt : "... ");
642             shouldQuit = !line;
643             if (!line)
644                 break;
645             source = source + line;
646             source = source + '\n';
647             checkSyntax(globalObject->vm(), makeSource(source, interpreterName), error);
648             if (!line[0])
649                 break;
650             add_history(line);
651         } while (error.m_syntaxErrorType == ParserError::SyntaxErrorRecoverable);
652         
653         if (error.m_type != ParserError::ErrorNone) {
654             printf("%s:%d\n", error.m_message.utf8().data(), error.m_line);
655             continue;
656         }
657         
658         
659         JSValue evaluationException;
660         JSValue returnValue = evaluate(globalObject->globalExec(), makeSource(source, interpreterName), JSValue(), &evaluationException);
661 #else
662         printf("%s", interactivePrompt);
663         Vector<char, 256> line;
664         int c;
665         while ((c = getchar()) != EOF) {
666             // FIXME: Should we also break on \r? 
667             if (c == '\n')
668                 break;
669             line.append(c);
670         }
671         if (line.isEmpty())
672             break;
673         line.append('\0');
674
675         JSValue evaluationException;
676         JSValue returnValue = evaluate(globalObject->globalExec(), jscSource(line.data(), interpreterName), JSValue(), &evaluationException);
677 #endif
678         if (evaluationException)
679             printf("Exception: %s\n", evaluationException.toString(globalObject->globalExec())->value(globalObject->globalExec()).utf8().data());
680         else
681             printf("%s\n", returnValue.toString(globalObject->globalExec())->value(globalObject->globalExec()).utf8().data());
682
683         globalObject->globalExec()->clearException();
684     }
685     printf("\n");
686 }
687
688 static NO_RETURN void printUsageStatement(bool help = false)
689 {
690     fprintf(stderr, "Usage: jsc [options] [files] [-- arguments]\n");
691     fprintf(stderr, "  -d         Dumps bytecode (debug builds only)\n");
692     fprintf(stderr, "  -e         Evaluate argument as script code\n");
693     fprintf(stderr, "  -f         Specifies a source file (deprecated)\n");
694     fprintf(stderr, "  -h|--help  Prints this help message\n");
695     fprintf(stderr, "  -i         Enables interactive mode (default if no files are specified)\n");
696 #if HAVE(SIGNAL_H)
697     fprintf(stderr, "  -s         Installs signal handlers that exit on a crash (Unix platforms only)\n");
698 #endif
699     fprintf(stderr, "  -p <file>  Outputs profiling data to a file\n");
700     fprintf(stderr, "  -x         Output exit code before terminating\n");
701     fprintf(stderr, "\n");
702     fprintf(stderr, "  --options                  Dumps all JSC VM options and exits\n");
703     fprintf(stderr, "  --dumpOptions              Dumps all JSC VM options before continuing\n");
704     fprintf(stderr, "  --<jsc VM option>=<value>  Sets the specified JSC VM option\n");
705     fprintf(stderr, "\n");
706
707     exit(help ? EXIT_SUCCESS : EXIT_FAILURE);
708 }
709
710 void CommandLine::parseArguments(int argc, char** argv)
711 {
712     int i = 1;
713     bool needToDumpOptions = false;
714     bool needToExit = false;
715
716     for (; i < argc; ++i) {
717         const char* arg = argv[i];
718         if (!strcmp(arg, "-f")) {
719             if (++i == argc)
720                 printUsageStatement();
721             m_scripts.append(Script(true, argv[i]));
722             continue;
723         }
724         if (!strcmp(arg, "-e")) {
725             if (++i == argc)
726                 printUsageStatement();
727             m_scripts.append(Script(false, argv[i]));
728             continue;
729         }
730         if (!strcmp(arg, "-i")) {
731             m_interactive = true;
732             continue;
733         }
734         if (!strcmp(arg, "-d")) {
735             m_dump = true;
736             continue;
737         }
738         if (!strcmp(arg, "-p")) {
739             if (++i == argc)
740                 printUsageStatement();
741             m_profile = true;
742             m_profilerOutput = argv[i];
743             continue;
744         }
745         if (!strcmp(arg, "-s")) {
746 #if HAVE(SIGNAL_H)
747             signal(SIGILL, _exit);
748             signal(SIGFPE, _exit);
749             signal(SIGBUS, _exit);
750             signal(SIGSEGV, _exit);
751 #endif
752             continue;
753         }
754         if (!strcmp(arg, "-x")) {
755             m_exitCode = true;
756             continue;
757         }
758         if (!strcmp(arg, "--")) {
759             ++i;
760             break;
761         }
762         if (!strcmp(arg, "-h") || !strcmp(arg, "--help"))
763             printUsageStatement(true);
764
765         if (!strcmp(arg, "--options")) {
766             needToDumpOptions = true;
767             needToExit = true;
768             continue;
769         }
770         if (!strcmp(arg, "--dumpOptions")) {
771             needToDumpOptions = true;
772             continue;
773         }
774
775         // See if the -- option is a JSC VM option.
776         // NOTE: At this point, we know that the arg starts with "--". Skip it.
777         if (JSC::Options::setOption(&arg[2])) {
778             // The arg was recognized as a VM option and has been parsed.
779             continue; // Just continue with the next arg. 
780         }
781
782         // This arg is not recognized by the VM nor by jsc. Pass it on to the
783         // script.
784         m_scripts.append(Script(true, argv[i]));
785     }
786
787     if (m_scripts.isEmpty())
788         m_interactive = true;
789
790     for (; i < argc; ++i)
791         m_arguments.append(argv[i]);
792
793     if (needToDumpOptions)
794         JSC::Options::dumpAllOptions(stderr);
795     if (needToExit)
796         exit(EXIT_SUCCESS);
797 }
798
799 int jscmain(int argc, char** argv)
800 {
801     // Note that the options parsing can affect VM creation, and thus
802     // comes first.
803     CommandLine options(argc, argv);
804     VM* vm = VM::create(LargeHeap).leakRef();
805     APIEntryShim shim(vm);
806     int result;
807
808     if (options.m_profile && !vm->m_perBytecodeProfiler)
809         vm->m_perBytecodeProfiler = adoptPtr(new Profiler::Database(*vm));
810     
811     GlobalObject* globalObject = GlobalObject::create(*vm, GlobalObject::createStructure(*vm, jsNull()), options.m_arguments);
812     bool success = runWithScripts(globalObject, options.m_scripts, options.m_dump);
813     if (options.m_interactive && success)
814         runInteractive(globalObject);
815
816     result = success ? 0 : 3;
817
818     if (options.m_exitCode)
819         printf("jsc exiting %d\n", result);
820     
821     if (options.m_profile) {
822         if (!vm->m_perBytecodeProfiler->save(options.m_profilerOutput.utf8().data()))
823             fprintf(stderr, "could not save profiler output.\n");
824     }
825
826     return result;
827 }
828
829 static bool fillBufferWithContentsOfFile(const String& fileName, Vector<char>& buffer)
830 {
831     FILE* f = fopen(fileName.utf8().data(), "r");
832     if (!f) {
833         fprintf(stderr, "Could not open file: %s\n", fileName.utf8().data());
834         return false;
835     }
836
837     size_t bufferSize = 0;
838     size_t bufferCapacity = 1024;
839
840     buffer.resize(bufferCapacity);
841
842     while (!feof(f) && !ferror(f)) {
843         bufferSize += fread(buffer.data() + bufferSize, 1, bufferCapacity - bufferSize, f);
844         if (bufferSize == bufferCapacity) { // guarantees space for trailing '\0'
845             bufferCapacity *= 2;
846             buffer.resize(bufferCapacity);
847         }
848     }
849     fclose(f);
850     buffer[bufferSize] = '\0';
851
852     if (buffer[0] == '#' && buffer[1] == '!')
853         buffer[0] = buffer[1] = '/';
854
855     return true;
856 }