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 "CurrentTime.h"
28 #include "ExceptionHelpers.h"
29 #include "InitializeThreading.h"
31 #include "JSFunction.h"
34 #include "SamplingTool.h"
45 #include <readline/history.h>
46 #include <readline/readline.h>
57 #if COMPILER(MSVC) && !OS(WINCE)
64 #include <QCoreApplication>
71 static void cleanupGlobalData(JSGlobalData
*);
72 static bool fillBufferWithContentsOfFile(const UString
& fileName
, Vector
<char>& buffer
);
74 static EncodedJSValue JSC_HOST_CALL
functionPrint(ExecState
*);
75 static EncodedJSValue JSC_HOST_CALL
functionDebug(ExecState
*);
76 static EncodedJSValue JSC_HOST_CALL
functionGC(ExecState
*);
78 static EncodedJSValue JSC_HOST_CALL
functionReleaseExecutableMemory(ExecState
*);
80 static EncodedJSValue JSC_HOST_CALL
functionVersion(ExecState
*);
81 static EncodedJSValue JSC_HOST_CALL
functionRun(ExecState
*);
82 static EncodedJSValue JSC_HOST_CALL
functionLoad(ExecState
*);
83 static EncodedJSValue JSC_HOST_CALL
functionCheckSyntax(ExecState
*);
84 static EncodedJSValue JSC_HOST_CALL
functionReadline(ExecState
*);
85 static NO_RETURN_WITH_VALUE EncodedJSValue JSC_HOST_CALL
functionQuit(ExecState
*);
87 #if ENABLE(SAMPLING_FLAGS)
88 static EncodedJSValue JSC_HOST_CALL
functionSetSamplingFlags(ExecState
*);
89 static EncodedJSValue JSC_HOST_CALL
functionClearSamplingFlags(ExecState
*);
96 Script(bool isFile
, char *argument
)
112 Vector
<Script
> scripts
;
113 Vector
<UString
> arguments
;
116 static const char interactivePrompt
[] = "> ";
117 static const UString
interpreterName("Interpreter");
123 long getElapsedMS(); // call stop() first
130 void StopWatch::start()
132 m_startTime
= currentTime();
135 void StopWatch::stop()
137 m_stopTime
= currentTime();
140 long StopWatch::getElapsedMS()
142 return static_cast<long>((m_stopTime
- m_startTime
) * 1000);
145 class GlobalObject
: public JSGlobalObject
{
147 GlobalObject(JSGlobalData
&, Structure
*, const Vector
<UString
>& arguments
);
148 virtual UString
className() const { return "global"; }
150 COMPILE_ASSERT(!IsInteger
<GlobalObject
>::value
, WTF_IsInteger_GlobalObject_false
);
151 ASSERT_CLASS_FITS_IN_CELL(GlobalObject
);
153 GlobalObject::GlobalObject(JSGlobalData
& globalData
, Structure
* structure
, const Vector
<UString
>& arguments
)
154 : JSGlobalObject(globalData
, structure
)
156 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 1, Identifier(globalExec(), "debug"), functionDebug
));
157 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 1, Identifier(globalExec(), "print"), functionPrint
));
158 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 0, Identifier(globalExec(), "quit"), functionQuit
));
159 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 0, Identifier(globalExec(), "gc"), functionGC
));
161 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 0, Identifier(globalExec(), "releaseExecutableMemory"), functionReleaseExecutableMemory
));
163 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 1, Identifier(globalExec(), "version"), functionVersion
));
164 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 1, Identifier(globalExec(), "run"), functionRun
));
165 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 1, Identifier(globalExec(), "load"), functionLoad
));
166 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 1, Identifier(globalExec(), "checkSyntax"), functionCheckSyntax
));
167 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 0, Identifier(globalExec(), "readline"), functionReadline
));
169 #if ENABLE(SAMPLING_FLAGS)
170 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 1, Identifier(globalExec(), "setSamplingFlags"), functionSetSamplingFlags
));
171 putDirectFunction(globalExec(), new (globalExec()) JSFunction(globalExec(), this, functionStructure(), 1, Identifier(globalExec(), "clearSamplingFlags"), functionClearSamplingFlags
));
174 JSObject
* array
= constructEmptyArray(globalExec());
175 for (size_t i
= 0; i
< arguments
.size(); ++i
)
176 array
->put(globalExec(), i
, jsString(globalExec(), arguments
[i
]));
177 putDirect(globalExec()->globalData(), Identifier(globalExec(), "arguments"), array
);
180 EncodedJSValue JSC_HOST_CALL
functionPrint(ExecState
* exec
)
182 for (unsigned i
= 0; i
< exec
->argumentCount(); ++i
) {
186 printf("%s", exec
->argument(i
).toString(exec
).utf8().data());
191 return JSValue::encode(jsUndefined());
194 EncodedJSValue JSC_HOST_CALL
functionDebug(ExecState
* exec
)
196 fprintf(stderr
, "--> %s\n", exec
->argument(0).toString(exec
).utf8().data());
197 return JSValue::encode(jsUndefined());
200 EncodedJSValue JSC_HOST_CALL
functionGC(ExecState
* exec
)
202 JSLock
lock(SilenceAssertionsOnly
);
203 exec
->heap()->collectAllGarbage();
204 return JSValue::encode(jsUndefined());
208 EncodedJSValue JSC_HOST_CALL
functionReleaseExecutableMemory(ExecState
* exec
)
210 JSLock
lock(SilenceAssertionsOnly
);
211 exec
->globalData().releaseExecutableMemory();
212 return JSValue::encode(jsUndefined());
216 EncodedJSValue JSC_HOST_CALL
functionVersion(ExecState
*)
218 // We need this function for compatibility with the Mozilla JS tests but for now
219 // we don't actually do any version-specific handling
220 return JSValue::encode(jsUndefined());
223 EncodedJSValue JSC_HOST_CALL
functionRun(ExecState
* exec
)
225 UString fileName
= exec
->argument(0).toString(exec
);
227 if (!fillBufferWithContentsOfFile(fileName
, script
))
228 return JSValue::encode(throwError(exec
, createError(exec
, "Could not open file.")));
230 GlobalObject
* globalObject
= new (&exec
->globalData()) GlobalObject(exec
->globalData(), GlobalObject::createStructure(exec
->globalData(), jsNull()), Vector
<UString
>());
234 evaluate(globalObject
->globalExec(), globalObject
->globalScopeChain(), makeSource(script
.data(), fileName
));
237 return JSValue::encode(jsNumber(stopWatch
.getElapsedMS()));
240 EncodedJSValue JSC_HOST_CALL
functionLoad(ExecState
* exec
)
242 UString fileName
= exec
->argument(0).toString(exec
);
244 if (!fillBufferWithContentsOfFile(fileName
, script
))
245 return JSValue::encode(throwError(exec
, createError(exec
, "Could not open file.")));
247 JSGlobalObject
* globalObject
= exec
->lexicalGlobalObject();
248 Completion result
= evaluate(globalObject
->globalExec(), globalObject
->globalScopeChain(), makeSource(script
.data(), fileName
));
249 if (result
.complType() == Throw
)
250 throwError(exec
, result
.value());
251 return JSValue::encode(result
.value());
254 EncodedJSValue JSC_HOST_CALL
functionCheckSyntax(ExecState
* exec
)
256 UString fileName
= exec
->argument(0).toString(exec
);
258 if (!fillBufferWithContentsOfFile(fileName
, script
))
259 return JSValue::encode(throwError(exec
, createError(exec
, "Could not open file.")));
261 JSGlobalObject
* globalObject
= exec
->lexicalGlobalObject();
265 Completion result
= checkSyntax(globalObject
->globalExec(), makeSource(script
.data(), fileName
));
268 if (result
.complType() == Throw
)
269 throwError(exec
, result
.value());
270 return JSValue::encode(jsNumber(stopWatch
.getElapsedMS()));
273 #if ENABLE(SAMPLING_FLAGS)
274 EncodedJSValue JSC_HOST_CALL
functionSetSamplingFlags(ExecState
* exec
)
276 for (unsigned i
= 0; i
< exec
->argumentCount(); ++i
) {
277 unsigned flag
= static_cast<unsigned>(exec
->argument(i
).toNumber(exec
));
278 if ((flag
>= 1) && (flag
<= 32))
279 SamplingFlags::setFlag(flag
);
281 return JSValue::encode(jsNull());
284 EncodedJSValue JSC_HOST_CALL
functionClearSamplingFlags(ExecState
* exec
)
286 for (unsigned i
= 0; i
< exec
->argumentCount(); ++i
) {
287 unsigned flag
= static_cast<unsigned>(exec
->argument(i
).toNumber(exec
));
288 if ((flag
>= 1) && (flag
<= 32))
289 SamplingFlags::clearFlag(flag
);
291 return JSValue::encode(jsNull());
295 EncodedJSValue JSC_HOST_CALL
functionReadline(ExecState
* exec
)
297 Vector
<char, 256> line
;
299 while ((c
= getchar()) != EOF
) {
300 // FIXME: Should we also break on \r?
306 return JSValue::encode(jsString(exec
, line
.data()));
309 EncodedJSValue JSC_HOST_CALL
functionQuit(ExecState
* exec
)
311 // Technically, destroying the heap in the middle of JS execution is a no-no,
312 // but we want to maintain compatibility with the Mozilla test suite, so
313 // we pretend that execution has terminated to avoid ASSERTs, then tear down the heap.
314 exec
->globalData().dynamicGlobalObject
= 0;
316 cleanupGlobalData(&exec
->globalData());
319 #if COMPILER(MSVC) && OS(WINCE)
320 // Without this, Visual Studio will complain that this method does not return a value.
321 return JSValue::encode(jsUndefined());
325 // Use SEH for Release builds only to get rid of the crash report dialog
326 // (luckily the same tests fail in Release and Debug builds so far). Need to
327 // be in a separate main function because the jscmain function requires object
330 #if COMPILER(MSVC) && !defined(_DEBUG) && !OS(WINCE)
332 #define EXCEPT(x) } __except (EXCEPTION_EXECUTE_HANDLER) { x; }
338 int jscmain(int argc
, char** argv
, JSGlobalData
*);
340 int main(int argc
, char** argv
)
344 // Cygwin calls ::SetErrorMode(SEM_FAILCRITICALERRORS), which we will inherit. This is bad for
345 // testing/debugging, as it causes the post-mortem debugger not to be invoked. We reset the
346 // error mode here to work around Cygwin's behavior. See <http://webkit.org/b/55222>.
351 _CrtSetReportFile(_CRT_WARN
, _CRTDBG_FILE_STDERR
);
352 _CrtSetReportMode(_CRT_WARN
, _CRTDBG_MODE_FILE
);
353 _CrtSetReportFile(_CRT_ERROR
, _CRTDBG_FILE_STDERR
);
354 _CrtSetReportMode(_CRT_ERROR
, _CRTDBG_MODE_FILE
);
355 _CrtSetReportFile(_CRT_ASSERT
, _CRTDBG_FILE_STDERR
);
356 _CrtSetReportMode(_CRT_ASSERT
, _CRTDBG_MODE_FILE
);
363 QCoreApplication
app(argc
, argv
);
366 // Initialize JSC before getting JSGlobalData.
367 JSC::initializeThreading();
369 // We can't use destructors in the following code because it uses Windows
370 // Structured Exception Handling
372 JSGlobalData
* globalData
= JSGlobalData::create(ThreadStackTypeLarge
).leakRef();
374 res
= jscmain(argc
, argv
, globalData
);
377 cleanupGlobalData(globalData
);
381 static void cleanupGlobalData(JSGlobalData
* globalData
)
383 JSLock
lock(SilenceAssertionsOnly
);
384 globalData
->clearBuiltinStructures();
385 globalData
->heap
.destroy();
389 static bool runWithScripts(GlobalObject
* globalObject
, const Vector
<Script
>& scripts
, bool dump
)
393 Vector
<char> scriptBuffer
;
396 BytecodeGenerator::setDumpsGeneratedCode(true);
398 JSGlobalData
& globalData
= globalObject
->globalData();
400 #if ENABLE(SAMPLING_FLAGS)
401 SamplingFlags::start();
405 for (size_t i
= 0; i
< scripts
.size(); i
++) {
406 if (scripts
[i
].isFile
) {
407 fileName
= scripts
[i
].argument
;
408 if (!fillBufferWithContentsOfFile(fileName
, scriptBuffer
))
409 return false; // fail early so we can catch missing files
410 script
= scriptBuffer
.data();
412 script
= scripts
[i
].argument
;
413 fileName
= "[Command Line]";
416 globalData
.startSampling();
418 Completion completion
= evaluate(globalObject
->globalExec(), globalObject
->globalScopeChain(), makeSource(script
, fileName
));
419 success
= success
&& completion
.complType() != Throw
;
421 if (completion
.complType() == Throw
)
422 printf("Exception: %s\n", completion
.value().toString(globalObject
->globalExec()).utf8().data());
424 printf("End: %s\n", completion
.value().toString(globalObject
->globalExec()).utf8().data());
427 globalData
.stopSampling();
428 globalObject
->globalExec()->clearException();
431 #if ENABLE(SAMPLING_FLAGS)
432 SamplingFlags::stop();
434 globalData
.dumpSampleData(globalObject
->globalExec());
435 #if ENABLE(SAMPLING_COUNTERS)
436 AbstractSamplingCounter::dump();
438 #if ENABLE(REGEXP_TRACING)
439 globalData
.dumpRegExpTrace();
444 #define RUNNING_FROM_XCODE 0
446 static void runInteractive(GlobalObject
* globalObject
)
449 #if HAVE(READLINE) && !RUNNING_FROM_XCODE
450 char* line
= readline(interactivePrompt
);
455 Completion completion
= evaluate(globalObject
->globalExec(), globalObject
->globalScopeChain(), makeSource(line
, interpreterName
));
458 printf("%s", interactivePrompt
);
459 Vector
<char, 256> line
;
461 while ((c
= getchar()) != EOF
) {
462 // FIXME: Should we also break on \r?
470 Completion completion
= evaluate(globalObject
->globalExec(), globalObject
->globalScopeChain(), makeSource(line
.data(), interpreterName
));
472 if (completion
.complType() == Throw
)
473 printf("Exception: %s\n", completion
.value().toString(globalObject
->globalExec()).utf8().data());
475 printf("%s\n", completion
.value().toString(globalObject
->globalExec()).utf8().data());
477 globalObject
->globalExec()->clearException();
482 static NO_RETURN
void printUsageStatement(JSGlobalData
* globalData
, bool help
= false)
484 fprintf(stderr
, "Usage: jsc [options] [files] [-- arguments]\n");
485 fprintf(stderr
, " -d Dumps bytecode (debug builds only)\n");
486 fprintf(stderr
, " -e Evaluate argument as script code\n");
487 fprintf(stderr
, " -f Specifies a source file (deprecated)\n");
488 fprintf(stderr
, " -h|--help Prints this help message\n");
489 fprintf(stderr
, " -i Enables interactive mode (default if no files are specified)\n");
491 fprintf(stderr
, " -s Installs signal handlers that exit on a crash (Unix platforms only)\n");
494 cleanupGlobalData(globalData
);
495 exit(help
? EXIT_SUCCESS
: EXIT_FAILURE
);
498 static void parseArguments(int argc
, char** argv
, Options
& options
, JSGlobalData
* globalData
)
501 for (; i
< argc
; ++i
) {
502 const char* arg
= argv
[i
];
503 if (!strcmp(arg
, "-f")) {
505 printUsageStatement(globalData
);
506 options
.scripts
.append(Script(true, argv
[i
]));
509 if (!strcmp(arg
, "-e")) {
511 printUsageStatement(globalData
);
512 options
.scripts
.append(Script(false, argv
[i
]));
515 if (!strcmp(arg
, "-i")) {
516 options
.interactive
= true;
519 if (!strcmp(arg
, "-d")) {
523 if (!strcmp(arg
, "-s")) {
525 signal(SIGILL
, _exit
);
526 signal(SIGFPE
, _exit
);
527 signal(SIGBUS
, _exit
);
528 signal(SIGSEGV
, _exit
);
532 if (!strcmp(arg
, "--")) {
536 if (!strcmp(arg
, "-h") || !strcmp(arg
, "--help"))
537 printUsageStatement(globalData
, true);
538 options
.scripts
.append(Script(true, argv
[i
]));
541 if (options
.scripts
.isEmpty())
542 options
.interactive
= true;
544 for (; i
< argc
; ++i
)
545 options
.arguments
.append(argv
[i
]);
548 int jscmain(int argc
, char** argv
, JSGlobalData
* globalData
)
550 JSLock
lock(SilenceAssertionsOnly
);
553 parseArguments(argc
, argv
, options
, globalData
);
555 GlobalObject
* globalObject
= new (globalData
) GlobalObject(*globalData
, GlobalObject::createStructure(*globalData
, jsNull()), options
.arguments
);
556 bool success
= runWithScripts(globalObject
, options
.scripts
, options
.dump
);
557 if (options
.interactive
&& success
)
558 runInteractive(globalObject
);
560 return success
? 0 : 3;
563 static bool fillBufferWithContentsOfFile(const UString
& fileName
, Vector
<char>& buffer
)
565 FILE* f
= fopen(fileName
.utf8().data(), "r");
567 fprintf(stderr
, "Could not open file: %s\n", fileName
.utf8().data());
571 size_t bufferSize
= 0;
572 size_t bufferCapacity
= 1024;
574 buffer
.resize(bufferCapacity
);
576 while (!feof(f
) && !ferror(f
)) {
577 bufferSize
+= fread(buffer
.data() + bufferSize
, 1, bufferCapacity
- bufferSize
, f
);
578 if (bufferSize
== bufferCapacity
) { // guarantees space for trailing '\0'
580 buffer
.resize(bufferCapacity
);
584 buffer
[bufferSize
] = '\0';
586 if (buffer
[0] == '#' && buffer
[1] == '!')
587 buffer
[0] = buffer
[1] = '/';