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