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 "InitializeThreading.h"
30 #include "JSFunction.h"
33 #include "PrototypeFunction.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 JSValue JSC_HOST_CALL
functionPrint(ExecState
*, JSObject
*, JSValue
, const ArgList
&);
75 static JSValue JSC_HOST_CALL
functionDebug(ExecState
*, JSObject
*, JSValue
, const ArgList
&);
76 static JSValue JSC_HOST_CALL
functionGC(ExecState
*, JSObject
*, JSValue
, const ArgList
&);
77 static JSValue JSC_HOST_CALL
functionVersion(ExecState
*, JSObject
*, JSValue
, const ArgList
&);
78 static JSValue JSC_HOST_CALL
functionRun(ExecState
*, JSObject
*, JSValue
, const ArgList
&);
79 static JSValue JSC_HOST_CALL
functionLoad(ExecState
*, JSObject
*, JSValue
, const ArgList
&);
80 static JSValue JSC_HOST_CALL
functionCheckSyntax(ExecState
*, JSObject
*, JSValue
, const ArgList
&);
81 static JSValue JSC_HOST_CALL
functionReadline(ExecState
*, JSObject
*, JSValue
, const ArgList
&);
82 static NO_RETURN_WITH_VALUE JSValue JSC_HOST_CALL
functionQuit(ExecState
*, JSObject
*, JSValue
, const ArgList
&);
84 #if ENABLE(SAMPLING_FLAGS)
85 static JSValue JSC_HOST_CALL
functionSetSamplingFlags(ExecState
*, JSObject
*, JSValue
, const ArgList
&);
86 static JSValue JSC_HOST_CALL
functionClearSamplingFlags(ExecState
*, JSObject
*, JSValue
, const ArgList
&);
93 Script(bool isFile
, char *argument
)
109 Vector
<Script
> scripts
;
110 Vector
<UString
> arguments
;
113 static const char interactivePrompt
[] = "> ";
114 static const UString
interpreterName("Interpreter");
120 long getElapsedMS(); // call stop() first
127 void StopWatch::start()
129 m_startTime
= currentTime();
132 void StopWatch::stop()
134 m_stopTime
= currentTime();
137 long StopWatch::getElapsedMS()
139 return static_cast<long>((m_stopTime
- m_startTime
) * 1000);
142 class GlobalObject
: public JSGlobalObject
{
144 GlobalObject(const Vector
<UString
>& arguments
);
145 virtual UString
className() const { return "global"; }
147 COMPILE_ASSERT(!IsInteger
<GlobalObject
>::value
, WTF_IsInteger_GlobalObject_false
);
148 ASSERT_CLASS_FITS_IN_CELL(GlobalObject
);
150 GlobalObject::GlobalObject(const Vector
<UString
>& arguments
)
153 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "debug"), functionDebug
));
154 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "print"), functionPrint
));
155 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 0, Identifier(globalExec(), "quit"), functionQuit
));
156 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 0, Identifier(globalExec(), "gc"), functionGC
));
157 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "version"), functionVersion
));
158 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "run"), functionRun
));
159 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "load"), functionLoad
));
160 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "checkSyntax"), functionCheckSyntax
));
161 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 0, Identifier(globalExec(), "readline"), functionReadline
));
163 #if ENABLE(SAMPLING_FLAGS)
164 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "setSamplingFlags"), functionSetSamplingFlags
));
165 putDirectFunction(globalExec(), new (globalExec()) NativeFunctionWrapper(globalExec(), prototypeFunctionStructure(), 1, Identifier(globalExec(), "clearSamplingFlags"), functionClearSamplingFlags
));
168 JSObject
* array
= constructEmptyArray(globalExec());
169 for (size_t i
= 0; i
< arguments
.size(); ++i
)
170 array
->put(globalExec(), i
, jsString(globalExec(), arguments
[i
]));
171 putDirect(Identifier(globalExec(), "arguments"), array
);
174 JSValue JSC_HOST_CALL
functionPrint(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
& args
)
176 for (unsigned i
= 0; i
< args
.size(); ++i
) {
180 printf("%s", args
.at(i
).toString(exec
).UTF8String().data());
185 return jsUndefined();
188 JSValue JSC_HOST_CALL
functionDebug(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
& args
)
190 fprintf(stderr
, "--> %s\n", args
.at(0).toString(exec
).UTF8String().data());
191 return jsUndefined();
194 JSValue JSC_HOST_CALL
functionGC(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
&)
196 JSLock
lock(SilenceAssertionsOnly
);
197 exec
->heap()->collectAllGarbage();
198 return jsUndefined();
201 JSValue JSC_HOST_CALL
functionVersion(ExecState
*, JSObject
*, JSValue
, const ArgList
&)
203 // We need this function for compatibility with the Mozilla JS tests but for now
204 // we don't actually do any version-specific handling
205 return jsUndefined();
208 JSValue JSC_HOST_CALL
functionRun(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
& args
)
211 UString fileName
= args
.at(0).toString(exec
);
213 if (!fillBufferWithContentsOfFile(fileName
, script
))
214 return throwError(exec
, GeneralError
, "Could not open file.");
216 JSGlobalObject
* globalObject
= exec
->lexicalGlobalObject();
219 evaluate(globalObject
->globalExec(), globalObject
->globalScopeChain(), makeSource(script
.data(), fileName
));
222 return jsNumber(globalObject
->globalExec(), stopWatch
.getElapsedMS());
225 JSValue JSC_HOST_CALL
functionLoad(ExecState
* exec
, JSObject
* o
, JSValue v
, const ArgList
& args
)
229 UString fileName
= args
.at(0).toString(exec
);
231 if (!fillBufferWithContentsOfFile(fileName
, script
))
232 return throwError(exec
, GeneralError
, "Could not open file.");
234 JSGlobalObject
* globalObject
= exec
->lexicalGlobalObject();
235 Completion result
= evaluate(globalObject
->globalExec(), globalObject
->globalScopeChain(), makeSource(script
.data(), fileName
));
236 if (result
.complType() == Throw
)
237 exec
->setException(result
.value());
238 return result
.value();
241 JSValue JSC_HOST_CALL
functionCheckSyntax(ExecState
* exec
, JSObject
* o
, JSValue v
, const ArgList
& args
)
245 UString fileName
= args
.at(0).toString(exec
);
247 if (!fillBufferWithContentsOfFile(fileName
, script
))
248 return throwError(exec
, GeneralError
, "Could not open file.");
250 JSGlobalObject
* globalObject
= exec
->lexicalGlobalObject();
251 Completion result
= checkSyntax(globalObject
->globalExec(), makeSource(script
.data(), fileName
));
252 if (result
.complType() == Throw
)
253 exec
->setException(result
.value());
254 return result
.value();
257 #if ENABLE(SAMPLING_FLAGS)
258 JSValue JSC_HOST_CALL
functionSetSamplingFlags(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
& args
)
260 for (unsigned i
= 0; i
< args
.size(); ++i
) {
261 unsigned flag
= static_cast<unsigned>(args
.at(i
).toNumber(exec
));
262 if ((flag
>= 1) && (flag
<= 32))
263 SamplingFlags::setFlag(flag
);
268 JSValue JSC_HOST_CALL
functionClearSamplingFlags(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
& args
)
270 for (unsigned i
= 0; i
< args
.size(); ++i
) {
271 unsigned flag
= static_cast<unsigned>(args
.at(i
).toNumber(exec
));
272 if ((flag
>= 1) && (flag
<= 32))
273 SamplingFlags::clearFlag(flag
);
279 JSValue JSC_HOST_CALL
functionReadline(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
&)
281 Vector
<char, 256> line
;
283 while ((c
= getchar()) != EOF
) {
284 // FIXME: Should we also break on \r?
290 return jsString(exec
, line
.data());
293 JSValue JSC_HOST_CALL
functionQuit(ExecState
* exec
, JSObject
*, JSValue
, const ArgList
&)
295 // Technically, destroying the heap in the middle of JS execution is a no-no,
296 // but we want to maintain compatibility with the Mozilla test suite, so
297 // we pretend that execution has terminated to avoid ASSERTs, then tear down the heap.
298 exec
->globalData().dynamicGlobalObject
= 0;
300 cleanupGlobalData(&exec
->globalData());
303 #if COMPILER(MSVC) && OS(WINCE)
304 // Without this, Visual Studio will complain that this method does not return a value.
305 return jsUndefined();
309 // Use SEH for Release builds only to get rid of the crash report dialog
310 // (luckily the same tests fail in Release and Debug builds so far). Need to
311 // be in a separate main function because the jscmain function requires object
314 #if COMPILER(MSVC) && !defined(_DEBUG)
316 #define EXCEPT(x) } __except (EXCEPTION_EXECUTE_HANDLER) { x; }
322 int jscmain(int argc
, char** argv
, JSGlobalData
*);
324 int main(int argc
, char** argv
)
326 #if defined(_DEBUG) && OS(WINDOWS)
327 _CrtSetReportFile(_CRT_WARN
, _CRTDBG_FILE_STDERR
);
328 _CrtSetReportMode(_CRT_WARN
, _CRTDBG_MODE_FILE
);
329 _CrtSetReportFile(_CRT_ERROR
, _CRTDBG_FILE_STDERR
);
330 _CrtSetReportMode(_CRT_ERROR
, _CRTDBG_MODE_FILE
);
331 _CrtSetReportFile(_CRT_ASSERT
, _CRTDBG_FILE_STDERR
);
332 _CrtSetReportMode(_CRT_ASSERT
, _CRTDBG_MODE_FILE
);
335 #if COMPILER(MSVC) && !OS(WINCE)
340 QCoreApplication
app(argc
, argv
);
343 // Initialize JSC before getting JSGlobalData.
344 JSC::initializeThreading();
346 // We can't use destructors in the following code because it uses Windows
347 // Structured Exception Handling
349 JSGlobalData
* globalData
= JSGlobalData::create(ThreadStackTypeLarge
).releaseRef();
351 res
= jscmain(argc
, argv
, globalData
);
354 cleanupGlobalData(globalData
);
358 static void cleanupGlobalData(JSGlobalData
* globalData
)
360 JSLock
lock(SilenceAssertionsOnly
);
361 globalData
->heap
.destroy();
365 static bool runWithScripts(GlobalObject
* globalObject
, const Vector
<Script
>& scripts
, bool dump
)
369 Vector
<char> scriptBuffer
;
372 BytecodeGenerator::setDumpsGeneratedCode(true);
374 JSGlobalData
* globalData
= globalObject
->globalData();
376 #if ENABLE(SAMPLING_FLAGS)
377 SamplingFlags::start();
381 for (size_t i
= 0; i
< scripts
.size(); i
++) {
382 if (scripts
[i
].isFile
) {
383 fileName
= scripts
[i
].argument
;
384 if (!fillBufferWithContentsOfFile(fileName
, scriptBuffer
))
385 return false; // fail early so we can catch missing files
386 script
= scriptBuffer
.data();
388 script
= scripts
[i
].argument
;
389 fileName
= "[Command Line]";
392 globalData
->startSampling();
394 Completion completion
= evaluate(globalObject
->globalExec(), globalObject
->globalScopeChain(), makeSource(script
, fileName
));
395 success
= success
&& completion
.complType() != Throw
;
397 if (completion
.complType() == Throw
)
398 printf("Exception: %s\n", completion
.value().toString(globalObject
->globalExec()).ascii());
400 printf("End: %s\n", completion
.value().toString(globalObject
->globalExec()).ascii());
403 globalData
->stopSampling();
404 globalObject
->globalExec()->clearException();
407 #if ENABLE(SAMPLING_FLAGS)
408 SamplingFlags::stop();
410 globalData
->dumpSampleData(globalObject
->globalExec());
411 #if ENABLE(SAMPLING_COUNTERS)
412 AbstractSamplingCounter::dump();
417 #define RUNNING_FROM_XCODE 0
419 static void runInteractive(GlobalObject
* globalObject
)
422 #if HAVE(READLINE) && !RUNNING_FROM_XCODE
423 char* line
= readline(interactivePrompt
);
428 Completion completion
= evaluate(globalObject
->globalExec(), globalObject
->globalScopeChain(), makeSource(line
, interpreterName
));
431 printf("%s", interactivePrompt
);
432 Vector
<char, 256> line
;
434 while ((c
= getchar()) != EOF
) {
435 // FIXME: Should we also break on \r?
443 Completion completion
= evaluate(globalObject
->globalExec(), globalObject
->globalScopeChain(), makeSource(line
.data(), interpreterName
));
445 if (completion
.complType() == Throw
)
446 printf("Exception: %s\n", completion
.value().toString(globalObject
->globalExec()).ascii());
448 printf("%s\n", completion
.value().toString(globalObject
->globalExec()).UTF8String().data());
450 globalObject
->globalExec()->clearException();
455 static NO_RETURN
void printUsageStatement(JSGlobalData
* globalData
, bool help
= false)
457 fprintf(stderr
, "Usage: jsc [options] [files] [-- arguments]\n");
458 fprintf(stderr
, " -d Dumps bytecode (debug builds only)\n");
459 fprintf(stderr
, " -e Evaluate argument as script code\n");
460 fprintf(stderr
, " -f Specifies a source file (deprecated)\n");
461 fprintf(stderr
, " -h|--help Prints this help message\n");
462 fprintf(stderr
, " -i Enables interactive mode (default if no files are specified)\n");
464 fprintf(stderr
, " -s Installs signal handlers that exit on a crash (Unix platforms only)\n");
467 cleanupGlobalData(globalData
);
468 exit(help
? EXIT_SUCCESS
: EXIT_FAILURE
);
471 static void parseArguments(int argc
, char** argv
, Options
& options
, JSGlobalData
* globalData
)
474 for (; i
< argc
; ++i
) {
475 const char* arg
= argv
[i
];
476 if (!strcmp(arg
, "-f")) {
478 printUsageStatement(globalData
);
479 options
.scripts
.append(Script(true, argv
[i
]));
482 if (!strcmp(arg
, "-e")) {
484 printUsageStatement(globalData
);
485 options
.scripts
.append(Script(false, argv
[i
]));
488 if (!strcmp(arg
, "-i")) {
489 options
.interactive
= true;
492 if (!strcmp(arg
, "-d")) {
496 if (!strcmp(arg
, "-s")) {
498 signal(SIGILL
, _exit
);
499 signal(SIGFPE
, _exit
);
500 signal(SIGBUS
, _exit
);
501 signal(SIGSEGV
, _exit
);
505 if (!strcmp(arg
, "--")) {
509 if (!strcmp(arg
, "-h") || !strcmp(arg
, "--help"))
510 printUsageStatement(globalData
, true);
511 options
.scripts
.append(Script(true, argv
[i
]));
514 if (options
.scripts
.isEmpty())
515 options
.interactive
= true;
517 for (; i
< argc
; ++i
)
518 options
.arguments
.append(argv
[i
]);
521 int jscmain(int argc
, char** argv
, JSGlobalData
* globalData
)
523 JSLock
lock(SilenceAssertionsOnly
);
526 parseArguments(argc
, argv
, options
, globalData
);
528 GlobalObject
* globalObject
= new (globalData
) GlobalObject(options
.arguments
);
529 bool success
= runWithScripts(globalObject
, options
.scripts
, options
.dump
);
530 if (options
.interactive
&& success
)
531 runInteractive(globalObject
);
533 return success
? 0 : 3;
536 static bool fillBufferWithContentsOfFile(const UString
& fileName
, Vector
<char>& buffer
)
538 FILE* f
= fopen(fileName
.UTF8String().data(), "r");
540 fprintf(stderr
, "Could not open file: %s\n", fileName
.UTF8String().data());
544 size_t bufferSize
= 0;
545 size_t bufferCapacity
= 1024;
547 buffer
.resize(bufferCapacity
);
549 while (!feof(f
) && !ferror(f
)) {
550 bufferSize
+= fread(buffer
.data() + bufferSize
, 1, bufferCapacity
- bufferSize
, f
);
551 if (bufferSize
== bufferCapacity
) { // guarantees space for trailing '\0'
553 buffer
.resize(bufferCapacity
);
557 buffer
[bufferSize
] = '\0';