jsc.cpp 29.3 KB
Newer Older
1 2
/*
 *  Copyright (C) 1999-2000 Harri Porten (porten@kde.org)
3
 *  Copyright (C) 2004, 2005, 2006, 2007, 2008, 2012, 2013 Apple Inc. All rights reserved.
4
 *  Copyright (C) 2006 Bjoern Graf (bjoern.graf@gmail.com)
5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Library General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU Library General Public License
 *  along with this library; see the file COPYING.LIB.  If not, write to
mjs's avatar
mjs committed
18
 *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
19
 *  Boston, MA 02110-1301, USA.
20
 *
21 22
 */

mjs's avatar
mjs committed
23
#include "config.h"
ggaren's avatar
ggaren committed
24

25
#include "APIShims.h"
26
#include "ButterflyInlines.h"
27
#include "BytecodeGenerator.h"
28
#include "CallFrameInlines.h"
29
#include "Completion.h"
30
#include "CopiedSpaceInlines.h"
31
#include "ExceptionHelpers.h"
32
#include "HeapStatistics.h"
33
#include "InitializeThreading.h"
oliver@apple.com's avatar
oliver@apple.com committed
34
#include "Interpreter.h"
35
#include "JSArray.h"
36
#include "JSArrayBuffer.h"
37
#include "JSFunction.h"
ap@webkit.org's avatar
ap@webkit.org committed
38
#include "JSLock.h"
39
#include "JSProxy.h"
40
#include "JSString.h"
41
#include "Operations.h"
42
#include "SamplingTool.h"
43
#include "StackVisitor.h"
44
#include "StructureRareDataInlines.h"
45
#include "TestRunnerUtils.h"
46
#include <math.h>
47
#include <stdio.h>
48
#include <stdlib.h>
mjs's avatar
mjs committed
49
#include <string.h>
50
#include <thread>
51 52
#include <wtf/CurrentTime.h>
#include <wtf/MainThread.h>
53
#include <wtf/StringPrintStream.h>
54
#include <wtf/text/StringBuilder.h>
ggaren's avatar
ggaren committed
55

56
#if !OS(WINDOWS)
57 58 59
#include <unistd.h>
#endif

60
#if HAVE(READLINE)
61 62 63
// readline/history.h has a Function typedef which conflicts with the WTF::Function template from WTF/Forward.h
// We #define it to something else to avoid this conflict.
#define Function ReadlineFunction
64
#include <readline/history.h>
65
#include <readline/readline.h>
66
#undef Function
67 68
#endif

mjs's avatar
mjs committed
69
#if HAVE(SYS_TIME_H)
70
#include <sys/time.h>
71
#endif
72

73
#if HAVE(SIGNAL_H)
74 75 76
#include <signal.h>
#endif

77
#if COMPILER(MSVC) && !OS(WINCE)
78
#include <crtdbg.h>
79
#include <mmsystem.h>
80
#include <windows.h>
81 82
#endif

83
#if PLATFORM(IOS) && CPU(ARM_THUMB2)
84 85 86 87
#include <fenv.h>
#include <arm/arch.h>
#endif

88 89 90 91
#if PLATFORM(EFL)
#include <Ecore.h>
#endif

92
using namespace JSC;
mjs's avatar
mjs committed
93
using namespace WTF;
94

95
static bool fillBufferWithContentsOfFile(const String& fileName, Vector<char>& buffer);
96

97 98
static EncodedJSValue JSC_HOST_CALL functionPrint(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionDebug(ExecState*);
99
static EncodedJSValue JSC_HOST_CALL functionDescribe(ExecState*);
oliver@apple.com's avatar
oliver@apple.com committed
100
static EncodedJSValue JSC_HOST_CALL functionJSCStack(ExecState*);
101
static EncodedJSValue JSC_HOST_CALL functionGC(ExecState*);
102
#ifndef NDEBUG
103
static EncodedJSValue JSC_HOST_CALL functionReleaseExecutableMemory(ExecState*);
104
static EncodedJSValue JSC_HOST_CALL functionDumpCallFrame(ExecState*);
105
#endif
106 107 108
static EncodedJSValue JSC_HOST_CALL functionVersion(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionRun(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionLoad(ExecState*);
109
static EncodedJSValue JSC_HOST_CALL functionReadFile(ExecState*);
110 111
static EncodedJSValue JSC_HOST_CALL functionCheckSyntax(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionReadline(ExecState*);
112
static EncodedJSValue JSC_HOST_CALL functionPreciseTime(ExecState*);
113 114
static EncodedJSValue JSC_HOST_CALL functionNeverInlineFunction(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionNumberOfDFGCompiles(ExecState*);
115
static EncodedJSValue JSC_HOST_CALL functionTransferArrayBuffer(ExecState*);
116
static NO_RETURN_WITH_VALUE EncodedJSValue JSC_HOST_CALL functionQuit(ExecState*);
weinig@apple.com's avatar
weinig@apple.com committed
117

118
#if ENABLE(SAMPLING_FLAGS)
119 120
static EncodedJSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState*);
static EncodedJSValue JSC_HOST_CALL functionClearSamplingFlags(ExecState*);
121 122
#endif

123 124
struct Script {
    bool isFile;
125 126
    char* argument;

127 128 129 130 131 132 133
    Script(bool isFile, char *argument)
        : isFile(isFile)
        , argument(argument)
    {
    }
};

134 135 136 137 138 139
class CommandLine {
public:
    CommandLine(int argc, char** argv)
        : m_interactive(false)
        , m_dump(false)
        , m_exitCode(false)
140
        , m_profile(false)
141
    {
142
        parseArguments(argc, argv);
143 144
    }

145 146 147 148
    bool m_interactive;
    bool m_dump;
    bool m_exitCode;
    Vector<Script> m_scripts;
149
    Vector<String> m_arguments;
150 151
    bool m_profile;
    String m_profilerOutput;
152 153

    void parseArguments(int, char**);
154 155
};

156
static const char interactivePrompt[] = ">>> ";
157

weinig@apple.com's avatar
weinig@apple.com committed
158
class StopWatch {
159 160 161 162
public:
    void start();
    void stop();
    long getElapsedMS(); // call stop() first
weinig@apple.com's avatar
weinig@apple.com committed
163

164
private:
165 166
    double m_startTime;
    double m_stopTime;
167 168 169 170
};

void StopWatch::start()
{
171
    m_startTime = monotonicallyIncreasingTime();
172 173 174 175
}

void StopWatch::stop()
{
176
    m_stopTime = monotonicallyIncreasingTime();
177 178 179 180
}

long StopWatch::getElapsedMS()
{
181
    return static_cast<long>((m_stopTime - m_startTime) * 1000);
182
}
ggaren's avatar
ggaren committed
183

weinig@apple.com's avatar
weinig@apple.com committed
184
class GlobalObject : public JSGlobalObject {
185
private:
186
    GlobalObject(VM&, Structure*);
187 188

public:
189 190
    typedef JSGlobalObject Base;

191
    static GlobalObject* create(VM& vm, Structure* structure, const Vector<String>& arguments)
192
    {
193 194 195 196
        GlobalObject* object = new (NotNull, allocateCell<GlobalObject>(vm.heap)) GlobalObject(vm, structure);
        object->finishCreation(vm, arguments);
        vm.heap.addFinalizer(object, destroy);
        object->setGlobalThis(vm, JSProxy::create(vm, JSProxy::createStructure(vm, object, object->prototype()), object));
197
        return object;
198
    }
199

200 201
    static const bool needsDestruction = false;

202
    DECLARE_INFO;
203
    static const GlobalObjectMethodTable s_globalObjectMethodTable;
204

205
    static Structure* createStructure(VM& vm, JSValue prototype)
206
    {
207
        return Structure::create(vm, 0, prototype, TypeInfo(GlobalObjectType, StructureFlags), info());
208
    }
ggaren's avatar
ggaren committed
209

210 211
    static bool javaScriptExperimentsEnabled(const JSGlobalObject*) { return true; }

212
protected:
213
    void finishCreation(VM& vm, const Vector<String>& arguments)
214
    {
215
        Base::finishCreation(vm);
216
        
217 218 219 220 221
        addFunction(vm, "debug", functionDebug, 1);
        addFunction(vm, "describe", functionDescribe, 1);
        addFunction(vm, "print", functionPrint, 1);
        addFunction(vm, "quit", functionQuit, 0);
        addFunction(vm, "gc", functionGC, 0);
222
#ifndef NDEBUG
223 224
        addFunction(vm, "dumpCallFrame", functionDumpCallFrame, 0);
        addFunction(vm, "releaseExecutableMemory", functionReleaseExecutableMemory, 0);
225
#endif
226 227 228
        addFunction(vm, "version", functionVersion, 1);
        addFunction(vm, "run", functionRun, 1);
        addFunction(vm, "load", functionLoad, 1);
229
        addFunction(vm, "readFile", functionReadFile, 1);
230 231 232 233
        addFunction(vm, "checkSyntax", functionCheckSyntax, 1);
        addFunction(vm, "jscStack", functionJSCStack, 1);
        addFunction(vm, "readline", functionReadline, 0);
        addFunction(vm, "preciseTime", functionPreciseTime, 0);
234
        addFunction(vm, "neverInlineFunction", functionNeverInlineFunction, 1);
235
        addFunction(vm, "noInline", functionNeverInlineFunction, 1);
236
        addFunction(vm, "numberOfDFGCompiles", functionNumberOfDFGCompiles, 1);
237
        addFunction(vm, "transferArrayBuffer", functionTransferArrayBuffer, 1);
238
#if ENABLE(SAMPLING_FLAGS)
239 240
        addFunction(vm, "setSamplingFlags", functionSetSamplingFlags, 1);
        addFunction(vm, "clearSamplingFlags", functionClearSamplingFlags, 1);
241
#endif
242
        
243
        JSArray* array = constructEmptyArray(globalExec(), 0);
244
        for (size_t i = 0; i < arguments.size(); ++i)
245
            array->putDirectIndex(globalExec(), i, jsString(globalExec(), arguments[i]));
246
        putDirect(vm, Identifier(globalExec(), "arguments"), array);
247 248
    }

249
    void addFunction(VM& vm, const char* name, NativeFunction function, unsigned arguments)
250
    {
251 252
        Identifier identifier(&vm, name);
        putDirect(vm, identifier, JSFunction::create(vm, this, arguments, identifier.string(), function));
253
    }
254
    
255
    void addConstructableFunction(VM& vm, const char* name, NativeFunction function, unsigned arguments)
256
    {
257 258
        Identifier identifier(&vm, name);
        putDirect(vm, identifier, JSFunction::create(vm, this, arguments, identifier.string(), function, NoIntrinsic, function));
259
    }
260
};
261

262
const ClassInfo GlobalObject::s_info = { "global", &JSGlobalObject::s_info, 0, ExecState::globalObjectTable, CREATE_METHOD_TABLE(GlobalObject) };
263
const GlobalObjectMethodTable GlobalObject::s_globalObjectMethodTable = { &allowsAccessFrom, &supportsProfiling, &supportsRichSourceInfo, &shouldInterruptScript, &javaScriptExperimentsEnabled, 0, &shouldInterruptScriptBeforeTimeout };
264

265

266 267
GlobalObject::GlobalObject(VM& vm, Structure* structure)
    : JSGlobalObject(vm, structure, &s_globalObjectMethodTable)
268
{
weinig@apple.com's avatar
weinig@apple.com committed
269
}
270

271
static inline String stringFromUTF(const char* utf8)
272 273 274 275 276 277
{
    // Find the the first non-ascii character, or nul.
    const char* pos = utf8;
    while (*pos > 0)
        pos++;
    size_t asciiLength = pos - utf8;
278
    
279 280
    // Fast case - string is all ascii.
    if (!*pos)
281 282
        return String(utf8, asciiLength);
    
283 284 285
    // Slow case - contains non-ascii characters, use fromUTF8WithLatin1Fallback.
    ASSERT(*pos < 0);
    ASSERT(strlen(utf8) == asciiLength + strlen(pos));
286 287 288 289 290 291 292
    return String::fromUTF8WithLatin1Fallback(utf8, asciiLength + strlen(pos));
}

static inline SourceCode jscSource(const char* utf8, const String& filename)
{
    String str = stringFromUTF(utf8);
    return makeSource(str, filename);
293 294
}

295
EncodedJSValue JSC_HOST_CALL functionPrint(ExecState* exec)
weinig@apple.com's avatar
weinig@apple.com committed
296
{
297
    for (unsigned i = 0; i < exec->argumentCount(); ++i) {
298
        if (i)
299
            putchar(' ');
300

301
        printf("%s", exec->uncheckedArgument(i).toString(exec)->value(exec).utf8().data());
302
    }
303

304
    putchar('\n');
305
    fflush(stdout);
306
    return JSValue::encode(jsUndefined());
weinig@apple.com's avatar
weinig@apple.com committed
307
}
darin's avatar
darin committed
308

309 310 311
#ifndef NDEBUG
EncodedJSValue JSC_HOST_CALL functionDumpCallFrame(ExecState* exec)
{
312
    if (!exec->callerFrame()->isVMEntrySentinel())
313
        exec->vm().interpreter->dumpCallFrame(exec->callerFrame());
314 315 316 317
    return JSValue::encode(jsUndefined());
}
#endif

318
EncodedJSValue JSC_HOST_CALL functionDebug(ExecState* exec)
weinig@apple.com's avatar
weinig@apple.com committed
319
{
320
    fprintf(stderr, "--> %s\n", exec->argument(0).toString(exec)->value(exec).utf8().data());
321
    return JSValue::encode(jsUndefined());
weinig@apple.com's avatar
weinig@apple.com committed
322
}
323

324 325
EncodedJSValue JSC_HOST_CALL functionDescribe(ExecState* exec)
{
326
    fprintf(stderr, "--> %s\n", toCString(exec->argument(0)).data());
327 328 329
    return JSValue::encode(jsUndefined());
}

330 331 332 333 334 335 336
class FunctionJSCStackFunctor {
public:
    FunctionJSCStackFunctor(StringBuilder& trace)
        : m_trace(trace)
    {
    }

337
    StackVisitor::Status operator()(StackVisitor& visitor)
338
    {
339 340
        m_trace.append(String::format("    %zu   %s\n", visitor->index(), visitor->toString().utf8().data()));
        return StackVisitor::Continue;
341 342 343 344 345 346
    }

private:
    StringBuilder& m_trace;
};

oliver@apple.com's avatar
oliver@apple.com committed
347 348
EncodedJSValue JSC_HOST_CALL functionJSCStack(ExecState* exec)
{
349 350 351
    StringBuilder trace;
    trace.appendLiteral("--> Stack trace:\n");

352
    FunctionJSCStackFunctor functor(trace);
353
    exec->iterate(functor);
354
    fprintf(stderr, "%s", trace.toString().utf8().data());
oliver@apple.com's avatar
oliver@apple.com committed
355 356 357
    return JSValue::encode(jsUndefined());
}

358
EncodedJSValue JSC_HOST_CALL functionGC(ExecState* exec)
darin's avatar
darin committed
359
{
360
    JSLockHolder lock(exec);
361
    exec->heap()->collectAllGarbage();
362
    return JSValue::encode(jsUndefined());
darin's avatar
darin committed
363 364
}

365 366 367
#ifndef NDEBUG
EncodedJSValue JSC_HOST_CALL functionReleaseExecutableMemory(ExecState* exec)
{
368
    JSLockHolder lock(exec);
369
    exec->vm().releaseExecutableMemory();
370 371 372 373
    return JSValue::encode(jsUndefined());
}
#endif

374
EncodedJSValue JSC_HOST_CALL functionVersion(ExecState*)
375
{
weinig@apple.com's avatar
weinig@apple.com committed
376 377
    // We need this function for compatibility with the Mozilla JS tests but for now
    // we don't actually do any version-specific handling
378
    return JSValue::encode(jsUndefined());
weinig@apple.com's avatar
weinig@apple.com committed
379 380
}

381
EncodedJSValue JSC_HOST_CALL functionRun(ExecState* exec)
weinig@apple.com's avatar
weinig@apple.com committed
382
{
383
    String fileName = exec->argument(0).toString(exec)->value(exec);
weinig@apple.com's avatar
weinig@apple.com committed
384 385
    Vector<char> script;
    if (!fillBufferWithContentsOfFile(fileName, script))
386
        return JSValue::encode(exec->vm().throwException(exec, createError(exec, "Could not open file.")));
darin's avatar
darin committed
387

388
    GlobalObject* globalObject = GlobalObject::create(exec->vm(), GlobalObject::createStructure(exec->vm(), jsNull()), Vector<String>());
389

390 391
    JSArray* array = constructEmptyArray(globalObject->globalExec(), 0);
    for (unsigned i = 1; i < exec->argumentCount(); ++i)
392
        array->putDirectIndex(globalObject->globalExec(), i - 1, exec->uncheckedArgument(i));
393 394 395
    globalObject->putDirect(
        exec->vm(), Identifier(globalObject->globalExec(), "arguments"), array);

396
    JSValue exception;
397
    StopWatch stopWatch;
weinig@apple.com's avatar
weinig@apple.com committed
398
    stopWatch.start();
399
    evaluate(globalObject->globalExec(), jscSource(script.data(), fileName), JSValue(), &exception);
weinig@apple.com's avatar
weinig@apple.com committed
400 401
    stopWatch.stop();

402
    if (!!exception) {
403
        exec->vm().throwException(globalObject->globalExec(), exception);
404 405 406
        return JSValue::encode(jsUndefined());
    }
    
407
    return JSValue::encode(jsNumber(stopWatch.getElapsedMS()));
weinig@apple.com's avatar
weinig@apple.com committed
408 409
}

410
EncodedJSValue JSC_HOST_CALL functionLoad(ExecState* exec)
weinig@apple.com's avatar
weinig@apple.com committed
411
{
412
    String fileName = exec->argument(0).toString(exec)->value(exec);
weinig@apple.com's avatar
weinig@apple.com committed
413 414
    Vector<char> script;
    if (!fillBufferWithContentsOfFile(fileName, script))
415
        return JSValue::encode(exec->vm().throwException(exec, createError(exec, "Could not open file.")));
mjs's avatar
mjs committed
416

417
    JSGlobalObject* globalObject = exec->lexicalGlobalObject();
418 419
    
    JSValue evaluationException;
420
    JSValue result = evaluate(globalObject->globalExec(), jscSource(script.data(), fileName), JSValue(), &evaluationException);
421
    if (evaluationException)
422
        exec->vm().throwException(exec, evaluationException);
423
    return JSValue::encode(result);
weinig@apple.com's avatar
weinig@apple.com committed
424 425
}

426 427 428 429 430 431 432 433 434 435
EncodedJSValue JSC_HOST_CALL functionReadFile(ExecState* exec)
{
    String fileName = exec->argument(0).toString(exec)->value(exec);
    Vector<char> script;
    if (!fillBufferWithContentsOfFile(fileName, script))
        return JSValue::encode(exec->vm().throwException(exec, createError(exec, "Could not open file.")));

    return JSValue::encode(jsString(exec, stringFromUTF(script.data())));
}

436
EncodedJSValue JSC_HOST_CALL functionCheckSyntax(ExecState* exec)
437
{
438
    String fileName = exec->argument(0).toString(exec)->value(exec);
439 440
    Vector<char> script;
    if (!fillBufferWithContentsOfFile(fileName, script))
441
        return JSValue::encode(exec->vm().throwException(exec, createError(exec, "Could not open file.")));
442 443

    JSGlobalObject* globalObject = exec->lexicalGlobalObject();
444 445 446

    StopWatch stopWatch;
    stopWatch.start();
447 448

    JSValue syntaxException;
449
    bool validSyntax = checkSyntax(globalObject->globalExec(), jscSource(script.data(), fileName), &syntaxException);
450 451
    stopWatch.stop();

452
    if (!validSyntax)
453
        exec->vm().throwException(exec, syntaxException);
454
    return JSValue::encode(jsNumber(stopWatch.getElapsedMS()));
455 456
}

457
#if ENABLE(SAMPLING_FLAGS)
458
EncodedJSValue JSC_HOST_CALL functionSetSamplingFlags(ExecState* exec)
459
{
460
    for (unsigned i = 0; i < exec->argumentCount(); ++i) {
461
        unsigned flag = static_cast<unsigned>(exec->uncheckedArgument(i).toNumber(exec));
462 463 464
        if ((flag >= 1) && (flag <= 32))
            SamplingFlags::setFlag(flag);
    }
465
    return JSValue::encode(jsNull());
466 467
}

468
EncodedJSValue JSC_HOST_CALL functionClearSamplingFlags(ExecState* exec)
469
{
470
    for (unsigned i = 0; i < exec->argumentCount(); ++i) {
471
        unsigned flag = static_cast<unsigned>(exec->uncheckedArgument(i).toNumber(exec));
472 473 474
        if ((flag >= 1) && (flag <= 32))
            SamplingFlags::clearFlag(flag);
    }
475
    return JSValue::encode(jsNull());
476 477 478
}
#endif

479
EncodedJSValue JSC_HOST_CALL functionReadline(ExecState* exec)
weinig@apple.com's avatar
weinig@apple.com committed
480 481 482 483 484 485 486 487 488 489
{
    Vector<char, 256> line;
    int c;
    while ((c = getchar()) != EOF) {
        // FIXME: Should we also break on \r? 
        if (c == '\n')
            break;
        line.append(c);
    }
    line.append('\0');
490
    return JSValue::encode(jsString(exec, line.data()));
weinig@apple.com's avatar
weinig@apple.com committed
491 492
}

493 494 495 496 497
EncodedJSValue JSC_HOST_CALL functionPreciseTime(ExecState*)
{
    return JSValue::encode(jsNumber(currentTime()));
}

498 499 500 501 502 503 504 505 506 507
EncodedJSValue JSC_HOST_CALL functionNeverInlineFunction(ExecState* exec)
{
    return JSValue::encode(setNeverInline(exec));
}

EncodedJSValue JSC_HOST_CALL functionNumberOfDFGCompiles(ExecState* exec)
{
    return JSValue::encode(numberOfDFGCompiles(exec));
}

508 509 510 511 512 513 514 515 516 517 518 519 520 521 522
EncodedJSValue JSC_HOST_CALL functionTransferArrayBuffer(ExecState* exec)
{
    if (exec->argumentCount() < 1)
        return JSValue::encode(exec->vm().throwException(exec, createError(exec, "Not enough arguments")));
    
    JSArrayBuffer* buffer = jsDynamicCast<JSArrayBuffer*>(exec->argument(0));
    if (!buffer)
        return JSValue::encode(exec->vm().throwException(exec, createError(exec, "Expected an array buffer")));
    
    ArrayBufferContents dummyContents;
    buffer->impl()->transfer(dummyContents);
    
    return JSValue::encode(jsUndefined());
}

523
EncodedJSValue JSC_HOST_CALL functionQuit(ExecState*)
weinig@apple.com's avatar
weinig@apple.com committed
524
{
525
    exit(EXIT_SUCCESS);
526 527 528

#if COMPILER(MSVC) && OS(WINCE)
    // Without this, Visual Studio will complain that this method does not return a value.
529
    return JSValue::encode(jsUndefined());
530
#endif
531 532
}

533
// Use SEH for Release builds only to get rid of the crash report dialog
534
// (luckily the same tests fail in Release and Debug builds so far). Need to
mjs@apple.com's avatar
mjs@apple.com committed
535
// be in a separate main function because the jscmain function requires object
536 537
// unwinding.

538
#if COMPILER(MSVC) && !defined(_DEBUG) && !OS(WINCE)
539 540 541 542 543 544 545
#define TRY       __try {
#define EXCEPT(x) } __except (EXCEPTION_EXECUTE_HANDLER) { x; }
#else
#define TRY
#define EXCEPT(x)
#endif

546
int jscmain(int argc, char** argv);
547

548 549 550 551
static double s_desiredTimeout;

static NO_RETURN_DUE_TO_CRASH void timeoutThreadMain(void*)
{
552 553
    auto timeout = std::chrono::microseconds(static_cast<std::chrono::microseconds::rep>(s_desiredTimeout * 1000000));
    std::this_thread::sleep_for(timeout);
554 555 556 557 558
    
    dataLog("Timed out after ", s_desiredTimeout, " seconds!\n");
    CRASH();
}

559
int main(int argc, char** argv)
560
{
561
#if PLATFORM(IOS) && CPU(ARM_THUMB2)
562 563 564 565 566 567 568
    // Enabled IEEE754 denormal support.
    fenv_t env;
    fegetenv( &env );
    env.__fpscr &= ~0x01000000u;
    fesetenv( &env );
#endif

569
#if OS(WINDOWS)
570
#if !OS(WINCE)
571 572 573 574 575 576
    // Cygwin calls ::SetErrorMode(SEM_FAILCRITICALERRORS), which we will inherit. This is bad for
    // testing/debugging, as it causes the post-mortem debugger not to be invoked. We reset the
    // error mode here to work around Cygwin's behavior. See <http://webkit.org/b/55222>.
    ::SetErrorMode(0);

#if defined(_DEBUG)
577 578 579 580 581 582
    _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
    _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE);
    _CrtSetReportFile(_CRT_ERROR, _CRTDBG_FILE_STDERR);
    _CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_FILE);
    _CrtSetReportFile(_CRT_ASSERT, _CRTDBG_FILE_STDERR);
    _CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_FILE);
583
#endif
584 585
#endif

586 587 588
    timeBeginPeriod(1);
#endif

589 590 591 592
#if PLATFORM(EFL)
    ecore_init();
#endif

593
    // Initialize JSC before getting VM.
594
#if ENABLE(SAMPLING_REGIONS)
595
    WTF::initializeMainThread();
596
#endif
597
    JSC::initializeThreading();
598 599

#if !OS(WINCE)
600 601 602 603 604
    if (char* timeoutString = getenv("JSC_timeout")) {
        if (sscanf(timeoutString, "%lf", &s_desiredTimeout) != 1) {
            dataLog(
                "WARNING: timeout string is malformed, got ", timeoutString,
                " but expected a number. Not using a timeout.\n");
605
        } else
606 607
            createThread(timeoutThreadMain, 0, "jsc Timeout Thread");
    }
608
#endif
609

610 611 612 613
#if PLATFORM(IOS)
    Options::crashIfCantAllocateJITMemory() = true;
#endif

614 615
    // We can't use destructors in the following code because it uses Windows
    // Structured Exception Handling
616 617
    int res = 0;
    TRY
618
        res = jscmain(argc, argv);
619
    EXCEPT(res = 3)
620 621
    if (Options::logHeapStatisticsAtExit())
        HeapStatistics::reportSuccess();
622 623 624 625 626

#if PLATFORM(EFL)
    ecore_shutdown();
#endif

627 628
    return res;
}
629

630
static bool runWithScripts(GlobalObject* globalObject, const Vector<Script>& scripts, bool dump)
631
{
632
    const char* script;
633
    String fileName;
634
    Vector<char> scriptBuffer;
weinig@apple.com's avatar
weinig@apple.com committed
635

636
    if (dump)
637
        JSC::Options::dumpGeneratedBytecodes() = true;
638

639
    VM& vm = globalObject->vm();
oliver@apple.com's avatar
oliver@apple.com committed
640

641 642
#if ENABLE(SAMPLING_FLAGS)
    SamplingFlags::start();
weinig@apple.com's avatar
weinig@apple.com committed
643 644
#endif

weinig@apple.com's avatar
weinig@apple.com committed
645
    bool success = true;
646 647 648 649 650 651 652 653 654 655
    for (size_t i = 0; i < scripts.size(); i++) {
        if (scripts[i].isFile) {
            fileName = scripts[i].argument;
            if (!fillBufferWithContentsOfFile(fileName, scriptBuffer))
                return false; // fail early so we can catch missing files
            script = scriptBuffer.data();
        } else {
            script = scripts[i].argument;
            fileName = "[Command Line]";
        }
weinig@apple.com's avatar
weinig@apple.com committed
656

657
        vm.startSampling();
658

659
        JSValue evaluationException;
660
        JSValue returnValue = evaluate(globalObject->globalExec(), jscSource(script, fileName), JSValue(), &evaluationException);
661
        success = success && !evaluationException;
662 663 664 665 666 667 668 669
        if (dump && !evaluationException)
            printf("End: %s\n", returnValue.toString(globalObject->globalExec())->value(globalObject->globalExec()).utf8().data());
        if (evaluationException) {
            printf("Exception: %s\n", evaluationException.toString(globalObject->globalExec())->value(globalObject->globalExec()).utf8().data());
            Identifier stackID(globalObject->globalExec(), "stack");
            JSValue stackValue = evaluationException.get(globalObject->globalExec(), stackID);
            if (!stackValue.isUndefinedOrNull())
                printf("%s\n", stackValue.toString(globalObject->globalExec())->value(globalObject->globalExec()).utf8().data());
670
        }
671

672
        vm.stopSampling();
673
        globalObject->globalExec()->clearException();
weinig@apple.com's avatar
weinig@apple.com committed
674
    }
675

676 677
#if ENABLE(SAMPLING_FLAGS)
    SamplingFlags::stop();
678 679 680
#endif
#if ENABLE(SAMPLING_REGIONS)
    SamplingRegion::dump();
681
#endif
682
    vm.dumpSampleData(globalObject->globalExec());
683 684
#if ENABLE(SAMPLING_COUNTERS)
    AbstractSamplingCounter::dump();
685 686
#endif
#if ENABLE(REGEXP_TRACING)
687
    vm.dumpRegExpTrace();
688
#endif
weinig@apple.com's avatar
weinig@apple.com committed
689
    return success;
mjs's avatar
mjs committed
690 691
}

692 693 694
#define RUNNING_FROM_XCODE 0

static void runInteractive(GlobalObject* globalObject)
695
{
696
    String interpreterName("Interpreter");
697 698 699
    
    bool shouldQuit = false;
    while (!shouldQuit) {
700
#if HAVE(READLINE) && !RUNNING_FROM_XCODE
701 702 703 704 705
        ParserError error;
        String source;
        do {
            error = ParserError();
            char* line = readline(source.isEmpty() ? interactivePrompt : "... ");
706 707 708
            shouldQuit = !line;
            if (!line)
                break;
709 710
            source = source + line;
            source = source + '\n';
711
            checkSyntax(globalObject->vm(), makeSource(source, interpreterName), error);
712
            if (!line[0])
713
                break;
714
            add_history(line);
715 716 717 718 719 720 721 722
        } while (error.m_syntaxErrorType == ParserError::SyntaxErrorRecoverable);
        
        if (error.m_type != ParserError::ErrorNone) {
            printf("%s:%d\n", error.m_message.utf8().data(), error.m_line);
            continue;
        }
        
        
723
        JSValue evaluationException;
724
        JSValue returnValue = evaluate(globalObject->globalExec(), makeSource(source, interpreterName), JSValue(), &evaluationException);
725
#else
726
        printf("%s", interactivePrompt);
727 728 729 730 731 732 733 734
        Vector<char, 256> line;
        int c;
        while ((c = getchar()) != EOF) {
            // FIXME: Should we also break on \r? 
            if (c == '\n')
                break;
            line.append(c);
        }
735 736
        if (line.isEmpty())
            break;
737
        line.append('\0');
738 739

        JSValue evaluationException;
740
        JSValue returnValue = evaluate(globalObject->globalExec(), jscSource(line.data(), interpreterName), JSValue(), &evaluationException);
741
#endif
742
        if (evaluationException)
743
            printf("Exception: %s\n", evaluationException.toString(globalObject->globalExec())->value(globalObject->globalExec()).utf8().data());
744
        else
745
            printf("%s\n", returnValue.toString(globalObject->globalExec())->value(globalObject->globalExec()).utf8().data());
746 747

        globalObject->globalExec()->clearException();
748
    }
749
    printf("\n");
750 751
}

752
static NO_RETURN void printUsageStatement(bool help = false)
753
{
mjs@apple.com's avatar
mjs@apple.com committed
754
    fprintf(stderr, "Usage: jsc [options] [files] [-- arguments]\n");
755
    fprintf(stderr, "  -d         Dumps bytecode (debug builds only)\n");
756
    fprintf(stderr, "  -e         Evaluate argument as script code\n");
757 758 759
    fprintf(stderr, "  -f         Specifies a source file (deprecated)\n");
    fprintf(stderr, "  -h|--help  Prints this help message\n");
    fprintf(stderr, "  -i         Enables interactive mode (default if no files are specified)\n");
760
#if HAVE(SIGNAL_H)
761
    fprintf(stderr, "  -s         Installs signal handlers that exit on a crash (Unix platforms only)\n");
762
#endif
763
    fprintf(stderr, "  -p <file>  Outputs profiling data to a file\n");
764
    fprintf(stderr, "  -x         Output exit code before terminating\n");
765 766 767 768 769
    fprintf(stderr, "\n");
    fprintf(stderr, "  --options                  Dumps all JSC VM options and exits\n");
    fprintf(stderr, "  --dumpOptions              Dumps all JSC VM options before continuing\n");
    fprintf(stderr, "  --<jsc VM option>=<value>  Sets the specified JSC VM option\n");
    fprintf(stderr, "\n");
770

771
    exit(help ? EXIT_SUCCESS : EXIT_FAILURE);
772 773
}

774
void CommandLine::parseArguments(int argc, char** argv)
775
{
weinig@apple.com's avatar
weinig@apple.com committed
776
    int i = 1;
777 778 779
    bool needToDumpOptions = false;
    bool needToExit = false;

weinig@apple.com's avatar
weinig@apple.com committed
780 781
    for (; i < argc; ++i) {
        const char* arg = argv[i];
782
        if (!strcmp(arg, "-f")) {
weinig@apple.com's avatar
weinig@apple.com committed
783
            if (++i == argc)
784
                printUsageStatement();
785
            m_scripts.append(Script(true, argv[i]));
786 787
            continue;
        }
788
        if (!strcmp(arg, "-e")) {
789
            if (++i == argc)
790
                printUsageStatement();
791
            m_scripts.append(Script(false, argv[i]));
792 793
            continue;
        }
794
        if (!strcmp(arg, "-i")) {
795
            m_interactive = true;
weinig@apple.com's avatar
weinig@apple.com committed
796 797
            continue;
        }
798
        if (!strcmp(arg, "-d")) {
799
            m_dump = true;
800 801
            continue;
        }
802 803 804 805 806 807 808
        if (!strcmp(arg, "-p")) {
            if (++i == argc)
                printUsageStatement();
            m_profile = true;
            m_profilerOutput = argv[i];
            continue;
        }
809
        if (!strcmp(arg, "-s")) {
810
#if HAVE(SIGNAL_H)
811 812 813 814 815 816 817
            signal(SIGILL, _exit);
            signal(SIGFPE, _exit);
            signal(SIGBUS, _exit);
            signal(SIGSEGV, _exit);
#endif
            continue;
        }
msaboff@apple.com's avatar