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 "InitializeThreading.h"
29 #include "JSFunction.h"
31 #include "PrototypeFunction.h"
32 #include "SamplingTool.h"
43 #include <readline/history.h>
44 #include <readline/readline.h>
55 #if COMPILER(MSVC) && !PLATFORM(WINCE)
62 #include <QCoreApplication>
69 static void cleanupGlobalData(JSGlobalData
*);
70 static bool fillBufferWithContentsOfFile(const UString
& fileName
, Vector
<char>& buffer
);
72 static JSValue JSC_HOST_CALL
functionPrint(ExecState
*, JSObject
*, JSValue
, const ArgList
&);
73 static JSValue JSC_HOST_CALL
functionDebug(ExecState
*, JSObject
*, JSValue
, const ArgList
&);
74 static JSValue JSC_HOST_CALL
functionGC(ExecState
*, JSObject
*, JSValue
, const ArgList
&);
75 static JSValue JSC_HOST_CALL
functionVersion(ExecState
*, JSObject
*, JSValue
, const ArgList
&);
76 static JSValue JSC_HOST_CALL
functionRun(ExecState
*, JSObject
*, JSValue
, const ArgList
&);
77 static JSValue JSC_HOST_CALL
functionLoad(ExecState
*, JSObject
*, JSValue
, const ArgList
&);
78 static JSValue JSC_HOST_CALL
functionCheckSyntax(ExecState
*, JSObject
*, JSValue
, const ArgList
&);
79 static JSValue JSC_HOST_CALL
functionReadline(ExecState
*, JSObject
*, JSValue
, const ArgList
&);
80 static NO_RETURN JSValue JSC_HOST_CALL
functionQuit(ExecState
*, JSObject
*, JSValue
, const ArgList
&);
82 #if ENABLE(SAMPLING_FLAGS)
83 static JSValue JSC_HOST_CALL
functionSetSamplingFlags(ExecState
*, JSObject
*, JSValue
, const ArgList
&);
84 static JSValue JSC_HOST_CALL
functionClearSamplingFlags(ExecState
*, JSObject
*, JSValue
, const ArgList
&);
91 Script(bool isFile
, char *argument
)
107 Vector
<Script
> scripts
;
108 Vector
<UString
> arguments
;
111 static const char interactivePrompt
[] = "> ";
112 static const UString
interpreterName("Interpreter");
118 long getElapsedMS(); // call stop() first
124 #elif PLATFORM(WIN_OS)
128 // Windows does not have timeval, disabling this class for now (bug 7399)
134 void StopWatch::start()
137 QDateTime t
= QDateTime::currentDateTime();
138 m_startTime
= t
.toTime_t() * 1000 + t
.time().msec();
139 #elif PLATFORM(WIN_OS)
140 m_startTime
= timeGetTime();
142 gettimeofday(&m_startTime
, 0);
146 void StopWatch::stop()
149 QDateTime t
= QDateTime::currentDateTime();
150 m_stopTime
= t
.toTime_t() * 1000 + t
.time().msec();
151 #elif PLATFORM(WIN_OS)
152 m_stopTime
= timeGetTime();
154 gettimeofday(&m_stopTime
, 0);
158 long StopWatch::getElapsedMS()
160 #if PLATFORM(WIN_OS) || PLATFORM(QT)
161 return m_stopTime
- m_startTime
;
164 timersub(&m_stopTime
, &m_startTime
, &elapsedTime
);
166 return elapsedTime
.tv_sec
* 1000 + lroundf(elapsedTime
.tv_usec
/ 1000.0f
);
170 class GlobalObject
: public JSGlobalObject
{
172 GlobalObject(const Vector
<UString
>& arguments
);
173 virtual UString
className() const { return "global"; }
175 COMPILE_ASSERT(!IsInteger
<GlobalObject
>::value
, WTF_IsInteger_GlobalObject_false
);
176 ASSERT_CLASS_FITS_IN_CELL(GlobalObject
);
178 GlobalObject::GlobalObject(const Vector
<UString
>& arguments
)
181 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "debug"), functionDebug
));
182 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "print"), functionPrint
));
183 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 0, Identifier(globalExec(), "quit"), functionQuit
));
184 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 0, Identifier(globalExec(), "gc"), functionGC
));
185 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "version"), functionVersion
));
186 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "run"), functionRun
));
187 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "load"), functionLoad
));
188 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "checkSyntax"), functionCheckSyntax
));
189 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 0, Identifier(globalExec(), "readline"), functionReadline
));
191 #if ENABLE(SAMPLING_FLAGS)
192 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "setSamplingFlags"), functionSetSamplingFlags
));
193 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "clearSamplingFlags"), functionClearSamplingFlags
));
196 JSObject
* array
= constructEmptyArray(globalExec());
197 for (size_t i
= 0; i
< arguments
.size(); ++i
)
198 array
->put(globalExec(), i
, jsString(globalExec(), arguments
[i
]));
199 putDirect(Identifier(globalExec(), "arguments"), array
);
202 JSValue JSC_HOST_CALL
functionPrint(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
& args
)
204 for (unsigned i
= 0; i
< args
.size(); ++i
) {
208 printf("%s", args
.at(i
).toString(exec
).UTF8String().c_str());
213 return jsUndefined();
216 JSValue JSC_HOST_CALL
functionDebug(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
& args
)
218 fprintf(stderr
, "--> %s\n", args
.at(0).toString(exec
).UTF8String().c_str());
219 return jsUndefined();
222 JSValue JSC_HOST_CALL
functionGC(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
&)
225 exec
->heap()->collect();
226 return jsUndefined();
229 JSValue JSC_HOST_CALL
functionVersion(ExecState
*, JSObject
*, JSValue
, const ArgList
&)
231 // We need this function for compatibility with the Mozilla JS tests but for now
232 // we don't actually do any version-specific handling
233 return jsUndefined();
236 JSValue JSC_HOST_CALL
functionRun(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
& args
)
239 UString fileName
= args
.at(0).toString(exec
);
241 if (!fillBufferWithContentsOfFile(fileName
, script
))
242 return throwError(exec
, GeneralError
, "Could not open file.");
244 JSGlobalObject
* globalObject
= exec
->lexicalGlobalObject();
247 evaluate(globalObject
->globalExec(), globalObject
->globalScopeChain(), makeSource(script
.data(), fileName
));
250 return jsNumber(globalObject
->globalExec(), stopWatch
.getElapsedMS());
253 JSValue JSC_HOST_CALL
functionLoad(ExecState
* exec
, JSObject
* o
, JSValue v
, const ArgList
& args
)
257 UString fileName
= args
.at(0).toString(exec
);
259 if (!fillBufferWithContentsOfFile(fileName
, script
))
260 return throwError(exec
, GeneralError
, "Could not open file.");
262 JSGlobalObject
* globalObject
= exec
->lexicalGlobalObject();
263 Completion result
= evaluate(globalObject
->globalExec(), globalObject
->globalScopeChain(), makeSource(script
.data(), fileName
));
264 if (result
.complType() == Throw
)
265 exec
->setException(result
.value());
266 return result
.value();
269 JSValue JSC_HOST_CALL
functionCheckSyntax(ExecState
* exec
, JSObject
* o
, JSValue v
, const ArgList
& args
)
273 UString fileName
= args
.at(0).toString(exec
);
275 if (!fillBufferWithContentsOfFile(fileName
, script
))
276 return throwError(exec
, GeneralError
, "Could not open file.");
278 JSGlobalObject
* globalObject
= exec
->lexicalGlobalObject();
279 Completion result
= checkSyntax(globalObject
->globalExec(), makeSource(script
.data(), fileName
));
280 if (result
.complType() == Throw
)
281 exec
->setException(result
.value());
282 return result
.value();
285 #if ENABLE(SAMPLING_FLAGS)
286 JSValue JSC_HOST_CALL
functionSetSamplingFlags(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
& args
)
288 for (unsigned i
= 0; i
< args
.size(); ++i
) {
289 unsigned flag
= static_cast<unsigned>(args
.at(i
).toNumber(exec
));
290 if ((flag
>= 1) && (flag
<= 32))
291 SamplingFlags::setFlag(flag
);
296 JSValue JSC_HOST_CALL
functionClearSamplingFlags(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
& args
)
298 for (unsigned i
= 0; i
< args
.size(); ++i
) {
299 unsigned flag
= static_cast<unsigned>(args
.at(i
).toNumber(exec
));
300 if ((flag
>= 1) && (flag
<= 32))
301 SamplingFlags::clearFlag(flag
);
307 JSValue JSC_HOST_CALL
functionReadline(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
&)
309 Vector
<char, 256> line
;
311 while ((c
= getchar()) != EOF
) {
312 // FIXME: Should we also break on \r?
318 return jsString(exec
, line
.data());
321 JSValue JSC_HOST_CALL
functionQuit(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
&)
323 cleanupGlobalData(&exec
->globalData());
327 // Use SEH for Release builds only to get rid of the crash report dialog
328 // (luckily the same tests fail in Release and Debug builds so far). Need to
329 // be in a separate main function because the jscmain function requires object
332 #if COMPILER(MSVC) && !defined(_DEBUG)
334 #define EXCEPT(x) } __except (EXCEPTION_EXECUTE_HANDLER) { x; }
340 int jscmain(int argc
, char** argv
, JSGlobalData
*);
342 int main(int argc
, char** argv
)
344 #if defined(_DEBUG) && PLATFORM(WIN_OS)
345 _CrtSetReportFile(_CRT_WARN
, _CRTDBG_FILE_STDERR
);
346 _CrtSetReportMode(_CRT_WARN
, _CRTDBG_MODE_FILE
);
347 _CrtSetReportFile(_CRT_ERROR
, _CRTDBG_FILE_STDERR
);
348 _CrtSetReportMode(_CRT_ERROR
, _CRTDBG_MODE_FILE
);
349 _CrtSetReportFile(_CRT_ASSERT
, _CRTDBG_FILE_STDERR
);
350 _CrtSetReportMode(_CRT_ASSERT
, _CRTDBG_MODE_FILE
);
353 #if COMPILER(MSVC) && !PLATFORM(WINCE)
358 QCoreApplication
app(argc
, argv
);
361 // Initialize JSC before getting JSGlobalData.
362 JSC::initializeThreading();
364 // We can't use destructors in the following code because it uses Windows
365 // Structured Exception Handling
367 JSGlobalData
* globalData
= JSGlobalData::create().releaseRef();
369 res
= jscmain(argc
, argv
, globalData
);
372 cleanupGlobalData(globalData
);
376 static void cleanupGlobalData(JSGlobalData
* globalData
)
379 globalData
->heap
.destroy();
383 static bool runWithScripts(GlobalObject
* globalObject
, const Vector
<Script
>& scripts
, bool dump
)
387 Vector
<char> scriptBuffer
;
390 BytecodeGenerator::setDumpsGeneratedCode(true);
392 #if ENABLE(OPCODE_SAMPLING)
393 Interpreter
* interpreter
= globalObject
->globalData()->interpreter
;
394 interpreter
->setSampler(new SamplingTool(interpreter
));
395 interpreter
->sampler()->setup();
397 #if ENABLE(SAMPLING_FLAGS)
398 SamplingFlags::start();
402 for (size_t i
= 0; i
< scripts
.size(); i
++) {
403 if (scripts
[i
].isFile
) {
404 fileName
= scripts
[i
].argument
;
405 if (!fillBufferWithContentsOfFile(fileName
, scriptBuffer
))
406 return false; // fail early so we can catch missing files
407 script
= scriptBuffer
.data();
409 script
= scripts
[i
].argument
;
410 fileName
= "[Command Line]";
413 #if ENABLE(SAMPLING_THREAD)
414 SamplingThread::start();
417 Completion completion
= evaluate(globalObject
->globalExec(), globalObject
->globalScopeChain(), makeSource(script
, fileName
));
418 success
= success
&& completion
.complType() != Throw
;
420 if (completion
.complType() == Throw
)
421 printf("Exception: %s\n", completion
.value().toString(globalObject
->globalExec()).ascii());
423 printf("End: %s\n", completion
.value().toString(globalObject
->globalExec()).ascii());
426 #if ENABLE(SAMPLING_THREAD)
427 SamplingThread::stop();
430 globalObject
->globalExec()->clearException();
433 #if ENABLE(SAMPLING_FLAGS)
434 SamplingFlags::stop();
436 #if ENABLE(OPCODE_SAMPLING)
437 interpreter
->sampler()->dump(globalObject
->globalExec());
438 delete interpreter
->sampler();
440 #if ENABLE(SAMPLING_COUNTERS)
441 AbstractSamplingCounter::dump();
446 #define RUNNING_FROM_XCODE 0
448 static void runInteractive(GlobalObject
* globalObject
)
451 #if HAVE(READLINE) && !RUNNING_FROM_XCODE
452 char* line
= readline(interactivePrompt
);
457 Completion completion
= evaluate(globalObject
->globalExec(), globalObject
->globalScopeChain(), makeSource(line
, interpreterName
));
460 printf("%s", interactivePrompt
);
461 Vector
<char, 256> line
;
463 while ((c
= getchar()) != EOF
) {
464 // FIXME: Should we also break on \r?
472 Completion completion
= evaluate(globalObject
->globalExec(), globalObject
->globalScopeChain(), makeSource(line
.data(), interpreterName
));
474 if (completion
.complType() == Throw
)
475 printf("Exception: %s\n", completion
.value().toString(globalObject
->globalExec()).ascii());
477 printf("%s\n", completion
.value().toString(globalObject
->globalExec()).UTF8String().c_str());
479 globalObject
->globalExec()->clearException();
484 static NO_RETURN
void printUsageStatement(JSGlobalData
* globalData
, bool help
= false)
486 fprintf(stderr
, "Usage: jsc [options] [files] [-- arguments]\n");
487 fprintf(stderr
, " -d Dumps bytecode (debug builds only)\n");
488 fprintf(stderr
, " -e Evaluate argument as script code\n");
489 fprintf(stderr
, " -f Specifies a source file (deprecated)\n");
490 fprintf(stderr
, " -h|--help Prints this help message\n");
491 fprintf(stderr
, " -i Enables interactive mode (default if no files are specified)\n");
493 fprintf(stderr
, " -s Installs signal handlers that exit on a crash (Unix platforms only)\n");
496 cleanupGlobalData(globalData
);
497 exit(help
? EXIT_SUCCESS
: EXIT_FAILURE
);
500 static void parseArguments(int argc
, char** argv
, Options
& options
, JSGlobalData
* globalData
)
503 for (; i
< argc
; ++i
) {
504 const char* arg
= argv
[i
];
505 if (strcmp(arg
, "-f") == 0) {
507 printUsageStatement(globalData
);
508 options
.scripts
.append(Script(true, argv
[i
]));
511 if (strcmp(arg
, "-e") == 0) {
513 printUsageStatement(globalData
);
514 options
.scripts
.append(Script(false, argv
[i
]));
517 if (strcmp(arg
, "-h") == 0 || strcmp(arg
, "--help") == 0) {
518 printUsageStatement(globalData
, true);
520 if (strcmp(arg
, "-i") == 0) {
521 options
.interactive
= true;
524 if (strcmp(arg
, "-d") == 0) {
528 if (strcmp(arg
, "-s") == 0) {
530 signal(SIGILL
, _exit
);
531 signal(SIGFPE
, _exit
);
532 signal(SIGBUS
, _exit
);
533 signal(SIGSEGV
, _exit
);
537 if (strcmp(arg
, "--") == 0) {
541 options
.scripts
.append(Script(true, argv
[i
]));
544 if (options
.scripts
.isEmpty())
545 options
.interactive
= true;
547 for (; i
< argc
; ++i
)
548 options
.arguments
.append(argv
[i
]);
551 int jscmain(int argc
, char** argv
, JSGlobalData
* globalData
)
556 parseArguments(argc
, argv
, options
, globalData
);
558 GlobalObject
* globalObject
= new (globalData
) GlobalObject(options
.arguments
);
559 bool success
= runWithScripts(globalObject
, options
.scripts
, options
.dump
);
560 if (options
.interactive
&& success
)
561 runInteractive(globalObject
);
563 return success
? 0 : 3;
566 static bool fillBufferWithContentsOfFile(const UString
& fileName
, Vector
<char>& buffer
)
568 FILE* f
= fopen(fileName
.UTF8String().c_str(), "r");
570 fprintf(stderr
, "Could not open file: %s\n", fileName
.UTF8String().c_str());
574 size_t buffer_size
= 0;
575 size_t buffer_capacity
= 1024;
577 buffer
.resize(buffer_capacity
);
579 while (!feof(f
) && !ferror(f
)) {
580 buffer_size
+= fread(buffer
.data() + buffer_size
, 1, buffer_capacity
- buffer_size
, f
);
581 if (buffer_size
== buffer_capacity
) { // guarantees space for trailing '\0'
582 buffer_capacity
*= 2;
583 buffer
.resize(buffer_capacity
);
587 buffer
[buffer_size
] = '\0';