1/* 2 * Copyright (C) 1999-2000 Harri Porten (porten@kde.org) 3 * Copyright (C) 2004, 2005, 2006, 2007, 2008, 2012 Apple Inc. All rights reserved. 4 * Copyright (C) 2006 Bjoern Graf (bjoern.graf@gmail.com) 5 * 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. 10 * 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. 15 * 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. 20 * 21 */ 22 23#include "config.h" 24 25#include "APIShims.h" 26#include "ButterflyInlines.h" 27#include "BytecodeGenerator.h" 28#include "Completion.h" 29#include "CopiedSpaceInlines.h" 30#include "ExceptionHelpers.h" 31#include "HeapStatistics.h" 32#include "InitializeThreading.h" 33#include "Interpreter.h" 34#include "JSArray.h" 35#include "JSCTypedArrayStubs.h" 36#include "JSFunction.h" 37#include "JSLock.h" 38#include "JSProxy.h" 39#include "JSString.h" 40#include "Operations.h" 41#include "SamplingTool.h" 42#include "StructureRareDataInlines.h" 43#include <math.h> 44#include <stdio.h> 45#include <stdlib.h> 46#include <string.h> 47#include <wtf/CurrentTime.h> 48#include <wtf/MainThread.h> 49#include <wtf/StringPrintStream.h> 50#include <wtf/text/StringBuilder.h> 51 52#if !OS(WINDOWS) 53#include <unistd.h> 54#endif 55 56#if HAVE(READLINE) 57// readline/history.h has a Function typedef which conflicts with the WTF::Function template from WTF/Forward.h 58// We #define it to something else to avoid this conflict. 59#define Function ReadlineFunction 60#include <readline/history.h> 61#include <readline/readline.h> 62#undef Function 63#endif 64 65#if HAVE(SYS_TIME_H) 66#include <sys/time.h> 67#endif 68 69#if HAVE(SIGNAL_H) 70#include <signal.h> 71#endif 72 73#if COMPILER(MSVC) && !OS(WINCE) 74#include <crtdbg.h> 75#include <mmsystem.h> 76#include <windows.h> 77#endif 78 79#if PLATFORM(QT) 80#include <QCoreApplication> 81#include <QDateTime> 82#endif 83 84#if PLATFORM(IOS) 85#include <fenv.h> 86#include <arm/arch.h> 87#endif 88 89#if PLATFORM(BLACKBERRY) 90#include <BlackBerryPlatformLog.h> 91#endif 92 93#if PLATFORM(EFL) 94#include <Ecore.h> 95#endif 96 97using namespace JSC; 98using namespace WTF; 99 100static bool fillBufferWithContentsOfFile(const String& fileName, Vector<char>& buffer); 101 102static EncodedJSValue JSC_HOST_CALL functionPrint(ExecState*); 103static EncodedJSValue JSC_HOST_CALL functionDebug(ExecState*); 104static EncodedJSValue JSC_HOST_CALL functionDescribe(ExecState*); 105static EncodedJSValue JSC_HOST_CALL functionJSCStack(ExecState*); 106static EncodedJSValue JSC_HOST_CALL functionGC(ExecState*); 107#ifndef NDEBUG 108static EncodedJSValue JSC_HOST_CALL functionReleaseExecutableMemory(ExecState*); 109static EncodedJSValue JSC_HOST_CALL functionDumpCallFrame(ExecState*); 110#endif 111static EncodedJSValue JSC_HOST_CALL functionVersion(ExecState*); 112static EncodedJSValue JSC_HOST_CALL functionRun(ExecState*); 113static EncodedJSValue JSC_HOST_CALL functionLoad(ExecState*); 114static EncodedJSValue JSC_HOST_CALL functionCheckSyntax(ExecState*); 115static EncodedJSValue JSC_HOST_CALL functionReadline(ExecState*); 116static EncodedJSValue JSC_HOST_CALL functionPreciseTime(ExecState*); 117static NO_RETURN_WITH_VALUE EncodedJSValue JSC_HOST_CALL functionQuit(ExecState*); 118 119#if ENABLE(SAMPLING_FLAGS) 120static EncodedJSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState*); 121static EncodedJSValue JSC_HOST_CALL functionClearSamplingFlags(ExecState*); 122#endif 123 124struct Script { 125 bool isFile; 126 char* argument; 127 128 Script(bool isFile, char *argument) 129 : isFile(isFile) 130 , argument(argument) 131 { 132 } 133}; 134 135class CommandLine { 136public: 137 CommandLine(int argc, char** argv) 138 : m_interactive(false) 139 , m_dump(false) 140 , m_exitCode(false) 141 , m_profile(false) 142 { 143 parseArguments(argc, argv); 144 } 145 146 bool m_interactive; 147 bool m_dump; 148 bool m_exitCode; 149 Vector<Script> m_scripts; 150 Vector<String> m_arguments; 151 bool m_profile; 152 String m_profilerOutput; 153 154 void parseArguments(int, char**); 155}; 156 157static const char interactivePrompt[] = ">>> "; 158 159class StopWatch { 160public: 161 void start(); 162 void stop(); 163 long getElapsedMS(); // call stop() first 164 165private: 166 double m_startTime; 167 double m_stopTime; 168}; 169 170void StopWatch::start() 171{ 172 m_startTime = currentTime(); 173} 174 175void StopWatch::stop() 176{ 177 m_stopTime = currentTime(); 178} 179 180long StopWatch::getElapsedMS() 181{ 182 return static_cast<long>((m_stopTime - m_startTime) * 1000); 183} 184 185class GlobalObject : public JSGlobalObject { 186private: 187 GlobalObject(VM&, Structure*); 188 189public: 190 typedef JSGlobalObject Base; 191 192 static GlobalObject* create(VM& vm, Structure* structure, const Vector<String>& arguments) 193 { 194 GlobalObject* object = new (NotNull, allocateCell<GlobalObject>(vm.heap)) GlobalObject(vm, structure); 195 object->finishCreation(vm, arguments); 196 vm.heap.addFinalizer(object, destroy); 197 object->setGlobalThis(vm, JSProxy::create(vm, JSProxy::createStructure(vm, object, object->prototype()), object)); 198 return object; 199 } 200 201 static const bool needsDestruction = false; 202 203 static const ClassInfo s_info; 204 static const GlobalObjectMethodTable s_globalObjectMethodTable; 205 206 static Structure* createStructure(VM& vm, JSValue prototype) 207 { 208 return Structure::create(vm, 0, prototype, TypeInfo(GlobalObjectType, StructureFlags), &s_info); 209 } 210 211 static bool javaScriptExperimentsEnabled(const JSGlobalObject*) { return true; } 212 213protected: 214 void finishCreation(VM& vm, const Vector<String>& arguments) 215 { 216 Base::finishCreation(vm); 217 218 addFunction(vm, "debug", functionDebug, 1); 219 addFunction(vm, "describe", functionDescribe, 1); 220 addFunction(vm, "print", functionPrint, 1); 221 addFunction(vm, "quit", functionQuit, 0); 222 addFunction(vm, "gc", functionGC, 0); 223#ifndef NDEBUG 224 addFunction(vm, "dumpCallFrame", functionDumpCallFrame, 0); 225 addFunction(vm, "releaseExecutableMemory", functionReleaseExecutableMemory, 0); 226#endif 227 addFunction(vm, "version", functionVersion, 1); 228 addFunction(vm, "run", functionRun, 1); 229 addFunction(vm, "load", functionLoad, 1); 230 addFunction(vm, "checkSyntax", functionCheckSyntax, 1); 231 addFunction(vm, "jscStack", functionJSCStack, 1); 232 addFunction(vm, "readline", functionReadline, 0); 233 addFunction(vm, "preciseTime", functionPreciseTime, 0); 234#if ENABLE(SAMPLING_FLAGS) 235 addFunction(vm, "setSamplingFlags", functionSetSamplingFlags, 1); 236 addFunction(vm, "clearSamplingFlags", functionClearSamplingFlags, 1); 237#endif 238 239 addConstructableFunction(vm, "Uint8Array", constructJSUint8Array, 1); 240 addConstructableFunction(vm, "Uint8ClampedArray", constructJSUint8ClampedArray, 1); 241 addConstructableFunction(vm, "Uint16Array", constructJSUint16Array, 1); 242 addConstructableFunction(vm, "Uint32Array", constructJSUint32Array, 1); 243 addConstructableFunction(vm, "Int8Array", constructJSInt8Array, 1); 244 addConstructableFunction(vm, "Int16Array", constructJSInt16Array, 1); 245 addConstructableFunction(vm, "Int32Array", constructJSInt32Array, 1); 246 addConstructableFunction(vm, "Float32Array", constructJSFloat32Array, 1); 247 addConstructableFunction(vm, "Float64Array", constructJSFloat64Array, 1); 248 249 JSArray* array = constructEmptyArray(globalExec(), 0); 250 for (size_t i = 0; i < arguments.size(); ++i) 251 array->putDirectIndex(globalExec(), i, jsString(globalExec(), arguments[i])); 252 putDirect(vm, Identifier(globalExec(), "arguments"), array); 253 } 254 255 void addFunction(VM& vm, const char* name, NativeFunction function, unsigned arguments) 256 { 257 Identifier identifier(globalExec(), name); 258 putDirect(vm, identifier, JSFunction::create(globalExec(), this, arguments, identifier.string(), function)); 259 } 260 261 void addConstructableFunction(VM& vm, const char* name, NativeFunction function, unsigned arguments) 262 { 263 Identifier identifier(globalExec(), name); 264 putDirect(vm, identifier, JSFunction::create(globalExec(), this, arguments, identifier.string(), function, NoIntrinsic, function)); 265 } 266}; 267 268COMPILE_ASSERT(!IsInteger<GlobalObject>::value, WTF_IsInteger_GlobalObject_false); 269 270const ClassInfo GlobalObject::s_info = { "global", &JSGlobalObject::s_info, 0, ExecState::globalObjectTable, CREATE_METHOD_TABLE(GlobalObject) }; 271const GlobalObjectMethodTable GlobalObject::s_globalObjectMethodTable = { &allowsAccessFrom, &supportsProfiling, &supportsRichSourceInfo, &shouldInterruptScript, &javaScriptExperimentsEnabled }; 272 273 274GlobalObject::GlobalObject(VM& vm, Structure* structure) 275 : JSGlobalObject(vm, structure, &s_globalObjectMethodTable) 276{ 277} 278 279static inline String stringFromUTF(const char* utf8) 280{ 281 // Find the the first non-ascii character, or nul. 282 const char* pos = utf8; 283 while (*pos > 0) 284 pos++; 285 size_t asciiLength = pos - utf8; 286 287 // Fast case - string is all ascii. 288 if (!*pos) 289 return String(utf8, asciiLength); 290 291 // Slow case - contains non-ascii characters, use fromUTF8WithLatin1Fallback. 292 ASSERT(*pos < 0); 293 ASSERT(strlen(utf8) == asciiLength + strlen(pos)); 294 return String::fromUTF8WithLatin1Fallback(utf8, asciiLength + strlen(pos)); 295} 296 297static inline SourceCode jscSource(const char* utf8, const String& filename) 298{ 299 String str = stringFromUTF(utf8); 300 return makeSource(str, filename); 301} 302 303EncodedJSValue JSC_HOST_CALL functionPrint(ExecState* exec) 304{ 305 for (unsigned i = 0; i < exec->argumentCount(); ++i) { 306 if (i) 307 putchar(' '); 308 309 printf("%s", exec->argument(i).toString(exec)->value(exec).utf8().data()); 310 } 311 312 putchar('\n'); 313 fflush(stdout); 314 return JSValue::encode(jsUndefined()); 315} 316 317#ifndef NDEBUG 318EncodedJSValue JSC_HOST_CALL functionDumpCallFrame(ExecState* exec) 319{ 320 if (!exec->callerFrame()->hasHostCallFrameFlag()) 321 exec->vm().interpreter->dumpCallFrame(exec->callerFrame()); 322 return JSValue::encode(jsUndefined()); 323} 324#endif 325 326EncodedJSValue JSC_HOST_CALL functionDebug(ExecState* exec) 327{ 328 fprintf(stderr, "--> %s\n", exec->argument(0).toString(exec)->value(exec).utf8().data()); 329 return JSValue::encode(jsUndefined()); 330} 331 332EncodedJSValue JSC_HOST_CALL functionDescribe(ExecState* exec) 333{ 334 fprintf(stderr, "--> %s\n", toCString(exec->argument(0)).data()); 335 return JSValue::encode(jsUndefined()); 336} 337 338EncodedJSValue JSC_HOST_CALL functionJSCStack(ExecState* exec) 339{ 340 StringBuilder trace; 341 trace.appendLiteral("--> Stack trace:\n"); 342 343 Vector<StackFrame> stackTrace; 344 Interpreter::getStackTrace(&exec->vm(), stackTrace); 345 int i = 0; 346 347 for (Vector<StackFrame>::iterator iter = stackTrace.begin(); iter < stackTrace.end(); iter++) { 348 StackFrame level = *iter; 349 trace.append(String::format(" %i %s\n", i, level.toString(exec).utf8().data())); 350 i++; 351 } 352 fprintf(stderr, "%s", trace.toString().utf8().data()); 353 return JSValue::encode(jsUndefined()); 354} 355 356EncodedJSValue JSC_HOST_CALL functionGC(ExecState* exec) 357{ 358 JSLockHolder lock(exec); 359 exec->heap()->collectAllGarbage(); 360 return JSValue::encode(jsUndefined()); 361} 362 363#ifndef NDEBUG 364EncodedJSValue JSC_HOST_CALL functionReleaseExecutableMemory(ExecState* exec) 365{ 366 JSLockHolder lock(exec); 367 exec->vm().releaseExecutableMemory(); 368 return JSValue::encode(jsUndefined()); 369} 370#endif 371 372EncodedJSValue JSC_HOST_CALL functionVersion(ExecState*) 373{ 374 // We need this function for compatibility with the Mozilla JS tests but for now 375 // we don't actually do any version-specific handling 376 return JSValue::encode(jsUndefined()); 377} 378 379EncodedJSValue JSC_HOST_CALL functionRun(ExecState* exec) 380{ 381 String fileName = exec->argument(0).toString(exec)->value(exec); 382 Vector<char> script; 383 if (!fillBufferWithContentsOfFile(fileName, script)) 384 return JSValue::encode(throwError(exec, createError(exec, "Could not open file."))); 385 386 GlobalObject* globalObject = GlobalObject::create(exec->vm(), GlobalObject::createStructure(exec->vm(), jsNull()), Vector<String>()); 387 388 JSValue exception; 389 StopWatch stopWatch; 390 stopWatch.start(); 391 evaluate(globalObject->globalExec(), jscSource(script.data(), fileName), JSValue(), &exception); 392 stopWatch.stop(); 393 394 if (!!exception) { 395 throwError(globalObject->globalExec(), exception); 396 return JSValue::encode(jsUndefined()); 397 } 398 399 return JSValue::encode(jsNumber(stopWatch.getElapsedMS())); 400} 401 402EncodedJSValue JSC_HOST_CALL functionLoad(ExecState* exec) 403{ 404 String fileName = exec->argument(0).toString(exec)->value(exec); 405 Vector<char> script; 406 if (!fillBufferWithContentsOfFile(fileName, script)) 407 return JSValue::encode(throwError(exec, createError(exec, "Could not open file."))); 408 409 JSGlobalObject* globalObject = exec->lexicalGlobalObject(); 410 411 JSValue evaluationException; 412 JSValue result = evaluate(globalObject->globalExec(), jscSource(script.data(), fileName), JSValue(), &evaluationException); 413 if (evaluationException) 414 throwError(exec, evaluationException); 415 return JSValue::encode(result); 416} 417 418EncodedJSValue JSC_HOST_CALL functionCheckSyntax(ExecState* exec) 419{ 420 String fileName = exec->argument(0).toString(exec)->value(exec); 421 Vector<char> script; 422 if (!fillBufferWithContentsOfFile(fileName, script)) 423 return JSValue::encode(throwError(exec, createError(exec, "Could not open file."))); 424 425 JSGlobalObject* globalObject = exec->lexicalGlobalObject(); 426 427 StopWatch stopWatch; 428 stopWatch.start(); 429 430 JSValue syntaxException; 431 bool validSyntax = checkSyntax(globalObject->globalExec(), jscSource(script.data(), fileName), &syntaxException); 432 stopWatch.stop(); 433 434 if (!validSyntax) 435 throwError(exec, syntaxException); 436 return JSValue::encode(jsNumber(stopWatch.getElapsedMS())); 437} 438 439#if ENABLE(SAMPLING_FLAGS) 440EncodedJSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState* exec) 441{ 442 for (unsigned i = 0; i < exec->argumentCount(); ++i) { 443 unsigned flag = static_cast<unsigned>(exec->argument(i).toNumber(exec)); 444 if ((flag >= 1) && (flag <= 32)) 445 SamplingFlags::setFlag(flag); 446 } 447 return JSValue::encode(jsNull()); 448} 449 450EncodedJSValue JSC_HOST_CALL functionClearSamplingFlags(ExecState* exec) 451{ 452 for (unsigned i = 0; i < exec->argumentCount(); ++i) { 453 unsigned flag = static_cast<unsigned>(exec->argument(i).toNumber(exec)); 454 if ((flag >= 1) && (flag <= 32)) 455 SamplingFlags::clearFlag(flag); 456 } 457 return JSValue::encode(jsNull()); 458} 459#endif 460 461EncodedJSValue JSC_HOST_CALL functionReadline(ExecState* exec) 462{ 463 Vector<char, 256> line; 464 int c; 465 while ((c = getchar()) != EOF) { 466 // FIXME: Should we also break on \r? 467 if (c == '\n') 468 break; 469 line.append(c); 470 } 471 line.append('\0'); 472 return JSValue::encode(jsString(exec, line.data())); 473} 474 475EncodedJSValue JSC_HOST_CALL functionPreciseTime(ExecState*) 476{ 477 return JSValue::encode(jsNumber(currentTime())); 478} 479 480EncodedJSValue JSC_HOST_CALL functionQuit(ExecState*) 481{ 482 exit(EXIT_SUCCESS); 483 484#if COMPILER(MSVC) && OS(WINCE) 485 // Without this, Visual Studio will complain that this method does not return a value. 486 return JSValue::encode(jsUndefined()); 487#endif 488} 489 490// Use SEH for Release builds only to get rid of the crash report dialog 491// (luckily the same tests fail in Release and Debug builds so far). Need to 492// be in a separate main function because the jscmain function requires object 493// unwinding. 494 495#if COMPILER(MSVC) && !COMPILER(INTEL) && !defined(_DEBUG) && !OS(WINCE) 496#define TRY __try { 497#define EXCEPT(x) } __except (EXCEPTION_EXECUTE_HANDLER) { x; } 498#else 499#define TRY 500#define EXCEPT(x) 501#endif 502 503int jscmain(int argc, char** argv); 504 505int main(int argc, char** argv) 506{ 507#if PLATFORM(IOS) 508 // Enabled IEEE754 denormal support. 509 fenv_t env; 510 fegetenv( &env ); 511 env.__fpscr &= ~0x01000000u; 512 fesetenv( &env ); 513#endif 514 515#if OS(WINDOWS) 516#if !OS(WINCE) 517 // Cygwin calls ::SetErrorMode(SEM_FAILCRITICALERRORS), which we will inherit. This is bad for 518 // testing/debugging, as it causes the post-mortem debugger not to be invoked. We reset the 519 // error mode here to work around Cygwin's behavior. See <http://webkit.org/b/55222>. 520 ::SetErrorMode(0); 521#endif 522 523#if defined(_DEBUG) 524 _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR); 525 _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE); 526 _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR); 527 _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE); 528 _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR); 529 _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE); 530#endif 531 532 timeBeginPeriod(1); 533#endif 534 535#if PLATFORM(BLACKBERRY) 536 // Write all WTF logs to the system log 537 BlackBerry::Platform::setupApplicationLogging("jsc"); 538#endif 539 540#if PLATFORM(QT) 541 QCoreApplication app(argc, argv); 542#endif 543 544#if PLATFORM(EFL) 545 ecore_init(); 546#endif 547 548 // Initialize JSC before getting VM. 549#if ENABLE(SAMPLING_REGIONS) 550 WTF::initializeMainThread(); 551#endif 552 JSC::initializeThreading(); 553 554 // We can't use destructors in the following code because it uses Windows 555 // Structured Exception Handling 556 int res = 0; 557 TRY 558 res = jscmain(argc, argv); 559 EXCEPT(res = 3) 560 if (Options::logHeapStatisticsAtExit()) 561 HeapStatistics::reportSuccess(); 562 563#if PLATFORM(EFL) 564 ecore_shutdown(); 565#endif 566 567 return res; 568} 569 570static bool runWithScripts(GlobalObject* globalObject, const Vector<Script>& scripts, bool dump) 571{ 572 const char* script; 573 String fileName; 574 Vector<char> scriptBuffer; 575 576 if (dump) 577 JSC::Options::dumpGeneratedBytecodes() = true; 578 579 VM& vm = globalObject->vm(); 580 581#if ENABLE(SAMPLING_FLAGS) 582 SamplingFlags::start(); 583#endif 584 585 bool success = true; 586 for (size_t i = 0; i < scripts.size(); i++) { 587 if (scripts[i].isFile) { 588 fileName = scripts[i].argument; 589 if (!fillBufferWithContentsOfFile(fileName, scriptBuffer)) 590 return false; // fail early so we can catch missing files 591 script = scriptBuffer.data(); 592 } else { 593 script = scripts[i].argument; 594 fileName = "[Command Line]"; 595 } 596 597 vm.startSampling(); 598 599 JSValue evaluationException; 600 JSValue returnValue = evaluate(globalObject->globalExec(), jscSource(script, fileName), JSValue(), &evaluationException); 601 success = success && !evaluationException; 602 if (dump && !evaluationException) 603 printf("End: %s\n", returnValue.toString(globalObject->globalExec())->value(globalObject->globalExec()).utf8().data()); 604 if (evaluationException) { 605 printf("Exception: %s\n", evaluationException.toString(globalObject->globalExec())->value(globalObject->globalExec()).utf8().data()); 606 Identifier stackID(globalObject->globalExec(), "stack"); 607 JSValue stackValue = evaluationException.get(globalObject->globalExec(), stackID); 608 if (!stackValue.isUndefinedOrNull()) 609 printf("%s\n", stackValue.toString(globalObject->globalExec())->value(globalObject->globalExec()).utf8().data()); 610 } 611 612 vm.stopSampling(); 613 globalObject->globalExec()->clearException(); 614 } 615 616#if ENABLE(SAMPLING_FLAGS) 617 SamplingFlags::stop(); 618#endif 619#if ENABLE(SAMPLING_REGIONS) 620 SamplingRegion::dump(); 621#endif 622 vm.dumpSampleData(globalObject->globalExec()); 623#if ENABLE(SAMPLING_COUNTERS) 624 AbstractSamplingCounter::dump(); 625#endif 626#if ENABLE(REGEXP_TRACING) 627 vm.dumpRegExpTrace(); 628#endif 629 return success; 630} 631 632#define RUNNING_FROM_XCODE 0 633 634static void runInteractive(GlobalObject* globalObject) 635{ 636 String interpreterName("Interpreter"); 637 638 bool shouldQuit = false; 639 while (!shouldQuit) { 640#if HAVE(READLINE) && !RUNNING_FROM_XCODE 641 ParserError error; 642 String source; 643 do { 644 error = ParserError(); 645 char* line = readline(source.isEmpty() ? interactivePrompt : "... "); 646 shouldQuit = !line; 647 if (!line) 648 break; 649 source = source + line; 650 source = source + '\n'; 651 checkSyntax(globalObject->vm(), makeSource(source, interpreterName), error); 652 if (!line[0]) 653 break; 654 add_history(line); 655 } while (error.m_syntaxErrorType == ParserError::SyntaxErrorRecoverable); 656 657 if (error.m_type != ParserError::ErrorNone) { 658 printf("%s:%d\n", error.m_message.utf8().data(), error.m_line); 659 continue; 660 } 661 662 663 JSValue evaluationException; 664 JSValue returnValue = evaluate(globalObject->globalExec(), makeSource(source, interpreterName), JSValue(), &evaluationException); 665#else 666 printf("%s", interactivePrompt); 667 Vector<char, 256> line; 668 int c; 669 while ((c = getchar()) != EOF) { 670 // FIXME: Should we also break on \r? 671 if (c == '\n') 672 break; 673 line.append(c); 674 } 675 if (line.isEmpty()) 676 break; 677 line.append('\0'); 678 679 JSValue evaluationException; 680 JSValue returnValue = evaluate(globalObject->globalExec(), jscSource(line.data(), interpreterName), JSValue(), &evaluationException); 681#endif 682 if (evaluationException) 683 printf("Exception: %s\n", evaluationException.toString(globalObject->globalExec())->value(globalObject->globalExec()).utf8().data()); 684 else 685 printf("%s\n", returnValue.toString(globalObject->globalExec())->value(globalObject->globalExec()).utf8().data()); 686 687 globalObject->globalExec()->clearException(); 688 } 689 printf("\n"); 690} 691 692static NO_RETURN void printUsageStatement(bool help = false) 693{ 694 fprintf(stderr, "Usage: jsc [options] [files] [-- arguments]\n"); 695 fprintf(stderr, " -d Dumps bytecode (debug builds only)\n"); 696 fprintf(stderr, " -e Evaluate argument as script code\n"); 697 fprintf(stderr, " -f Specifies a source file (deprecated)\n"); 698 fprintf(stderr, " -h|--help Prints this help message\n"); 699 fprintf(stderr, " -i Enables interactive mode (default if no files are specified)\n"); 700#if HAVE(SIGNAL_H) 701 fprintf(stderr, " -s Installs signal handlers that exit on a crash (Unix platforms only)\n"); 702#endif 703 fprintf(stderr, " -p <file> Outputs profiling data to a file\n"); 704 fprintf(stderr, " -x Output exit code before terminating\n"); 705 fprintf(stderr, "\n"); 706 fprintf(stderr, " --options Dumps all JSC VM options and exits\n"); 707 fprintf(stderr, " --dumpOptions Dumps all JSC VM options before continuing\n"); 708 fprintf(stderr, " --<jsc VM option>=<value> Sets the specified JSC VM option\n"); 709 fprintf(stderr, "\n"); 710 711 exit(help ? EXIT_SUCCESS : EXIT_FAILURE); 712} 713 714void CommandLine::parseArguments(int argc, char** argv) 715{ 716 int i = 1; 717 bool needToDumpOptions = false; 718 bool needToExit = false; 719 720 for (; i < argc; ++i) { 721 const char* arg = argv[i]; 722 if (!strcmp(arg, "-f")) { 723 if (++i == argc) 724 printUsageStatement(); 725 m_scripts.append(Script(true, argv[i])); 726 continue; 727 } 728 if (!strcmp(arg, "-e")) { 729 if (++i == argc) 730 printUsageStatement(); 731 m_scripts.append(Script(false, argv[i])); 732 continue; 733 } 734 if (!strcmp(arg, "-i")) { 735 m_interactive = true; 736 continue; 737 } 738 if (!strcmp(arg, "-d")) { 739 m_dump = true; 740 continue; 741 } 742 if (!strcmp(arg, "-p")) { 743 if (++i == argc) 744 printUsageStatement(); 745 m_profile = true; 746 m_profilerOutput = argv[i]; 747 continue; 748 } 749 if (!strcmp(arg, "-s")) { 750#if HAVE(SIGNAL_H) 751 signal(SIGILL, _exit); 752 signal(SIGFPE, _exit); 753 signal(SIGBUS, _exit); 754 signal(SIGSEGV, _exit); 755#endif 756 continue; 757 } 758 if (!strcmp(arg, "-x")) { 759 m_exitCode = true; 760 continue; 761 } 762 if (!strcmp(arg, "--")) { 763 ++i; 764 break; 765 } 766 if (!strcmp(arg, "-h") || !strcmp(arg, "--help")) 767 printUsageStatement(true); 768 769 if (!strcmp(arg, "--options")) { 770 needToDumpOptions = true; 771 needToExit = true; 772 continue; 773 } 774 if (!strcmp(arg, "--dumpOptions")) { 775 needToDumpOptions = true; 776 continue; 777 } 778 779 // See if the -- option is a JSC VM option. 780 // NOTE: At this point, we know that the arg starts with "--". Skip it. 781 if (JSC::Options::setOption(&arg[2])) { 782 // The arg was recognized as a VM option and has been parsed. 783 continue; // Just continue with the next arg. 784 } 785 786 // This arg is not recognized by the VM nor by jsc. Pass it on to the 787 // script. 788 m_scripts.append(Script(true, argv[i])); 789 } 790 791 if (m_scripts.isEmpty()) 792 m_interactive = true; 793 794 for (; i < argc; ++i) 795 m_arguments.append(argv[i]); 796 797 if (needToDumpOptions) 798 JSC::Options::dumpAllOptions(stderr); 799 if (needToExit) 800 exit(EXIT_SUCCESS); 801} 802 803int jscmain(int argc, char** argv) 804{ 805 // Note that the options parsing can affect VM creation, and thus 806 // comes first. 807 CommandLine options(argc, argv); 808 VM* vm = VM::create(LargeHeap).leakRef(); 809 APIEntryShim shim(vm); 810 int result; 811 812 if (options.m_profile && !vm->m_perBytecodeProfiler) 813 vm->m_perBytecodeProfiler = adoptPtr(new Profiler::Database(*vm)); 814 815 GlobalObject* globalObject = GlobalObject::create(*vm, GlobalObject::createStructure(*vm, jsNull()), options.m_arguments); 816 bool success = runWithScripts(globalObject, options.m_scripts, options.m_dump); 817 if (options.m_interactive && success) 818 runInteractive(globalObject); 819 820 result = success ? 0 : 3; 821 822 if (options.m_exitCode) 823 printf("jsc exiting %d\n", result); 824 825 if (options.m_profile) { 826 if (!vm->m_perBytecodeProfiler->save(options.m_profilerOutput.utf8().data())) 827 fprintf(stderr, "could not save profiler output.\n"); 828 } 829 830 return result; 831} 832 833static bool fillBufferWithContentsOfFile(const String& fileName, Vector<char>& buffer) 834{ 835 FILE* f = fopen(fileName.utf8().data(), "r"); 836 if (!f) { 837 fprintf(stderr, "Could not open file: %s\n", fileName.utf8().data()); 838 return false; 839 } 840 841 size_t bufferSize = 0; 842 size_t bufferCapacity = 1024; 843 844 buffer.resize(bufferCapacity); 845 846 while (!feof(f) && !ferror(f)) { 847 bufferSize += fread(buffer.data() + bufferSize, 1, bufferCapacity - bufferSize, f); 848 if (bufferSize == bufferCapacity) { // guarantees space for trailing '\0' 849 bufferCapacity *= 2; 850 buffer.resize(bufferCapacity); 851 } 852 } 853 fclose(f); 854 buffer[bufferSize] = '\0'; 855 856 if (buffer[0] == '#' && buffer[1] == '!') 857 buffer[0] = buffer[1] = '/'; 858 859 return true; 860} 861