2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3 * Copyright (C) 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
4 * Copyright (C) 2006 Bjoern Graf (bjoern.graf@gmail.com)
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.
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.
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.
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"
32 #include "JSCTypedArrayStubs.h"
33 #include "JSFunction.h"
36 #include <wtf/MainThread.h>
37 #include "SamplingTool.h"
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>
64 #if COMPILER(MSVC) && !OS(WINCE)
71 #include <QCoreApplication>
83 static bool fillBufferWithContentsOfFile(const UString
& fileName
, Vector
<char>& buffer
);
85 static EncodedJSValue JSC_HOST_CALL
functionPrint(ExecState
*);
86 static EncodedJSValue JSC_HOST_CALL
functionDebug(ExecState
*);
87 static EncodedJSValue JSC_HOST_CALL
functionJSCStack(ExecState
*);
88 static EncodedJSValue JSC_HOST_CALL
functionGC(ExecState
*);
90 static EncodedJSValue JSC_HOST_CALL
functionReleaseExecutableMemory(ExecState
*);
92 static EncodedJSValue JSC_HOST_CALL
functionVersion(ExecState
*);
93 static EncodedJSValue JSC_HOST_CALL
functionRun(ExecState
*);
94 static EncodedJSValue JSC_HOST_CALL
functionLoad(ExecState
*);
95 static EncodedJSValue JSC_HOST_CALL
functionCheckSyntax(ExecState
*);
96 static EncodedJSValue JSC_HOST_CALL
functionReadline(ExecState
*);
97 static EncodedJSValue JSC_HOST_CALL
functionPreciseTime(ExecState
*);
98 static NO_RETURN_WITH_VALUE EncodedJSValue JSC_HOST_CALL
functionQuit(ExecState
*);
100 #if ENABLE(SAMPLING_FLAGS)
101 static EncodedJSValue JSC_HOST_CALL
functionSetSamplingFlags(ExecState
*);
102 static EncodedJSValue JSC_HOST_CALL
functionClearSamplingFlags(ExecState
*);
109 Script(bool isFile
, char *argument
)
125 Vector
<Script
> scripts
;
126 Vector
<UString
> arguments
;
129 static const char interactivePrompt
[] = "> ";
135 long getElapsedMS(); // call stop() first
142 void StopWatch::start()
144 m_startTime
= currentTime();
147 void StopWatch::stop()
149 m_stopTime
= currentTime();
152 long StopWatch::getElapsedMS()
154 return static_cast<long>((m_stopTime
- m_startTime
) * 1000);
157 class GlobalObject
: public JSGlobalObject
{
159 GlobalObject(JSGlobalData
&, Structure
*);
162 typedef JSGlobalObject Base
;
164 static GlobalObject
* create(JSGlobalData
& globalData
, Structure
* structure
, const Vector
<UString
>& arguments
)
166 GlobalObject
* object
= new (NotNull
, allocateCell
<GlobalObject
>(globalData
.heap
)) GlobalObject(globalData
, structure
);
167 object
->finishCreation(globalData
, arguments
);
171 static const ClassInfo s_info
;
173 static Structure
* createStructure(JSGlobalData
& globalData
, JSValue prototype
)
175 return Structure::create(globalData
, 0, prototype
, TypeInfo(GlobalObjectType
, StructureFlags
), &s_info
);
179 void finishCreation(JSGlobalData
& globalData
, const Vector
<UString
>& arguments
)
181 Base::finishCreation(globalData
);
183 addFunction(globalData
, "debug", functionDebug
, 1);
184 addFunction(globalData
, "print", functionPrint
, 1);
185 addFunction(globalData
, "quit", functionQuit
, 0);
186 addFunction(globalData
, "gc", functionGC
, 0);
188 addFunction(globalData
, "releaseExecutableMemory", functionReleaseExecutableMemory
, 0);
190 addFunction(globalData
, "version", functionVersion
, 1);
191 addFunction(globalData
, "run", functionRun
, 1);
192 addFunction(globalData
, "load", functionLoad
, 1);
193 addFunction(globalData
, "checkSyntax", functionCheckSyntax
, 1);
194 addFunction(globalData
, "jscStack", functionJSCStack
, 1);
195 addFunction(globalData
, "readline", functionReadline
, 0);
196 addFunction(globalData
, "preciseTime", functionPreciseTime
, 0);
197 #if ENABLE(SAMPLING_FLAGS)
198 addFunction(globalData
, "setSamplingFlags", functionSetSamplingFlags
, 1);
199 addFunction(globalData
, "clearSamplingFlags", functionClearSamplingFlags
, 1);
202 addConstructableFunction(globalData
, "Uint8Array", constructJSUint8Array
, 1);
203 addConstructableFunction(globalData
, "Uint8ClampedArray", constructJSUint8ClampedArray
, 1);
204 addConstructableFunction(globalData
, "Uint16Array", constructJSUint16Array
, 1);
205 addConstructableFunction(globalData
, "Uint32Array", constructJSUint32Array
, 1);
206 addConstructableFunction(globalData
, "Int8Array", constructJSInt8Array
, 1);
207 addConstructableFunction(globalData
, "Int16Array", constructJSInt16Array
, 1);
208 addConstructableFunction(globalData
, "Int32Array", constructJSInt32Array
, 1);
209 addConstructableFunction(globalData
, "Float32Array", constructJSFloat32Array
, 1);
210 addConstructableFunction(globalData
, "Float64Array", constructJSFloat64Array
, 1);
212 JSArray
* array
= constructEmptyArray(globalExec());
213 for (size_t i
= 0; i
< arguments
.size(); ++i
)
214 array
->putDirectIndex(globalExec(), i
, jsString(globalExec(), arguments
[i
]), false);
215 putDirect(globalData
, Identifier(globalExec(), "arguments"), array
);
218 void addFunction(JSGlobalData
& globalData
, const char* name
, NativeFunction function
, unsigned arguments
)
220 Identifier
identifier(globalExec(), name
);
221 putDirect(globalData
, identifier
, JSFunction::create(globalExec(), this, arguments
, identifier
, function
));
224 void addConstructableFunction(JSGlobalData
& globalData
, const char* name
, NativeFunction function
, unsigned arguments
)
226 Identifier
identifier(globalExec(), name
);
227 putDirect(globalData
, identifier
, JSFunction::create(globalExec(), this, arguments
, identifier
, function
, NoIntrinsic
, function
));
230 COMPILE_ASSERT(!IsInteger
<GlobalObject
>::value
, WTF_IsInteger_GlobalObject_false
);
231 ASSERT_CLASS_FITS_IN_CELL(GlobalObject
);
233 const ClassInfo
GlobalObject::s_info
= { "global", &JSGlobalObject::s_info
, 0, ExecState::globalObjectTable
, CREATE_METHOD_TABLE(GlobalObject
) };
235 GlobalObject::GlobalObject(JSGlobalData
& globalData
, Structure
* structure
)
236 : JSGlobalObject(globalData
, structure
)
240 static inline SourceCode
jscSource(const char* utf8
, const UString
& filename
)
242 // Find the the first non-ascii character, or nul.
243 const char* pos
= utf8
;
246 size_t asciiLength
= pos
- utf8
;
248 // Fast case - string is all ascii.
250 return makeSource(UString(utf8
, asciiLength
), filename
);
252 // Slow case - contains non-ascii characters, use fromUTF8WithLatin1Fallback.
254 ASSERT(strlen(utf8
) == asciiLength
+ strlen(pos
));
255 String source
= String::fromUTF8WithLatin1Fallback(utf8
, asciiLength
+ strlen(pos
));
256 return makeSource(source
.impl(), filename
);
259 EncodedJSValue JSC_HOST_CALL
functionPrint(ExecState
* exec
)
261 for (unsigned i
= 0; i
< exec
->argumentCount(); ++i
) {
265 printf("%s", exec
->argument(i
).toString(exec
)->value(exec
).utf8().data());
270 return JSValue::encode(jsUndefined());
273 EncodedJSValue JSC_HOST_CALL
functionDebug(ExecState
* exec
)
275 fprintf(stderr
, "--> %s\n", exec
->argument(0).toString(exec
)->value(exec
).utf8().data());
276 return JSValue::encode(jsUndefined());
279 EncodedJSValue JSC_HOST_CALL
functionJSCStack(ExecState
* exec
)
281 String trace
= "--> Stack trace:\n";
282 Vector
<StackFrame
> stackTrace
;
283 Interpreter::getStackTrace(&exec
->globalData(), stackTrace
);
286 for (Vector
<StackFrame
>::iterator iter
= stackTrace
.begin(); iter
< stackTrace
.end(); iter
++) {
287 StackFrame level
= *iter
;
288 trace
+= String::format(" %i %s\n", i
, level
.toString(exec
).utf8().data());
291 fprintf(stderr
, "%s", trace
.utf8().data());
292 return JSValue::encode(jsUndefined());
295 EncodedJSValue JSC_HOST_CALL
functionGC(ExecState
* exec
)
297 JSLockHolder
lock(exec
);
298 exec
->heap()->collectAllGarbage();
299 return JSValue::encode(jsUndefined());
303 EncodedJSValue JSC_HOST_CALL
functionReleaseExecutableMemory(ExecState
* exec
)
305 JSLockHolder
lock(exec
);
306 exec
->globalData().releaseExecutableMemory();
307 return JSValue::encode(jsUndefined());
311 EncodedJSValue JSC_HOST_CALL
functionVersion(ExecState
*)
313 // We need this function for compatibility with the Mozilla JS tests but for now
314 // we don't actually do any version-specific handling
315 return JSValue::encode(jsUndefined());
318 EncodedJSValue JSC_HOST_CALL
functionRun(ExecState
* exec
)
320 UString fileName
= exec
->argument(0).toString(exec
)->value(exec
);
322 if (!fillBufferWithContentsOfFile(fileName
, script
))
323 return JSValue::encode(throwError(exec
, createError(exec
, "Could not open file.")));
325 GlobalObject
* globalObject
= GlobalObject::create(exec
->globalData(), GlobalObject::createStructure(exec
->globalData(), jsNull()), Vector
<UString
>());
330 evaluate(globalObject
->globalExec(), globalObject
->globalScopeChain(), jscSource(script
.data(), fileName
), JSValue(), &exception
);
334 throwError(globalObject
->globalExec(), exception
);
335 return JSValue::encode(jsUndefined());
338 return JSValue::encode(jsNumber(stopWatch
.getElapsedMS()));
341 EncodedJSValue JSC_HOST_CALL
functionLoad(ExecState
* exec
)
343 UString fileName
= exec
->argument(0).toString(exec
)->value(exec
);
345 if (!fillBufferWithContentsOfFile(fileName
, script
))
346 return JSValue::encode(throwError(exec
, createError(exec
, "Could not open file.")));
348 JSGlobalObject
* globalObject
= exec
->lexicalGlobalObject();
350 JSValue evaluationException
;
351 JSValue result
= evaluate(globalObject
->globalExec(), globalObject
->globalScopeChain(), jscSource(script
.data(), fileName
), JSValue(), &evaluationException
);
352 if (evaluationException
)
353 throwError(exec
, evaluationException
);
354 return JSValue::encode(result
);
357 EncodedJSValue JSC_HOST_CALL
functionCheckSyntax(ExecState
* exec
)
359 UString fileName
= exec
->argument(0).toString(exec
)->value(exec
);
361 if (!fillBufferWithContentsOfFile(fileName
, script
))
362 return JSValue::encode(throwError(exec
, createError(exec
, "Could not open file.")));
364 JSGlobalObject
* globalObject
= exec
->lexicalGlobalObject();
369 JSValue syntaxException
;
370 bool validSyntax
= checkSyntax(globalObject
->globalExec(), jscSource(script
.data(), fileName
), &syntaxException
);
374 throwError(exec
, syntaxException
);
375 return JSValue::encode(jsNumber(stopWatch
.getElapsedMS()));
378 #if ENABLE(SAMPLING_FLAGS)
379 EncodedJSValue JSC_HOST_CALL
functionSetSamplingFlags(ExecState
* exec
)
381 for (unsigned i
= 0; i
< exec
->argumentCount(); ++i
) {
382 unsigned flag
= static_cast<unsigned>(exec
->argument(i
).toNumber(exec
));
383 if ((flag
>= 1) && (flag
<= 32))
384 SamplingFlags::setFlag(flag
);
386 return JSValue::encode(jsNull());
389 EncodedJSValue JSC_HOST_CALL
functionClearSamplingFlags(ExecState
* exec
)
391 for (unsigned i
= 0; i
< exec
->argumentCount(); ++i
) {
392 unsigned flag
= static_cast<unsigned>(exec
->argument(i
).toNumber(exec
));
393 if ((flag
>= 1) && (flag
<= 32))
394 SamplingFlags::clearFlag(flag
);
396 return JSValue::encode(jsNull());
400 EncodedJSValue JSC_HOST_CALL
functionReadline(ExecState
* exec
)
402 Vector
<char, 256> line
;
404 while ((c
= getchar()) != EOF
) {
405 // FIXME: Should we also break on \r?
411 return JSValue::encode(jsString(exec
, line
.data()));
414 EncodedJSValue JSC_HOST_CALL
functionPreciseTime(ExecState
*)
416 return JSValue::encode(jsNumber(currentTime()));
419 EncodedJSValue JSC_HOST_CALL
functionQuit(ExecState
*)
423 #if COMPILER(MSVC) && OS(WINCE)
424 // Without this, Visual Studio will complain that this method does not return a value.
425 return JSValue::encode(jsUndefined());
429 // Use SEH for Release builds only to get rid of the crash report dialog
430 // (luckily the same tests fail in Release and Debug builds so far). Need to
431 // be in a separate main function because the jscmain function requires object
434 #if COMPILER(MSVC) && !COMPILER(INTEL) && !defined(_DEBUG) && !OS(WINCE)
436 #define EXCEPT(x) } __except (EXCEPTION_EXECUTE_HANDLER) { x; }
442 int jscmain(int argc
, char** argv
);
444 int main(int argc
, char** argv
)
447 // Enabled IEEE754 denormal support.
450 env
.__fpscr
&= ~0x01000000u
;
456 // Cygwin calls ::SetErrorMode(SEM_FAILCRITICALERRORS), which we will inherit. This is bad for
457 // testing/debugging, as it causes the post-mortem debugger not to be invoked. We reset the
458 // error mode here to work around Cygwin's behavior. See <http://webkit.org/b/55222>.
463 _CrtSetReportFile(_CRT_WARN
, _CRTDBG_FILE_STDERR
);
464 _CrtSetReportMode(_CRT_WARN
, _CRTDBG_MODE_FILE
);
465 _CrtSetReportFile(_CRT_ERROR
, _CRTDBG_FILE_STDERR
);
466 _CrtSetReportMode(_CRT_ERROR
, _CRTDBG_MODE_FILE
);
467 _CrtSetReportFile(_CRT_ASSERT
, _CRTDBG_FILE_STDERR
);
468 _CrtSetReportMode(_CRT_ASSERT
, _CRTDBG_MODE_FILE
);
475 QCoreApplication
app(argc
, argv
);
478 // Initialize JSC before getting JSGlobalData.
479 WTF::initializeMainThread();
480 JSC::initializeThreading();
482 // We can't use destructors in the following code because it uses Windows
483 // Structured Exception Handling
486 res
= jscmain(argc
, argv
);
491 static bool runWithScripts(GlobalObject
* globalObject
, const Vector
<Script
>& scripts
, bool dump
)
495 Vector
<char> scriptBuffer
;
498 BytecodeGenerator::setDumpsGeneratedCode(true);
500 JSGlobalData
& globalData
= globalObject
->globalData();
502 #if ENABLE(SAMPLING_FLAGS)
503 SamplingFlags::start();
507 for (size_t i
= 0; i
< scripts
.size(); i
++) {
508 if (scripts
[i
].isFile
) {
509 fileName
= scripts
[i
].argument
;
510 if (!fillBufferWithContentsOfFile(fileName
, scriptBuffer
))
511 return false; // fail early so we can catch missing files
512 script
= scriptBuffer
.data();
514 script
= scripts
[i
].argument
;
515 fileName
= "[Command Line]";
518 globalData
.startSampling();
520 JSValue evaluationException
;
521 JSValue returnValue
= evaluate(globalObject
->globalExec(), globalObject
->globalScopeChain(), jscSource(script
, fileName
), JSValue(), &evaluationException
);
522 success
= success
&& !evaluationException
;
523 if (dump
&& !evaluationException
)
524 printf("End: %s\n", returnValue
.toString(globalObject
->globalExec())->value(globalObject
->globalExec()).utf8().data());
525 if (evaluationException
) {
526 printf("Exception: %s\n", evaluationException
.toString(globalObject
->globalExec())->value(globalObject
->globalExec()).utf8().data());
527 Identifier
stackID(globalObject
->globalExec(), "stack");
528 JSValue stackValue
= evaluationException
.get(globalObject
->globalExec(), stackID
);
529 if (!stackValue
.isUndefinedOrNull())
530 printf("%s\n", stackValue
.toString(globalObject
->globalExec())->value(globalObject
->globalExec()).utf8().data());
533 globalData
.stopSampling();
534 globalObject
->globalExec()->clearException();
537 #if ENABLE(SAMPLING_FLAGS)
538 SamplingFlags::stop();
540 #if ENABLE(SAMPLING_REGIONS)
541 SamplingRegion::dump();
543 globalData
.dumpSampleData(globalObject
->globalExec());
544 #if ENABLE(SAMPLING_COUNTERS)
545 AbstractSamplingCounter::dump();
547 #if ENABLE(REGEXP_TRACING)
548 globalData
.dumpRegExpTrace();
553 #define RUNNING_FROM_XCODE 0
555 static void runInteractive(GlobalObject
* globalObject
)
557 UString
interpreterName("Interpreter");
560 #if HAVE(READLINE) && !RUNNING_FROM_XCODE
561 char* line
= readline(interactivePrompt
);
566 JSValue evaluationException
;
567 JSValue returnValue
= evaluate(globalObject
->globalExec(), globalObject
->globalScopeChain(), jscSource(line
, interpreterName
), JSValue(), &evaluationException
);
570 printf("%s", interactivePrompt
);
571 Vector
<char, 256> line
;
573 while ((c
= getchar()) != EOF
) {
574 // FIXME: Should we also break on \r?
583 JSValue evaluationException
;
584 JSValue returnValue
= evaluate(globalObject
->globalExec(), globalObject
->globalScopeChain(), jscSource(line
.data(), interpreterName
), JSValue(), &evaluationException
);
586 if (evaluationException
)
587 printf("Exception: %s\n", evaluationException
.toString(globalObject
->globalExec())->value(globalObject
->globalExec()).utf8().data());
589 printf("%s\n", returnValue
.toString(globalObject
->globalExec())->value(globalObject
->globalExec()).utf8().data());
591 globalObject
->globalExec()->clearException();
596 static NO_RETURN
void printUsageStatement(bool help
= false)
598 fprintf(stderr
, "Usage: jsc [options] [files] [-- arguments]\n");
599 fprintf(stderr
, " -d Dumps bytecode (debug builds only)\n");
600 fprintf(stderr
, " -e Evaluate argument as script code\n");
601 fprintf(stderr
, " -f Specifies a source file (deprecated)\n");
602 fprintf(stderr
, " -h|--help Prints this help message\n");
603 fprintf(stderr
, " -i Enables interactive mode (default if no files are specified)\n");
605 fprintf(stderr
, " -s Installs signal handlers that exit on a crash (Unix platforms only)\n");
608 exit(help
? EXIT_SUCCESS
: EXIT_FAILURE
);
611 static void parseArguments(int argc
, char** argv
, CommandLine
& options
)
614 for (; i
< argc
; ++i
) {
615 const char* arg
= argv
[i
];
616 if (!strcmp(arg
, "-f")) {
618 printUsageStatement();
619 options
.scripts
.append(Script(true, argv
[i
]));
622 if (!strcmp(arg
, "-e")) {
624 printUsageStatement();
625 options
.scripts
.append(Script(false, argv
[i
]));
628 if (!strcmp(arg
, "-i")) {
629 options
.interactive
= true;
632 if (!strcmp(arg
, "-d")) {
636 if (!strcmp(arg
, "-s")) {
638 signal(SIGILL
, _exit
);
639 signal(SIGFPE
, _exit
);
640 signal(SIGBUS
, _exit
);
641 signal(SIGSEGV
, _exit
);
645 if (!strcmp(arg
, "--")) {
649 if (!strcmp(arg
, "-h") || !strcmp(arg
, "--help"))
650 printUsageStatement(true);
651 options
.scripts
.append(Script(true, argv
[i
]));
654 if (options
.scripts
.isEmpty())
655 options
.interactive
= true;
657 for (; i
< argc
; ++i
)
658 options
.arguments
.append(argv
[i
]);
661 int jscmain(int argc
, char** argv
)
664 RefPtr
<JSGlobalData
> globalData
= JSGlobalData::create(ThreadStackTypeLarge
, LargeHeap
);
665 JSLockHolder
lock(globalData
.get());
668 parseArguments(argc
, argv
, options
);
670 GlobalObject
* globalObject
= GlobalObject::create(*globalData
, GlobalObject::createStructure(*globalData
, jsNull()), options
.arguments
);
671 bool success
= runWithScripts(globalObject
, options
.scripts
, options
.dump
);
672 if (options
.interactive
&& success
)
673 runInteractive(globalObject
);
675 return success
? 0 : 3;
678 static bool fillBufferWithContentsOfFile(const UString
& fileName
, Vector
<char>& buffer
)
680 FILE* f
= fopen(fileName
.utf8().data(), "r");
682 fprintf(stderr
, "Could not open file: %s\n", fileName
.utf8().data());
686 size_t bufferSize
= 0;
687 size_t bufferCapacity
= 1024;
689 buffer
.resize(bufferCapacity
);
691 while (!feof(f
) && !ferror(f
)) {
692 bufferSize
+= fread(buffer
.data() + bufferSize
, 1, bufferCapacity
- bufferSize
, f
);
693 if (bufferSize
== bufferCapacity
) { // guarantees space for trailing '\0'
695 buffer
.resize(bufferCapacity
);
699 buffer
[bufferSize
] = '\0';
701 if (buffer
[0] == '#' && buffer
[1] == '!')
702 buffer
[0] = buffer
[1] = '/';