Machine.cpp 193 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
/*
 * Copyright (C) 2008 Apple Inc. All rights reserved.
 * Copyright (C) 2008 Cameron Zwarich <cwzwarich@uwaterloo.ca>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1.  Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 * 2.  Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of
 *     its contributors may be used to endorse or promote products derived
 *     from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "Machine.h"

33
#include "Arguments.h"
ggaren@apple.com's avatar
ggaren@apple.com committed
34
#include "BatchedTransitionOptimizer.h"
35 36 37 38
#include "CodeBlock.h"
#include "DebuggerCallFrame.h"
#include "ExceptionHelpers.h"
#include "ExecState.h"
weinig@apple.com's avatar
weinig@apple.com committed
39
#include "GlobalEvalFunction.h"
40
#include "JSActivation.h"
41 42
#include "JSArray.h"
#include "JSFunction.h"
43
#include "JSNotAnObject.h"
44
#include "JSPropertyNameIterator.h"
45
#include "JSStaticScopeObject.h"
46
#include "JSString.h"
weinig@apple.com's avatar
weinig@apple.com committed
47
#include "ObjectPrototype.h"
48
#include "Parser.h"
49
#include "Profiler.h"
50
#include "RegExpObject.h"
51
#include "RegExpPrototype.h"
52
#include "Register.h"
53
#include "collector.h"
54 55
#include "debugger.h"
#include "operations.h"
56
#include "SamplingTool.h"
57 58
#include <stdio.h>

59 60 61 62
#if PLATFORM(DARWIN)
#include <mach/mach.h>
#endif

63 64 65 66 67 68 69 70 71 72 73 74
#if HAVE(SYS_TIME_H)
#include <sys/time.h>
#endif

#if PLATFORM(WIN_OS)
#include <windows.h>
#endif

#if PLATFORM(QT)
#include <QDateTime>
#endif

ggaren@apple.com's avatar
ggaren@apple.com committed
75 76
using namespace std;

77
namespace JSC {
78

79 80 81
// Preferred number of milliseconds between each timeout check
static const int preferredScriptCheckTimeInterval = 1000;

82 83 84 85 86
#if HAVE(COMPUTED_GOTO)
static void* op_throw_end_indirect;
static void* op_call_indirect;
#endif

87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107
#if ENABLE(CTI)

ALWAYS_INLINE static Instruction* vPCForPC(CodeBlock* codeBlock, void* pc)
{
    if (pc >= codeBlock->instructions.begin() && pc < codeBlock->instructions.end())
        return static_cast<Instruction*>(pc);

    ASSERT(codeBlock->ctiReturnAddressVPCMap.contains(pc));
    unsigned vPCIndex = codeBlock->ctiReturnAddressVPCMap.get(pc);
    return codeBlock->instructions.begin() + vPCIndex;
}

#else // #ENABLE(CTI)

ALWAYS_INLINE static Instruction* vPCForPC(CodeBlock*, void* pc)
{
    return static_cast<Instruction*>(pc);
}

#endif // #ENABLE(CTI)

108
// Returns the depth of the scope chain within a given call frame.
109
static int depth(CodeBlock* codeBlock, ScopeChain& sc)
110
{
111 112
    if (!codeBlock->needsFullScopeChain)
        return 0;
113
    int scopeDepth = 0;
114 115
    ScopeChainIterator iter = sc.begin();
    ScopeChainIterator end = sc.end();
116
    while (!(*iter)->isObject(&JSActivation::info)) {
117
        ++iter;
118 119
        if (iter == end)
            break;
120 121 122 123
        ++scopeDepth;
    }
    return scopeDepth;
}
124

darin@apple.com's avatar
darin@apple.com committed
125 126 127 128
// FIXME: This operation should be called "getNumber", not "isNumber" (as it is in JSValue.h).
// FIXME: There's no need to have a "slow" version of this. All versions should be fast.
static bool fastIsNumber(JSValue* value, double& arg)
{
129 130
    if (JSImmediate::isNumber(value))
        arg = JSImmediate::getTruncatedInt32(value);
darin@apple.com's avatar
darin@apple.com committed
131
    else if (Heap::isNumber(static_cast<JSCell*>(value)))
132 133 134 135 136 137
        arg = static_cast<JSNumberCell*>(value)->value();
    else
        return false;
    return true;
}

darin@apple.com's avatar
darin@apple.com committed
138 139 140
// FIXME: Why doesn't JSValue::toInt32 have the Heap::isNumber optimization?
static bool fastToInt32(JSValue* value, int32_t& arg)
{
141 142
    if (JSImmediate::isNumber(value))
        arg = JSImmediate::getTruncatedInt32(value);
darin@apple.com's avatar
darin@apple.com committed
143 144
    else if (Heap::isNumber(static_cast<JSCell*>(value)))
        arg = static_cast<JSNumberCell*>(value)->toInt32();
145 146 147 148 149
    else
        return false;
    return true;
}

darin@apple.com's avatar
darin@apple.com committed
150 151
static ALWAYS_INLINE bool fastToUInt32(JSValue* value, uint32_t& arg)
{
152 153 154 155 156 157
    if (JSImmediate::isNumber(value)) {
        if (JSImmediate::getTruncatedUInt32(value, arg))
            return true;
        bool scratch;
        arg = JSValue::toUInt32SlowCase(JSImmediate::getTruncatedInt32(value), scratch);
        return true;
darin@apple.com's avatar
darin@apple.com committed
158 159
    } else if (Heap::isNumber(static_cast<JSCell*>(value)))
        arg = static_cast<JSNumberCell*>(value)->toUInt32();
160 161 162 163 164
    else
        return false;
    return true;
}

165 166
static inline bool jsLess(ExecState* exec, JSValue* v1, JSValue* v2)
{
167 168 169
    if (JSImmediate::areBothImmediateNumbers(v1, v2))
        return JSImmediate::getTruncatedInt32(v1) < JSImmediate::getTruncatedInt32(v2);

170 171
    double n1;
    double n2;
172 173 174
    if (fastIsNumber(v1, n1) && fastIsNumber(v2, n2))
        return n1 < n2;

darin@apple.com's avatar
darin@apple.com committed
175 176 177 178
    Machine* machine = exec->machine();
    if (machine->isJSString(v1) && machine->isJSString(v2))
        return static_cast<const JSString*>(v1)->value() < static_cast<const JSString*>(v2)->value();

179 180 181 182 183 184 185 186
    JSValue* p1;
    JSValue* p2;
    bool wasNotString1 = v1->getPrimitiveNumber(exec, n1, p1);
    bool wasNotString2 = v2->getPrimitiveNumber(exec, n2, p2);

    if (wasNotString1 | wasNotString2)
        return n1 < n2;

darin@apple.com's avatar
darin@apple.com committed
187
    return static_cast<const JSString*>(p1)->value() < static_cast<const JSString*>(p2)->value();
188 189 190 191
}

static inline bool jsLessEq(ExecState* exec, JSValue* v1, JSValue* v2)
{
192 193 194
    if (JSImmediate::areBothImmediateNumbers(v1, v2))
        return JSImmediate::getTruncatedInt32(v1) <= JSImmediate::getTruncatedInt32(v2);

195 196
    double n1;
    double n2;
197 198 199
    if (fastIsNumber(v1, n1) && fastIsNumber(v2, n2))
        return n1 <= n2;

darin@apple.com's avatar
darin@apple.com committed
200 201 202 203
    Machine* machine = exec->machine();
    if (machine->isJSString(v1) && machine->isJSString(v2))
        return !(static_cast<const JSString*>(v2)->value() < static_cast<const JSString*>(v1)->value());

204 205 206 207 208 209 210 211
    JSValue* p1;
    JSValue* p2;
    bool wasNotString1 = v1->getPrimitiveNumber(exec, n1, p1);
    bool wasNotString2 = v2->getPrimitiveNumber(exec, n2, p2);

    if (wasNotString1 | wasNotString2)
        return n1 <= n2;

darin@apple.com's avatar
darin@apple.com committed
212
    return !(static_cast<const JSString*>(p2)->value() < static_cast<const JSString*>(p1)->value());
213 214
}

215
static NEVER_INLINE JSValue* jsAddSlowCase(ExecState* exec, JSValue* v1, JSValue* v2)
216 217
{
    // exception for the Date exception in defaultValue()
darin@apple.com's avatar
darin@apple.com committed
218 219
    JSValue* p1 = v1->toPrimitive(exec);
    JSValue* p2 = v2->toPrimitive(exec);
220 221

    if (p1->isString() || p2->isString()) {
darin@apple.com's avatar
darin@apple.com committed
222 223
        RefPtr<UString::Rep> value = concatenate(p1->toString(exec).rep(), p2->toString(exec).rep());
        if (!value)
224
            return throwOutOfMemoryError(exec);
darin@apple.com's avatar
darin@apple.com committed
225
        return jsString(exec, value.release());
226 227
    }

ap@webkit.org's avatar
ap@webkit.org committed
228
    return jsNumber(exec, p1->toNumber(exec) + p2->toNumber(exec));
229 230 231 232 233
}

// Fast-path choices here are based on frequency data from SunSpider:
//    <times> Add case: <t1> <t2>
//    ---------------------------
234
//    5626160 Add case: 3 3 (of these, 3637690 are for immediate values)
ap@webkit.org's avatar
ap@webkit.org committed
235 236 237 238
//    247412  Add case: 5 5
//    20900   Add case: 5 6
//    13962   Add case: 5 3
//    4000    Add case: 3 5
239

240
static ALWAYS_INLINE JSValue* jsAdd(ExecState* exec, JSValue* v1, JSValue* v2)
241
{
242
    double left;
243 244 245 246
    double right = 0.0;

    bool rightIsNumber = fastIsNumber(v2, right);
    if (rightIsNumber && fastIsNumber(v1, left))
247 248
        return jsNumber(exec, left + right);
    
249 250
    bool leftIsString = v1->isString();
    if (leftIsString && v2->isString()) {
darin@apple.com's avatar
darin@apple.com committed
251 252
        RefPtr<UString::Rep> value = concatenate(static_cast<JSString*>(v1)->value().rep(), static_cast<JSString*>(v2)->value().rep());
        if (!value)
253
            return throwOutOfMemoryError(exec);
darin@apple.com's avatar
darin@apple.com committed
254
        return jsString(exec, value.release());
255 256
    }

257 258 259 260 261 262 263 264 265 266
    if (rightIsNumber & leftIsString) {
        RefPtr<UString::Rep> value = JSImmediate::isImmediate(v2) ?
            concatenate(static_cast<JSString*>(v1)->value().rep(), JSImmediate::getTruncatedInt32(v2)) :
            concatenate(static_cast<JSString*>(v1)->value().rep(), right);

        if (!value)
            return throwOutOfMemoryError(exec);
        return jsString(exec, value.release());
    }

267 268 269 270
    // All other cases are pretty uncommon
    return jsAddSlowCase(exec, v1, v2);
}

ap@webkit.org's avatar
ap@webkit.org committed
271
static JSValue* jsTypeStringForValue(ExecState* exec, JSValue* v)
272
{
darin@apple.com's avatar
darin@apple.com committed
273
    if (v->isUndefined())
darin@apple.com's avatar
darin@apple.com committed
274
        return jsNontrivialString(exec, "undefined");
darin@apple.com's avatar
darin@apple.com committed
275
    if (v->isBoolean())
darin@apple.com's avatar
darin@apple.com committed
276
        return jsNontrivialString(exec, "boolean");
darin@apple.com's avatar
darin@apple.com committed
277
    if (v->isNumber())
darin@apple.com's avatar
darin@apple.com committed
278
        return jsNontrivialString(exec, "number");
darin@apple.com's avatar
darin@apple.com committed
279
    if (v->isString())
darin@apple.com's avatar
darin@apple.com committed
280
        return jsNontrivialString(exec, "string");
darin@apple.com's avatar
darin@apple.com committed
281 282 283
    if (v->isObject()) {
        // Return "undefined" for objects that should be treated
        // as null when doing comparisons.
mjs@apple.com's avatar
mjs@apple.com committed
284
        if (static_cast<JSObject*>(v)->structureID()->typeInfo().masqueradesAsUndefined())
darin@apple.com's avatar
darin@apple.com committed
285
            return jsNontrivialString(exec, "undefined");
darin@apple.com's avatar
darin@apple.com committed
286 287
        CallData callData;
        if (static_cast<JSObject*>(v)->getCallData(callData) != CallTypeNone)
darin@apple.com's avatar
darin@apple.com committed
288
            return jsNontrivialString(exec, "function");
289
    }
darin@apple.com's avatar
darin@apple.com committed
290
    return jsNontrivialString(exec, "object");
291 292
}

293 294 295 296 297
static bool jsIsObjectType(JSValue* v)
{
    if (JSImmediate::isImmediate(v))
        return v->isNull();

mjs@apple.com's avatar
mjs@apple.com committed
298
    JSType type = static_cast<JSCell*>(v)->structureID()->typeInfo().type();
299 300 301
    if (type == NumberType || type == StringType)
        return false;
    if (type == ObjectType) {
mjs@apple.com's avatar
mjs@apple.com committed
302
        if (static_cast<JSObject*>(v)->structureID()->typeInfo().masqueradesAsUndefined())
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320
            return false;
        CallData callData;
        if (static_cast<JSObject*>(v)->getCallData(callData) != CallTypeNone)
            return false;
    }
    return true;
}

static bool jsIsFunctionType(JSValue* v)
{
    if (v->isObject()) {
        CallData callData;
        if (static_cast<JSObject*>(v)->getCallData(callData) != CallTypeNone)
            return true;
    }
    return false;
}

ggaren@apple.com's avatar
ggaren@apple.com committed
321
NEVER_INLINE bool Machine::resolve(ExecState* exec, Instruction* vPC, Register* r, JSValue*& exceptionValue)
322 323 324 325
{
    int dst = (vPC + 1)->u.operand;
    int property = (vPC + 2)->u.operand;

ggaren@apple.com's avatar
ggaren@apple.com committed
326
    ScopeChainNode* scopeChain = this->scopeChain(r);
327 328 329 330
    ScopeChainIterator iter = scopeChain->begin();
    ScopeChainIterator end = scopeChain->end();
    ASSERT(iter != end);

ggaren@apple.com's avatar
ggaren@apple.com committed
331
    CodeBlock* codeBlock = this->codeBlock(r);
332 333 334
    Identifier& ident = codeBlock->identifiers[property];
    do {
        JSObject* o = *iter;
darin@apple.com's avatar
darin@apple.com committed
335
        PropertySlot slot(o);
336
        if (o->getPropertySlot(exec, ident, slot)) {
darin@apple.com's avatar
darin@apple.com committed
337
            JSValue* result = slot.getValue(exec, ident);
338 339 340
            exceptionValue = exec->exception();
            if (exceptionValue)
                return false;
ggaren@apple.com's avatar
ggaren@apple.com committed
341
            r[dst] = result;
342 343 344
            return true;
        }
    } while (++iter != end);
345
    exceptionValue = createUndefinedVariableError(exec, ident, vPC, codeBlock);
346 347
    return false;
}
348

ggaren@apple.com's avatar
ggaren@apple.com committed
349
NEVER_INLINE bool Machine::resolveSkip(ExecState* exec, Instruction* vPC, Register* r, JSValue*& exceptionValue)
350
{
ggaren@apple.com's avatar
ggaren@apple.com committed
351
    CodeBlock* codeBlock = this->codeBlock(r);
352

353 354 355
    int dst = (vPC + 1)->u.operand;
    int property = (vPC + 2)->u.operand;
    int skip = (vPC + 3)->u.operand + codeBlock->needsFullScopeChain;
356

ggaren@apple.com's avatar
ggaren@apple.com committed
357
    ScopeChainNode* scopeChain = this->scopeChain(r);
358 359 360 361 362 363 364 365 366 367
    ScopeChainIterator iter = scopeChain->begin();
    ScopeChainIterator end = scopeChain->end();
    ASSERT(iter != end);
    while (skip--) {
        ++iter;
        ASSERT(iter != end);
    }
    Identifier& ident = codeBlock->identifiers[property];
    do {
        JSObject* o = *iter;
darin@apple.com's avatar
darin@apple.com committed
368
        PropertySlot slot(o);
369
        if (o->getPropertySlot(exec, ident, slot)) {
darin@apple.com's avatar
darin@apple.com committed
370
            JSValue* result = slot.getValue(exec, ident);
371 372 373
            exceptionValue = exec->exception();
            if (exceptionValue)
                return false;
ggaren@apple.com's avatar
ggaren@apple.com committed
374
            r[dst] = result;
375 376 377
            return true;
        }
    } while (++iter != end);
378
    exceptionValue = createUndefinedVariableError(exec, ident, vPC, codeBlock);
379 380 381
    return false;
}

382
NEVER_INLINE bool Machine::resolveGlobal(ExecState* exec, Instruction* vPC, Register* r, JSValue*& exceptionValue)
383 384 385 386 387 388 389 390 391 392 393 394 395
{
    int dst = (vPC + 1)->u.operand;
    JSGlobalObject* globalObject = static_cast<JSGlobalObject*>((vPC + 2)->u.jsCell);
    ASSERT(globalObject->isGlobalObject());
    int property = (vPC + 3)->u.operand;
    StructureID* structureID = (vPC + 4)->u.structureID;
    int offset = (vPC + 5)->u.operand;

    if (structureID == globalObject->structureID()) {
        r[dst] = globalObject->getDirectOffset(offset);
        return true;
    }

ggaren@apple.com's avatar
ggaren@apple.com committed
396
    CodeBlock* codeBlock = this->codeBlock(r);
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421
    Identifier& ident = codeBlock->identifiers[property];
    PropertySlot slot(globalObject);
    if (globalObject->getPropertySlot(exec, ident, slot)) {
        JSValue* result = slot.getValue(exec, ident);
        if (slot.isCacheable()) {
            if (vPC[4].u.structureID)
                vPC[4].u.structureID->deref();
            globalObject->structureID()->ref();
            vPC[4] = globalObject->structureID();
            vPC[5] = slot.cachedOffset();
            r[dst] = result;
            return true;
        }

        exceptionValue = exec->exception();
        if (exceptionValue)
            return false;
        r[dst] = result;
        return true;
    }

    exceptionValue = createUndefinedVariableError(exec, ident, vPC, codeBlock);
    return false;
}

422
ALWAYS_INLINE static JSValue* inlineResolveBase(ExecState* exec, Identifier& property, ScopeChainNode* scopeChain)
423 424
{
    ScopeChainIterator iter = scopeChain->begin();
425 426
    ScopeChainIterator next = iter;
    ++next;
427 428 429 430 431
    ScopeChainIterator end = scopeChain->end();
    ASSERT(iter != end);

    PropertySlot slot;
    JSObject* base;
432
    while (true) {
433
        base = *iter;
434 435 436
        if (next == end || base->getPropertySlot(exec, property, slot))
            return base;

437 438 439
        iter = next;
        ++next;
    }
440 441 442 443 444

    ASSERT_NOT_REACHED();
    return 0;
}

ggaren@apple.com's avatar
ggaren@apple.com committed
445
NEVER_INLINE void Machine::resolveBase(ExecState* exec, Instruction* vPC, Register* r)
446 447 448
{
    int dst = (vPC + 1)->u.operand;
    int property = (vPC + 2)->u.operand;
ggaren@apple.com's avatar
ggaren@apple.com committed
449 450
    CodeBlock* codeBlock = this->codeBlock(r);
    ScopeChainNode* scopeChain = this->scopeChain(r);
451
    r[dst] = inlineResolveBase(exec, codeBlock->identifiers[property], scopeChain);
452 453
}

ggaren@apple.com's avatar
ggaren@apple.com committed
454
NEVER_INLINE bool Machine::resolveBaseAndProperty(ExecState* exec, Instruction* vPC, Register* r, JSValue*& exceptionValue)
455 456 457 458
{
    int baseDst = (vPC + 1)->u.operand;
    int propDst = (vPC + 2)->u.operand;
    int property = (vPC + 3)->u.operand;
459

ggaren@apple.com's avatar
ggaren@apple.com committed
460
    ScopeChainNode* scopeChain = this->scopeChain(r);
461 462
    ScopeChainIterator iter = scopeChain->begin();
    ScopeChainIterator end = scopeChain->end();
463

464
    // FIXME: add scopeDepthIsZero optimization
465

466
    ASSERT(iter != end);
467

ggaren@apple.com's avatar
ggaren@apple.com committed
468
    CodeBlock* codeBlock = this->codeBlock(r);
469 470 471 472
    Identifier& ident = codeBlock->identifiers[property];
    JSObject* base;
    do {
        base = *iter;
darin@apple.com's avatar
darin@apple.com committed
473
        PropertySlot slot(base);
474
        if (base->getPropertySlot(exec, ident, slot)) {
darin@apple.com's avatar
darin@apple.com committed
475
            JSValue* result = slot.getValue(exec, ident);
476 477 478
            exceptionValue = exec->exception();
            if (exceptionValue)
                return false;
ggaren@apple.com's avatar
ggaren@apple.com committed
479 480
            r[propDst] = result;
            r[baseDst] = base;
481 482 483 484
            return true;
        }
        ++iter;
    } while (iter != end);
485

486
    exceptionValue = createUndefinedVariableError(exec, ident, vPC, codeBlock);
487 488 489
    return false;
}

ggaren@apple.com's avatar
ggaren@apple.com committed
490
NEVER_INLINE bool Machine::resolveBaseAndFunc(ExecState* exec, Instruction* vPC, Register* r, JSValue*& exceptionValue)
491 492 493 494 495
{
    int baseDst = (vPC + 1)->u.operand;
    int funcDst = (vPC + 2)->u.operand;
    int property = (vPC + 3)->u.operand;

ggaren@apple.com's avatar
ggaren@apple.com committed
496
    ScopeChainNode* scopeChain = this->scopeChain(r);
497 498
    ScopeChainIterator iter = scopeChain->begin();
    ScopeChainIterator end = scopeChain->end();
499

500
    // FIXME: add scopeDepthIsZero optimization
501

502
    ASSERT(iter != end);
503

ggaren@apple.com's avatar
ggaren@apple.com committed
504
    CodeBlock* codeBlock = this->codeBlock(r);
505 506 507 508
    Identifier& ident = codeBlock->identifiers[property];
    JSObject* base;
    do {
        base = *iter;
darin@apple.com's avatar
darin@apple.com committed
509
        PropertySlot slot(base);
510 511 512 513 514 515 516 517 518
        if (base->getPropertySlot(exec, ident, slot)) {            
            // ECMA 11.2.3 says that if we hit an activation the this value should be null.
            // However, section 10.2.3 says that in the case where the value provided
            // by the caller is null, the global object should be used. It also says
            // that the section does not apply to internal functions, but for simplicity
            // of implementation we use the global object anyway here. This guarantees
            // that in host objects you always get a valid object for this.
            // We also handle wrapper substitution for the global object at the same time.
            JSObject* thisObj = base->toThisObject(exec);
darin@apple.com's avatar
darin@apple.com committed
519
            JSValue* result = slot.getValue(exec, ident);
520 521 522
            exceptionValue = exec->exception();
            if (exceptionValue)
                return false;
523

ggaren@apple.com's avatar
ggaren@apple.com committed
524 525
            r[baseDst] = thisObj;
            r[funcDst] = result;
526 527 528 529 530
            return true;
        }
        ++iter;
    } while (iter != end);

531
    exceptionValue = createUndefinedVariableError(exec, ident, vPC, codeBlock);
532 533 534
    return false;
}

darin@apple.com's avatar
darin@apple.com committed
535
ALWAYS_INLINE Register* slideRegisterWindowForCall(CodeBlock* newCodeBlock, RegisterFile* registerFile, Register* r, size_t registerOffset, int argc)
536
{
537
    Register* newEnd = r + registerOffset + newCodeBlock->numCalleeRegisters;
538

darin@apple.com's avatar
darin@apple.com committed
539 540 541
    if (LIKELY(argc == newCodeBlock->numParameters)) { // correct number of arguments
        if (UNLIKELY(!registerFile->grow(newEnd)))
            return 0;
542
        r += registerOffset;
543
    } else if (argc < newCodeBlock->numParameters) { // too few arguments -- fill in the blanks
544 545
        size_t omittedArgCount = newCodeBlock->numParameters - argc;
        registerOffset += omittedArgCount;
546
        newEnd += omittedArgCount;
darin@apple.com's avatar
darin@apple.com committed
547 548
        if (!registerFile->grow(newEnd))
            return 0;
549
        r += registerOffset;
550

551 552 553 554 555 556
        Register* argv = r - RegisterFile::CallFrameHeaderSize - omittedArgCount;
        for (size_t i = 0; i < omittedArgCount; ++i)
            argv[i] = jsUndefined();
    } else { // too many arguments -- copy expected arguments, leaving the extra arguments behind
        size_t numParameters = newCodeBlock->numParameters;
        registerOffset += numParameters;
557
        newEnd += numParameters;
558

darin@apple.com's avatar
darin@apple.com committed
559 560
        if (!registerFile->grow(newEnd))
            return 0;
561
        r += registerOffset;
562

563 564 565
        Register* argv = r - RegisterFile::CallFrameHeaderSize - numParameters - argc;
        for (size_t i = 0; i < numParameters; ++i)
            argv[i + argc] = argv[i];
566
    }
567

568 569 570
    return r;
}

571
static NEVER_INLINE bool isNotObject(ExecState* exec, bool forInstanceOf, CodeBlock* codeBlock, const Instruction* vPC, JSValue* value, JSValue*& exceptionData)
572 573 574
{
    if (value->isObject())
        return false;
575
    exceptionData = createInvalidParamError(exec, forInstanceOf ? "instanceof" : "in" , value, vPC, codeBlock);
576 577 578
    return true;
}

579
NEVER_INLINE JSValue* Machine::callEval(ExecState* exec, JSObject* thisObj, ScopeChainNode* scopeChain, RegisterFile* registerFile, Register* r, int argv, int argc, JSValue*& exceptionValue)
580
{
581 582
    if (argc < 2)
        return jsUndefined();
583

ggaren@apple.com's avatar
ggaren@apple.com committed
584
    JSValue* program = r[argv + 1].jsValue(exec);
585

586 587
    if (!program->isString())
        return program;
588

589 590 591
    Profiler** profiler = Profiler::enabledProfilerReference();
    if (*profiler)
        (*profiler)->willExecute(exec, scopeChain->globalObject()->evalFunction());
592

mjs@apple.com's avatar
mjs@apple.com committed
593
    UString programSource = static_cast<JSString*>(program)->value();
594

ggaren@apple.com's avatar
ggaren@apple.com committed
595
    CodeBlock* codeBlock = this->codeBlock(r);
596
    RefPtr<EvalNode> evalNode = codeBlock->evalCodeCache.get(exec, programSource, scopeChain, exceptionValue);
597

mjs@apple.com's avatar
mjs@apple.com committed
598 599
    JSValue* result = 0;
    if (evalNode)
600
        result = exec->globalData().machine->execute(evalNode.get(), exec, thisObj, r - registerFile->start() + argv + 1 + RegisterFile::CallFrameHeaderSize, scopeChain, &exceptionValue);
601

602
    if (*profiler)
603
        (*profiler)->didExecute(exec, scopeChain->globalObject()->evalFunction());
604 605

    return result;
606 607 608
}

Machine::Machine()
609
    : m_sampler(0)
610 611 612 613 614
#if ENABLE(CTI)
    , m_ctiArrayLengthTrampoline(0)
    , m_ctiStringLengthTrampoline(0)
    , m_jitCodeBuffer(new JITCodeBuffer(1024 * 1024))
#endif
615
    , m_reentryDepth(0)
616 617 618 619 620
    , m_timeoutTime(0)
    , m_timeAtLastCheckTimeout(0)
    , m_timeExecuting(0)
    , m_timeoutCheckCount(0)
    , m_ticksUntilNextTimeoutCheck(initialTickCountThreshold)
621
{
622
    initTimeout();
darin@apple.com's avatar
darin@apple.com committed
623
    privateExecute(InitializeAndReturn, 0, 0, 0);
624 625 626 627
    
    // Bizarrely, calling fastMalloc here is faster than allocating space on the stack.
    void* storage = fastMalloc(sizeof(CollectorBlock));

mjs@apple.com's avatar
mjs@apple.com committed
628
    JSArray* jsArray = new (storage) JSArray(JSArray::createStructureID(jsNull()));
629
    m_jsArrayVptr = jsArray->vptr();
630
    static_cast<JSCell*>(jsArray)->~JSCell();
631

632
    JSString* jsString = new (storage) JSString(JSString::VPtrStealingHack);
633
    m_jsStringVptr = jsString->vptr();
634
    static_cast<JSCell*>(jsString)->~JSCell();
635

mjs@apple.com's avatar
mjs@apple.com committed
636
    JSFunction* jsFunction = new (storage) JSFunction(JSFunction::createStructureID(jsNull()));
637 638
    m_jsFunctionVptr = jsFunction->vptr();
    static_cast<JSCell*>(jsFunction)->~JSCell();
639 640
    
    fastFree(storage);
641 642
}

643 644 645 646 647 648 649 650 651 652
Machine::~Machine()
{
#if ENABLE(CTI)
    if (m_ctiArrayLengthTrampoline)
        fastFree(m_ctiArrayLengthTrampoline);
    if (m_ctiStringLengthTrampoline)
        fastFree(m_ctiStringLengthTrampoline);
#endif
}

653 654
#ifndef NDEBUG

ggaren@apple.com's avatar
ggaren@apple.com committed
655
void Machine::dumpCallFrame(const RegisterFile* registerFile, const Register* r)
656
{
ggaren@apple.com's avatar
ggaren@apple.com committed
657
    JSGlobalObject* globalObject = scopeChain(r)->globalObject();
658

ggaren@apple.com's avatar
ggaren@apple.com committed
659
    CodeBlock* codeBlock = this->codeBlock(r);
660
    codeBlock->dump(globalObject->globalExec());
661 662

    dumpRegisters(registerFile, r);
663 664
}

665
void Machine::dumpRegisters(const RegisterFile* registerFile, const Register* r)
666 667
{
    printf("Register frame: \n\n");
ggaren@apple.com's avatar
ggaren@apple.com committed
668 669 670
    printf("----------------------------------------------------\n");
    printf("            use            |   address  |   value   \n");
    printf("----------------------------------------------------\n");
671

ggaren@apple.com's avatar
ggaren@apple.com committed
672
    CodeBlock* codeBlock = this->codeBlock(r);
673 674
    const Register* it;
    const Register* end;
675

ggaren@apple.com's avatar
ggaren@apple.com committed
676 677 678 679
    if (codeBlock->codeType == GlobalCode) {
        it = registerFile->lastGlobal();
        end = it + registerFile->numGlobals();
        while (it != end) {
ggaren@apple.com's avatar
ggaren@apple.com committed
680
            printf("[global var]               | %10p | %10p \n", it, (*it).v());
ggaren@apple.com's avatar
ggaren@apple.com committed
681
            ++it;
682
        }
ggaren@apple.com's avatar
ggaren@apple.com committed
683 684 685
        printf("----------------------------------------------------\n");
    }
    
686 687 688 689 690 691 692 693 694 695 696
    it = r - RegisterFile::CallFrameHeaderSize - codeBlock->numParameters;
    printf("[this]                     | %10p | %10p \n", it, (*it).v()); ++it;
    end = it + max(codeBlock->numParameters - 1, 0); // - 1 to skip "this"
    if (it != end) {
        do {
            printf("[param]                    | %10p | %10p \n", it, (*it).v());
            ++it;
        } while (it != end);
    }
    printf("----------------------------------------------------\n");

697
    printf("[CodeBlock]                | %10p | %10p \n", it, (*it).v()); ++it;
ggaren@apple.com's avatar
ggaren@apple.com committed
698
    printf("[ScopeChain]               | %10p | %10p \n", it, (*it).v()); ++it;
699 700
    printf("[CallerRegisters]          | %10p | %10p \n", it, (*it).v()); ++it;
    printf("[ReturnPC]                 | %10p | %10p \n", it, (*it).v()); ++it;
ggaren@apple.com's avatar
ggaren@apple.com committed
701 702 703 704
    printf("[ReturnValueRegister]      | %10p | %10p \n", it, (*it).v()); ++it;
    printf("[ArgumentCount]            | %10p | %10p \n", it, (*it).v()); ++it;
    printf("[Callee]                   | %10p | %10p \n", it, (*it).v()); ++it;
    printf("[OptionalCalleeActivation] | %10p | %10p \n", it, (*it).v()); ++it;
705
    printf("[OptionalCalleeArguments]  | %10p | %10p \n", it, (*it).v()); ++it;
ggaren@apple.com's avatar
ggaren@apple.com committed
706 707
    printf("----------------------------------------------------\n");

708 709 710
    int registerCount = 0;

    end = it + codeBlock->numVars;
ggaren@apple.com's avatar
ggaren@apple.com committed
711 712
    if (it != end) {
        do {
713
            printf("[r%2d]                      | %10p | %10p \n", registerCount, it, (*it).v());
ggaren@apple.com's avatar
ggaren@apple.com committed
714
            ++it;
715
            ++registerCount;
ggaren@apple.com's avatar
ggaren@apple.com committed
716 717 718
        } while (it != end);
    }
    printf("----------------------------------------------------\n");
719

720 721 722 723 724 725 726
    end = it + codeBlock->numConstants;
    if (it != end) {
        do {
            printf("[r%2d]                      | %10p | %10p \n", registerCount, it, (*it).v());
            ++it;
            ++registerCount;
        } while (it != end);
727
    }
728
    printf("----------------------------------------------------\n");
729

730
    end = it + codeBlock->numCalleeRegisters - codeBlock->numConstants - codeBlock->numVars;
731 732
    if (it != end) {
        do {
733
            printf("[r%2d]                      | %10p | %10p \n", registerCount, it, (*it).v());
734
            ++it;
735
            ++registerCount;
736 737
        } while (it != end);
    }
738
    printf("----------------------------------------------------\n");
739 740
}

741 742
#endif

743
//#if !defined(NDEBUG) || ENABLE(SAMPLING_TOOL)
744

745 746 747 748 749 750 751 752 753 754 755
bool Machine::isOpcode(Opcode opcode)
{
#if HAVE(COMPUTED_GOTO)
    return opcode != HashTraits<Opcode>::emptyValue()
        && !HashTraits<Opcode>::isDeletedValue(opcode)
        && m_opcodeIDTable.contains(opcode);
#else
    return opcode >= 0 && opcode <= op_end;
#endif
}

756
//#endif
757

758
NEVER_INLINE bool Machine::unwindCallFrame(ExecState*& exec, JSValue* exceptionValue, const Instruction*& vPC, CodeBlock*& codeBlock, Register*& r)
759 760
{
    CodeBlock* oldCodeBlock = codeBlock;
ggaren@apple.com's avatar
ggaren@apple.com committed
761
    ScopeChainNode* scopeChain = this->scopeChain(r);
762

763
    if (Debugger* debugger = exec->dynamicGlobalObject()->debugger()) {
darin@apple.com's avatar
darin@apple.com committed
764
        DebuggerCallFrame debuggerCallFrame(exec->dynamicGlobalObject(), codeBlock, scopeChain, r, exceptionValue);
765
        if (r[RegisterFile::Callee].jsValue(exec))
ggaren@apple.com's avatar
ggaren@apple.com committed
766
            debugger->returnEvent(debuggerCallFrame, codeBlock->ownerNode->sourceID(), codeBlock->ownerNode->lastLine());
ggaren@apple.com's avatar
ggaren@apple.com committed
767
        else
ggaren@apple.com's avatar
ggaren@apple.com committed
768
            debugger->didExecuteProgram(debuggerCallFrame, codeBlock->ownerNode->sourceID(), codeBlock->ownerNode->lastLine());
769 770
    }

771
    if (Profiler* profiler = *Profiler::enabledProfilerReference()) {
772 773
        if (r[RegisterFile::Callee].jsValue(exec))
            profiler->didExecute(exec, static_cast<JSObject*>(r[RegisterFile::Callee].jsValue(exec)));
774 775 776 777
        else
            profiler->didExecute(exec, codeBlock->ownerNode->sourceURL(), codeBlock->ownerNode->lineNo());
    }

778 779
    if (oldCodeBlock->needsFullScopeChain)
        scopeChain->deref();
780

781 782
    // If this call frame created an activation or an 'arguments' object, tear it off.
    if (JSActivation* activation = static_cast<JSActivation*>(r[RegisterFile::OptionalCalleeActivation].getJSValue())) {
783
        ASSERT(activation->isObject(&JSActivation::info));
784 785 786
        Arguments* arguments = static_cast<Arguments*>(r[RegisterFile::OptionalCalleeArguments].getJSValue());
        ASSERT(!arguments || arguments->isObject(&Arguments::info));
        activation->copyRegisters(arguments);
787 788
    } else if (Arguments* arguments = static_cast<Arguments*>(r[RegisterFile::OptionalCalleeArguments].getJSValue())) {
        ASSERT(arguments->isObject(&Arguments::info));
789 790
        if (!arguments->isTornOff())
            arguments->copyRegisters();
791
    }
792
    
793
    void* returnPC = r[RegisterFile::ReturnPC].v();
794
    r = r[RegisterFile::CallerRegisters].r();
795
    exec = CallFrame::create(r);
796
    if (isHostCallFrame(r))
797
        return false;
798

ggaren@apple.com's avatar
ggaren@apple.com committed
799
    codeBlock = this->codeBlock(r);
800
    vPC = vPCForPC(codeBlock, returnPC);
801 802 803
    return true;
}

ggaren@apple.com's avatar
ggaren@apple.com committed
804
NEVER_INLINE Instruction* Machine::throwException(ExecState* exec, JSValue*& exceptionValue, const Instruction* vPC, Register*& r, bool explicitThrow)
805 806
{
    // Set up the exception object
807
    
ggaren@apple.com's avatar
ggaren@apple.com committed
808
    CodeBlock* codeBlock = this->codeBlock(r);
809 810
    if (exceptionValue->isObject()) {
        JSObject* exception = static_cast<JSObject*>(exceptionValue);
811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828 829 830 831 832 833
        if (exception->isNotAnObjectErrorStub()) {
            exception = createNotAnObjectError(exec, static_cast<JSNotAnObjectErrorStub*>(exception), vPC, codeBlock);
            exceptionValue = exception;
        } else {
            if (!exception->hasProperty(exec, Identifier(exec, "line")) && 
                !exception->hasProperty(exec, Identifier(exec, "sourceId")) && 
                !exception->hasProperty(exec, Identifier(exec, "sourceURL")) && 
                !exception->hasProperty(exec, Identifier(exec, expressionBeginOffsetPropertyName)) && 
                !exception->hasProperty(exec, Identifier(exec, expressionCaretOffsetPropertyName)) && 
                !exception->hasProperty(exec, Identifier(exec, expressionEndOffsetPropertyName))) {
                if (explicitThrow) {
                    int startOffset = 0;
                    int endOffset = 0;
                    int divotPoint = 0;
                    int line = codeBlock->expressionRangeForVPC(vPC, divotPoint, startOffset, endOffset);
                    exception->putWithAttributes(exec, Identifier(exec, "line"), jsNumber(exec, line), ReadOnly | DontDelete);
                    
                    // We only hit this path for error messages and throw statements, which don't have a specific failure position
                    // So we just give the full range of the error/throw statement.
                    exception->putWithAttributes(exec, Identifier(exec, expressionBeginOffsetPropertyName), jsNumber(exec, divotPoint - startOffset), ReadOnly | DontDelete);
                    exception->putWithAttributes(exec, Identifier(exec, expressionEndOffsetPropertyName), jsNumber(exec, divotPoint + endOffset), ReadOnly | DontDelete);
                } else
                    exception->putWithAttributes(exec, Identifier(exec, "line"), jsNumber(exec, codeBlock->lineNumberForVPC(vPC)), ReadOnly | DontDelete);
ggaren@apple.com's avatar
ggaren@apple.com committed
834
                exception->putWithAttributes(exec, Identifier(exec, "sourceId"), jsNumber(exec, codeBlock->ownerNode->sourceID()), ReadOnly | DontDelete);
835 836 837 838
                exception->putWithAttributes(exec, Identifier(exec, "sourceURL"), jsOwnedString(exec, codeBlock->ownerNode->sourceURL()), ReadOnly | DontDelete);
            }
            
            if (exception->isWatchdogException()) {
ggaren@apple.com's avatar
ggaren@apple.com committed
839
                while (unwindCallFrame(exec, exceptionValue, vPC, codeBlock, r)) {
840 841 842
                    // Don't need handler checks or anything, we just want to unroll all the JS callframes possible.
                }
                return 0;
843 844
            }
        }
845 846 847
    }

    if (Debugger* debugger = exec->dynamicGlobalObject()->debugger()) {
ggaren@apple.com's avatar
ggaren@apple.com committed
848
        ScopeChainNode* scopeChain = this->scopeChain(r);
darin@apple.com's avatar
darin@apple.com committed
849
        DebuggerCallFrame debuggerCallFrame(exec->dynamicGlobalObject(), codeBlock, scopeChain, r, exceptionValue);
ggaren@apple.com's avatar
ggaren@apple.com committed
850
        debugger->exception(debuggerCallFrame, codeBlock->ownerNode->sourceID(), codeBlock->lineNumberForVPC(vPC));
851 852 853 854
    }

    // Calculate an exception handler vPC, unwinding call frames as necessary.

855
    int scopeDepth;
856 857
    Instruction* handlerVPC;

858
    while (!codeBlock->getHandlerForVPC(vPC, handlerVPC, scopeDepth)) {
ggaren@apple.com's avatar
ggaren@apple.com committed
859
        if (!unwindCallFrame(exec, exceptionValue, vPC, codeBlock, r))
860
            return 0;
861
    }
862 863

    // Now unwind the scope chain within the exception handler's call frame.
864

ggaren@apple.com's avatar
ggaren@apple.com committed
865
    ScopeChain sc(this->scopeChain(r));
866
    int scopeDelta = depth(codeBlock, sc) - scopeDepth;
867 868