source: webkit/trunk/Source/JavaScriptCore/jsc.cpp@ 114702

Last change on this file since 114702 was 114702, checked in by [email protected], 13 years ago

Exception stack traces aren't complete when the exception starts in native code
https://bugs.webkit.org/show_bug.cgi?id=84073

Reviewed by Filip Pizlo.

Source/JavaScriptCore:

Refactored building the stack trace to so that we can construct
it earlier, and don't rely on any prior work performed in the
exception handling machinery. Also updated LLInt and the DFG to
completely initialise the callframes of host function calls.

Also fixed a few LLInt paths that failed to correctly update the
topCallFrame.

  • JavaScriptCore.vcproj/JavaScriptCore/JavaScriptCore.def:
  • dfg/DFGJITCompiler.h:
  • dfg/DFGOperations.cpp:
  • dfg/DFGSpeculativeJIT32_64.cpp:

(JSC::DFG::SpeculativeJIT::emitCall):

  • dfg/DFGSpeculativeJIT64.cpp:

(JSC::DFG::SpeculativeJIT::emitCall):

  • interpreter/Interpreter.cpp:

(JSC::eval):
(JSC::Interpreter::getStackTrace):
(JSC::Interpreter::addStackTraceIfNecessary):
(JSC):
(JSC::Interpreter::throwException):

  • interpreter/Interpreter.h:

(Interpreter):

  • jit/JITCall.cpp:

(JSC::JIT::compileOpCall):

  • jit/JITCall32_64.cpp:

(JSC::JIT::compileOpCall):

  • jit/JITOpcodes.cpp:

(JSC::JIT::privateCompileCTINativeCall):

  • jit/JITOpcodes32_64.cpp:

(JSC::JIT::privateCompileCTINativeCall):

  • jsc.cpp:

(functionJSCStack):

  • llint/LLIntExceptions.cpp:

(JSC::LLInt::interpreterThrowInCaller):
(JSC::LLInt::returnToThrow):
(JSC::LLInt::callToThrow):

  • llint/LLIntSlowPaths.cpp:

(JSC::LLInt::handleHostCall):

  • llint/LowLevelInterpreter32_64.asm:
  • llint/LowLevelInterpreter64.asm:
  • parser/Parser.h:

(JSC::::parse):

  • runtime/Error.cpp:

(JSC::addErrorInfo):
(JSC::throwError):

  • runtime/Error.h:

(JSC):

LayoutTests:

Update tests to show complete trace information

  • fast/js/exception-properties-expected.txt:
  • fast/js/script-tests/exception-properties.js:
  • fast/js/script-tests/stack-trace.js:

(selfRecursive1):

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