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