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

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

Add option so that JSC will crash if it can't allocate executable memory for the JITs
https://bugs.webkit.org/show_bug.cgi?id=123048
<rdar://problem/12856193>

Patch by Michael Saboff <[email protected]> on 2013-10-21
Reviewed by Geoffrey Garen.

Added new option, called crashIfCantAllocateJITMemory. If this option is true then we crash
when checking the validity of the executable allocator. The default value for this option is
false, but jsc sets it to true when built for iOS to make it straightforward to identify whether
the app can obtain executable memory.

  • jsc.cpp: Explicitly enable crashIfCantAllocateJITMemory on iOS.

(main):

  • runtime/Options.h: Added option crashIfCantAllocateJITMemory.
  • runtime/VM.cpp:

(JSC::enableAssembler): Modified to crash if option crashIfCantAllocateJITMemory
is enabled.

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