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 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().c_str()); 
 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().c_str()); 
 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().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().c_str()); 
 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().c_str(), "r"); 
 540         fprintf(stderr
, "Could not open file: %s\n", fileName
.UTF8String().c_str()); 
 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';