nodes.cpp   [plain text]


/*
*  Copyright (C) 1999-2002 Harri Porten (porten@kde.org)
*  Copyright (C) 2001 Peter Kelly (pmk@post.com)
*  Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008 Apple Inc. All rights reserved.
*  Copyright (C) 2007 Cameron Zwarich (cwzwarich@uwaterloo.ca)
*  Copyright (C) 2007 Maks Orlovich
*  Copyright (C) 2007 Eric Seidel <eric@webkit.org>
*
*  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
*  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
*  Boston, MA 02110-1301, USA.
*
*/

#include "config.h"
#include "nodes.h"

#include "ExecState.h"
#include "JSGlobalObject.h"
#include "Parser.h"
#include "PropertyNameArray.h"
#include "array_object.h"
#include "debugger.h"
#include "function_object.h"
#include "lexer.h"
#include "operations.h"
#include "regexp_object.h"
#include <math.h>
#include <wtf/Assertions.h>
#include <wtf/HashCountedSet.h>
#include <wtf/HashSet.h>
#include <wtf/MathExtras.h>

namespace KJS {

class FunctionBodyNodeWithDebuggerHooks : public FunctionBodyNode {
public:
    FunctionBodyNodeWithDebuggerHooks(SourceElements*, VarStack*, FunctionStack*) KJS_FAST_CALL;
    virtual JSValue* execute(ExecState*) KJS_FAST_CALL;
};

#define KJS_CHECKEXCEPTION \
if (exec->hadException()) \
    return rethrowException(exec);

#define KJS_CHECKEXCEPTIONVALUE \
if (exec->hadException()) { \
    handleException(exec); \
    return jsUndefined(); \
}

#define KJS_CHECKEXCEPTIONNUMBER \
if (exec->hadException()) { \
    handleException(exec); \
    return 0; \
}

#define KJS_CHECKEXCEPTIONBOOLEAN \
if (exec->hadException()) { \
    handleException(exec); \
    return false; \
}

#define KJS_CHECKEXCEPTIONVOID \
if (exec->hadException()) { \
    handleException(exec); \
    return; \
}

#if !ASSERT_DISABLED
static inline bool canSkipLookup(ExecState* exec, const Identifier& ident)
{
    // Static lookup in EvalCode is impossible because variables aren't DontDelete.
    // Static lookup in GlobalCode may be possible, but we haven't implemented support for it yet.
    if (exec->codeType() != FunctionCode)
        return false;

    // Static lookup is impossible when something dynamic has been added to the front of the scope chain.
    if (exec->variableObject() != exec->scopeChain().top())
        return false;

    // Static lookup is impossible if the symbol isn't statically declared.
    if (!exec->variableObject()->symbolTable().contains(ident.ustring().rep()))
        return false;

    return true;
}
#endif

static inline bool isConstant(const LocalStorage& localStorage, size_t index)
{
    ASSERT(index < localStorage.size());
    return localStorage[index].attributes & ReadOnly;
}

// ------------------------------ Node -----------------------------------------

#ifndef NDEBUG
#ifndef LOG_CHANNEL_PREFIX
#define LOG_CHANNEL_PREFIX Log
#endif
static WTFLogChannel LogKJSNodeLeaks = { 0x00000000, "", WTFLogChannelOn };

struct ParserRefCountedCounter {
    static unsigned count;
    ParserRefCountedCounter()
    {
        if (count)
            LOG(KJSNodeLeaks, "LEAK: %u KJS::Node\n", count);
    }
};
unsigned ParserRefCountedCounter::count = 0;
static ParserRefCountedCounter parserRefCountedCounter;
#endif

static HashSet<ParserRefCounted*>* newTrackedObjects;
static HashCountedSet<ParserRefCounted*>* trackedObjectExtraRefCounts;

ParserRefCounted::ParserRefCounted()
{
#ifndef NDEBUG
    ++ParserRefCountedCounter::count;
#endif
    if (!newTrackedObjects)
        newTrackedObjects = new HashSet<ParserRefCounted*>;
    newTrackedObjects->add(this);
    ASSERT(newTrackedObjects->contains(this));
}

ParserRefCounted::~ParserRefCounted()
{
#ifndef NDEBUG
    --ParserRefCountedCounter::count;
#endif
}

void ParserRefCounted::ref()
{
    // bumping from 0 to 1 is just removing from the new nodes set
    if (newTrackedObjects) {
        HashSet<ParserRefCounted*>::iterator it = newTrackedObjects->find(this);
        if (it != newTrackedObjects->end()) {
            newTrackedObjects->remove(it);
            ASSERT(!trackedObjectExtraRefCounts || !trackedObjectExtraRefCounts->contains(this));
            return;
        }
    }

    ASSERT(!newTrackedObjects || !newTrackedObjects->contains(this));

    if (!trackedObjectExtraRefCounts)
        trackedObjectExtraRefCounts = new HashCountedSet<ParserRefCounted*>;
    trackedObjectExtraRefCounts->add(this);
}

void ParserRefCounted::deref()
{
    ASSERT(!newTrackedObjects || !newTrackedObjects->contains(this));

    if (!trackedObjectExtraRefCounts) {
        delete this;
        return;
    }

    HashCountedSet<ParserRefCounted*>::iterator it = trackedObjectExtraRefCounts->find(this);
    if (it == trackedObjectExtraRefCounts->end())
        delete this;
    else
        trackedObjectExtraRefCounts->remove(it);
}

unsigned ParserRefCounted::refcount()
{
    if (newTrackedObjects && newTrackedObjects->contains(this)) {
        ASSERT(!trackedObjectExtraRefCounts || !trackedObjectExtraRefCounts->contains(this));
        return 0;
    }

    ASSERT(!newTrackedObjects || !newTrackedObjects->contains(this));

    if (!trackedObjectExtraRefCounts)
        return 1;

    return 1 + trackedObjectExtraRefCounts->count(this);
}

void ParserRefCounted::deleteNewObjects()
{
    if (!newTrackedObjects)
        return;

#ifndef NDEBUG
    HashSet<ParserRefCounted*>::iterator end = newTrackedObjects->end();
    for (HashSet<ParserRefCounted*>::iterator it = newTrackedObjects->begin(); it != end; ++it)
        ASSERT(!trackedObjectExtraRefCounts || !trackedObjectExtraRefCounts->contains(*it));
#endif
    deleteAllValues(*newTrackedObjects);
    delete newTrackedObjects;
    newTrackedObjects = 0;
}

Node::Node()
    : m_expectedReturnType(ObjectType)
{
    m_line = lexer().lineNo();
}

Node::Node(JSType expectedReturn)
    : m_expectedReturnType(expectedReturn)
{
    m_line = lexer().lineNo();
}

double ExpressionNode::evaluateToNumber(ExecState* exec)
{
    JSValue* value = evaluate(exec);
    KJS_CHECKEXCEPTIONNUMBER
    return value->toNumber(exec);
}

bool ExpressionNode::evaluateToBoolean(ExecState* exec)
{
    JSValue* value = evaluate(exec);
    KJS_CHECKEXCEPTIONBOOLEAN
    return value->toBoolean(exec);
}

int32_t ExpressionNode::evaluateToInt32(ExecState* exec)
{
    JSValue* value = evaluate(exec);
    KJS_CHECKEXCEPTIONNUMBER
    return value->toInt32(exec);
}

uint32_t ExpressionNode::evaluateToUInt32(ExecState* exec)
{
    JSValue* value = evaluate(exec);
    KJS_CHECKEXCEPTIONNUMBER
    return value->toUInt32(exec);
}

static void substitute(UString& string, const UString& substring) KJS_FAST_CALL;
static void substitute(UString& string, const UString& substring)
{
    int position = string.find("%s");
    ASSERT(position != -1);
    UString newString = string.substr(0, position);
    newString.append(substring);
    newString.append(string.substr(position + 2));
    string = newString;
}

static inline int currentSourceId(ExecState* exec) KJS_FAST_CALL;
static inline int currentSourceId(ExecState* exec)
{
    return exec->scopeNode()->sourceId();
}

static inline const UString& currentSourceURL(ExecState* exec) KJS_FAST_CALL;
static inline const UString& currentSourceURL(ExecState* exec)
{
    return exec->scopeNode()->sourceURL();
}

JSValue* Node::setInterruptedCompletion(ExecState* exec)
{
    return exec->setInterruptedCompletion(Error::create(exec, TimeoutError, "JavaScript execution exceeded timeout.", lineNo(), currentSourceId(exec), currentSourceURL(exec)));
}

JSValue* Node::setErrorCompletion(ExecState* exec, ErrorType e, const char* msg)
{
    return exec->setThrowCompletion(Error::create(exec, e, msg, lineNo(), currentSourceId(exec), currentSourceURL(exec)));
}

JSValue* Node::setErrorCompletion(ExecState* exec, ErrorType e, const char* msg, const Identifier& ident)
{
    UString message = msg;
    substitute(message, ident.ustring());
    return exec->setThrowCompletion(Error::create(exec, e, message, lineNo(), currentSourceId(exec), currentSourceURL(exec)));
}

JSValue* Node::throwError(ExecState* exec, ErrorType e, const char* msg)
{
    return KJS::throwError(exec, e, msg, lineNo(), currentSourceId(exec), currentSourceURL(exec));
}

JSValue* Node::throwError(ExecState* exec, ErrorType e, const char* msg, const char* string)
{
    UString message = msg;
    substitute(message, string);
    return KJS::throwError(exec, e, message, lineNo(), currentSourceId(exec), currentSourceURL(exec));
}

JSValue* Node::throwError(ExecState* exec, ErrorType e, const char* msg, JSValue* v, Node* expr)
{
    UString message = msg;
    substitute(message, v->toString(exec));
    substitute(message, expr->toString());
    return KJS::throwError(exec, e, message, lineNo(), currentSourceId(exec), currentSourceURL(exec));
}

JSValue* Node::throwError(ExecState* exec, ErrorType e, const char* msg, const Identifier& label)
{
    UString message = msg;
    substitute(message, label.ustring());
    return KJS::throwError(exec, e, message, lineNo(), currentSourceId(exec), currentSourceURL(exec));
}

JSValue* Node::throwError(ExecState* exec, ErrorType e, const char* msg, JSValue* v, Node* e1, Node* e2)
{
    UString message = msg;
    substitute(message, v->toString(exec));
    substitute(message, e1->toString());
    substitute(message, e2->toString());
    return KJS::throwError(exec, e, message, lineNo(), currentSourceId(exec), currentSourceURL(exec));
}

JSValue* Node::throwError(ExecState* exec, ErrorType e, const char* msg, JSValue* v, Node* expr, const Identifier& label)
{
    UString message = msg;
    substitute(message, v->toString(exec));
    substitute(message, expr->toString());
    substitute(message, label.ustring());
    return KJS::throwError(exec, e, message, lineNo(), currentSourceId(exec), currentSourceURL(exec));
}

JSValue* Node::throwError(ExecState* exec, ErrorType e, const char* msg, JSValue* v, const Identifier& label)
{
    UString message = msg;
    substitute(message, v->toString(exec));
    substitute(message, label.ustring());
    return KJS::throwError(exec, e, message, lineNo(), currentSourceId(exec), currentSourceURL(exec));
}

JSValue* Node::throwUndefinedVariableError(ExecState* exec, const Identifier& ident)
{
    return throwError(exec, ReferenceError, "Can't find variable: %s", ident);
}

void Node::handleException(ExecState* exec)
{
    handleException(exec, exec->exception());
}

void Node::handleException(ExecState* exec, JSValue* exceptionValue)
{
    if (exceptionValue->isObject()) {
        JSObject* exception = static_cast<JSObject*>(exceptionValue);
        if (!exception->hasProperty(exec, "line") && !exception->hasProperty(exec, "sourceURL")) {
            exception->put(exec, "line", jsNumber(m_line));
            exception->put(exec, "sourceURL", jsString(currentSourceURL(exec)));
        }
    }
    Debugger* dbg = exec->dynamicGlobalObject()->debugger();
    if (dbg && !dbg->hasHandledException(exec, exceptionValue)) {
        bool cont = dbg->exception(exec, currentSourceId(exec), m_line, exceptionValue);
        if (!cont)
            dbg->imp()->abort();
    }
}

NEVER_INLINE JSValue* Node::rethrowException(ExecState* exec)
{
    JSValue* exception = exec->exception();
    exec->clearException();
    handleException(exec, exception);
    return exec->setThrowCompletion(exception);
}

// ------------------------------ StatementNode --------------------------------

StatementNode::StatementNode()
    : m_lastLine(-1)
{
    m_line = -1;
}

void StatementNode::setLoc(int firstLine, int lastLine)
{
    m_line = firstLine;
    m_lastLine = lastLine;
}

// ------------------------------ SourceElements --------------------------------

void SourceElements::append(PassRefPtr<StatementNode> statement)
{
    if (statement->isEmptyStatement())
        return;

    if (Debugger::debuggersPresent)
        m_statements.append(new BreakpointCheckStatement(statement));
    else
        m_statements.append(statement);
}

// ------------------------------ BreakpointCheckStatement --------------------------------

BreakpointCheckStatement::BreakpointCheckStatement(PassRefPtr<StatementNode> statement)
    : m_statement(statement)
{
    ASSERT(m_statement);
}

JSValue* BreakpointCheckStatement::execute(ExecState* exec)
{
    if (Debugger* debugger = exec->dynamicGlobalObject()->debugger())
        if (!debugger->atStatement(exec, currentSourceId(exec), m_statement->firstLine(), m_statement->lastLine()))
            return exec->setNormalCompletion();
    return m_statement->execute(exec);
}

void BreakpointCheckStatement::streamTo(SourceStream& stream) const
{
    m_statement->streamTo(stream);
}

void BreakpointCheckStatement::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_statement.get());
}

// ------------------------------ NullNode -------------------------------------

JSValue* NullNode::evaluate(ExecState* )
{
    return jsNull();
}

// ------------------------------ FalseNode ----------------------------------

JSValue* FalseNode::evaluate(ExecState*)
{
    return jsBoolean(false);
}

// ------------------------------ TrueNode ----------------------------------

JSValue* TrueNode::evaluate(ExecState*)
{
    return jsBoolean(true);
}

// ------------------------------ NumberNode -----------------------------------

JSValue* NumberNode::evaluate(ExecState*)
{
    // Number nodes are only created when the number can't fit in a JSImmediate, so no need to check again.
    return jsNumberCell(m_double);
}

double NumberNode::evaluateToNumber(ExecState*)
{
    return m_double;
}

bool NumberNode::evaluateToBoolean(ExecState*)
{
    return m_double < 0.0 || m_double > 0.0; // false for NaN as well as 0
}

int32_t NumberNode::evaluateToInt32(ExecState*)
{
    return JSValue::toInt32(m_double);
}

uint32_t NumberNode::evaluateToUInt32(ExecState*)
{
    return JSValue::toUInt32(m_double);
}

// ------------------------------ ImmediateNumberNode -----------------------------------

JSValue* ImmediateNumberNode::evaluate(ExecState*)
{
    return m_value;
}

int32_t ImmediateNumberNode::evaluateToInt32(ExecState*)
{
    return JSImmediate::getTruncatedInt32(m_value);
}

uint32_t ImmediateNumberNode::evaluateToUInt32(ExecState*)
{
    uint32_t i;
    if (JSImmediate::getTruncatedUInt32(m_value, i))
        return i;
    bool ok;
    return JSValue::toUInt32SlowCase(m_double, ok);
}

// ------------------------------ StringNode -----------------------------------

JSValue* StringNode::evaluate(ExecState*)
{
    return jsOwnedString(m_value);
}

double StringNode::evaluateToNumber(ExecState*)
{
    return m_value.toDouble();
}

bool StringNode::evaluateToBoolean(ExecState*)
{
    return !m_value.isEmpty();
}

// ------------------------------ RegExpNode -----------------------------------

JSValue* RegExpNode::evaluate(ExecState* exec)
{
    return exec->lexicalGlobalObject()->regExpConstructor()->createRegExpImp(exec, m_regExp);
}

// ------------------------------ ThisNode -------------------------------------

// ECMA 11.1.1
JSValue* ThisNode::evaluate(ExecState* exec)
{
    return exec->thisValue();
}

// ------------------------------ ResolveNode ----------------------------------

// ECMA 11.1.2 & 10.1.4
JSValue* ResolveNode::inlineEvaluate(ExecState* exec)
{
    // Check for missed optimization opportunity.
    ASSERT(!canSkipLookup(exec, m_ident));

    const ScopeChain& chain = exec->scopeChain();
    ScopeChainIterator iter = chain.begin();
    ScopeChainIterator end = chain.end();

    // we must always have something in the scope chain
    ASSERT(iter != end);

    PropertySlot slot;
    do {
        JSObject* o = *iter;

        if (o->getPropertySlot(exec, m_ident, slot))
            return slot.getValue(exec, o, m_ident);

        ++iter;
    } while (iter != end);

    return throwUndefinedVariableError(exec, m_ident);
}

JSValue* ResolveNode::evaluate(ExecState* exec)
{
    return inlineEvaluate(exec);
}

double ResolveNode::evaluateToNumber(ExecState* exec)
{
    JSValue* v = inlineEvaluate(exec);
    KJS_CHECKEXCEPTIONNUMBER
    return v->toNumber(exec);
}

bool ResolveNode::evaluateToBoolean(ExecState* exec)
{
    JSValue* v = inlineEvaluate(exec);
    KJS_CHECKEXCEPTIONBOOLEAN
    return v->toBoolean(exec);
}

int32_t ResolveNode::evaluateToInt32(ExecState* exec)
{
    JSValue* v = inlineEvaluate(exec);
    KJS_CHECKEXCEPTIONNUMBER
    return v->toInt32(exec);
}

uint32_t ResolveNode::evaluateToUInt32(ExecState* exec)
{
    JSValue* v = inlineEvaluate(exec);
    KJS_CHECKEXCEPTIONNUMBER
    return v->toUInt32(exec);
}

void ResolveNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage&, NodeStack&)
{
    size_t index = symbolTable.get(m_ident.ustring().rep());
    if (index != missingSymbolMarker())
        new (this) LocalVarAccessNode(index);
}

JSValue* LocalVarAccessNode::inlineEvaluate(ExecState* exec)
{
    ASSERT(exec->variableObject() == exec->scopeChain().top());
    return exec->localStorage()[m_index].value;
}

JSValue* LocalVarAccessNode::evaluate(ExecState* exec)
{
    return inlineEvaluate(exec);
}

double LocalVarAccessNode::evaluateToNumber(ExecState* exec)
{
    return inlineEvaluate(exec)->toNumber(exec);
}

bool LocalVarAccessNode::evaluateToBoolean(ExecState* exec)
{
    return inlineEvaluate(exec)->toBoolean(exec);
}

int32_t LocalVarAccessNode::evaluateToInt32(ExecState* exec)
{
    return inlineEvaluate(exec)->toInt32(exec);
}

uint32_t LocalVarAccessNode::evaluateToUInt32(ExecState* exec)
{
    return inlineEvaluate(exec)->toUInt32(exec);
}

// ------------------------------ ElementNode ----------------------------------

void ElementNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    if (m_next)
        nodeStack.append(m_next.get());
    ASSERT(m_node);
    nodeStack.append(m_node.get());
}

// ECMA 11.1.4
JSValue* ElementNode::evaluate(ExecState* exec)
{
    JSObject* array = exec->lexicalGlobalObject()->arrayConstructor()->construct(exec, exec->emptyList());
    int length = 0;
    for (ElementNode* n = this; n; n = n->m_next.get()) {
        JSValue* val = n->m_node->evaluate(exec);
        KJS_CHECKEXCEPTIONVALUE
        length += n->m_elision;
        array->put(exec, length++, val);
    }
    return array;
}

// ------------------------------ ArrayNode ------------------------------------

void ArrayNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    if (m_element)
        nodeStack.append(m_element.get());
}

// ECMA 11.1.4
JSValue* ArrayNode::evaluate(ExecState* exec)
{
    JSObject* array;
    int length;

    if (m_element) {
        array = static_cast<JSObject*>(m_element->evaluate(exec));
        KJS_CHECKEXCEPTIONVALUE
        length = m_optional ? array->get(exec, exec->propertyNames().length)->toInt32(exec) : 0;
    } else {
        JSValue* newArr = exec->lexicalGlobalObject()->arrayConstructor()->construct(exec, exec->emptyList());
        array = static_cast<JSObject*>(newArr);
        length = 0;
    }

    if (m_optional)
        array->put(exec, exec->propertyNames().length, jsNumber(m_elision + length));

    return array;
}

// ------------------------------ ObjectLiteralNode ----------------------------

void ObjectLiteralNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    if (m_list)
        nodeStack.append(m_list.get());
}

// ECMA 11.1.5
JSValue* ObjectLiteralNode::evaluate(ExecState* exec)
{
    if (m_list)
        return m_list->evaluate(exec);

    return exec->lexicalGlobalObject()->objectConstructor()->construct(exec, exec->emptyList());
}

// ------------------------------ PropertyListNode -----------------------------

void PropertyListNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    if (m_next)
        nodeStack.append(m_next.get());
    nodeStack.append(m_node.get());
}

// ECMA 11.1.5
JSValue* PropertyListNode::evaluate(ExecState* exec)
{
    JSObject* obj = exec->lexicalGlobalObject()->objectConstructor()->construct(exec, exec->emptyList());

    for (PropertyListNode* p = this; p; p = p->m_next.get()) {
        JSValue* v = p->m_node->m_assign->evaluate(exec);
        KJS_CHECKEXCEPTIONVALUE

        switch (p->m_node->m_type) {
            case PropertyNode::Getter:
                ASSERT(v->isObject());
                obj->defineGetter(exec, p->m_node->name(), static_cast<JSObject* >(v));
                break;
            case PropertyNode::Setter:
                ASSERT(v->isObject());
                obj->defineSetter(exec, p->m_node->name(), static_cast<JSObject* >(v));
                break;
            case PropertyNode::Constant:
                obj->put(exec, p->m_node->name(), v);
                break;
        }
    }

    return obj;
}

// ------------------------------ PropertyNode -----------------------------

void PropertyNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_assign.get());
}

// ECMA 11.1.5
JSValue* PropertyNode::evaluate(ExecState*)
{
    ASSERT(false);
    return jsNull();
}

// ------------------------------ BracketAccessorNode --------------------------------

void BracketAccessorNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_subscript.get());
    nodeStack.append(m_base.get());
}

// ECMA 11.2.1a
JSValue* BracketAccessorNode::inlineEvaluate(ExecState* exec)
{
    JSValue* v1 = m_base->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE
    JSValue* v2 = m_subscript->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE
    JSObject* o = v1->toObject(exec);
    uint32_t i;
    if (v2->getUInt32(i))
        return o->get(exec, i);
    return o->get(exec, Identifier(v2->toString(exec)));
}

JSValue* BracketAccessorNode::evaluate(ExecState* exec)
{
    return inlineEvaluate(exec);
}

double BracketAccessorNode::evaluateToNumber(ExecState* exec)
{
    JSValue* v = inlineEvaluate(exec);
    KJS_CHECKEXCEPTIONNUMBER
    return v->toNumber(exec);
}

bool BracketAccessorNode::evaluateToBoolean(ExecState* exec)
{
    JSValue* v = inlineEvaluate(exec);
    KJS_CHECKEXCEPTIONBOOLEAN
    return v->toBoolean(exec);
}

int32_t BracketAccessorNode::evaluateToInt32(ExecState* exec)
{
    JSValue* v = inlineEvaluate(exec);
    KJS_CHECKEXCEPTIONNUMBER
    return v->toInt32(exec);
}

uint32_t BracketAccessorNode::evaluateToUInt32(ExecState* exec)
{
    JSValue* v = inlineEvaluate(exec);
    KJS_CHECKEXCEPTIONNUMBER
    return v->toUInt32(exec);
}

// ------------------------------ DotAccessorNode --------------------------------

void DotAccessorNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_base.get());
}

// ECMA 11.2.1b
JSValue* DotAccessorNode::inlineEvaluate(ExecState* exec)
{
    JSValue* v = m_base->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE
    return v->toObject(exec)->get(exec, m_ident);
}

JSValue* DotAccessorNode::evaluate(ExecState* exec)
{
    return inlineEvaluate(exec);
}

double DotAccessorNode::evaluateToNumber(ExecState* exec)
{
    JSValue* v = inlineEvaluate(exec);
    KJS_CHECKEXCEPTIONNUMBER
    return v->toNumber(exec);
}

bool DotAccessorNode::evaluateToBoolean(ExecState* exec)
{
    JSValue* v = inlineEvaluate(exec);
    KJS_CHECKEXCEPTIONBOOLEAN
    return v->toBoolean(exec);
}

int32_t DotAccessorNode::evaluateToInt32(ExecState* exec)
{
    JSValue* v = inlineEvaluate(exec);
    KJS_CHECKEXCEPTIONNUMBER
    return v->toInt32(exec);
}

uint32_t DotAccessorNode::evaluateToUInt32(ExecState* exec)
{
    JSValue* v = inlineEvaluate(exec);
    KJS_CHECKEXCEPTIONNUMBER
    return v->toUInt32(exec);
}

// ------------------------------ ArgumentListNode -----------------------------

void ArgumentListNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    if (m_next)
        nodeStack.append(m_next.get());
    ASSERT(m_expr);
    nodeStack.append(m_expr.get());
}

// ECMA 11.2.4
void ArgumentListNode::evaluateList(ExecState* exec, List& list)
{
    for (ArgumentListNode* n = this; n; n = n->m_next.get()) {
        JSValue* v = n->m_expr->evaluate(exec);
        KJS_CHECKEXCEPTIONVOID
        list.append(v);
    }
}

// ------------------------------ ArgumentsNode --------------------------------

void ArgumentsNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    if (m_listNode)
        nodeStack.append(m_listNode.get());
}

// ------------------------------ NewExprNode ----------------------------------

void NewExprNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    if (m_args)
        nodeStack.append(m_args.get());
    nodeStack.append(m_expr.get());
}

// ECMA 11.2.2

JSValue* NewExprNode::inlineEvaluate(ExecState* exec)
{
    JSValue* v = m_expr->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE

    List argList;
    if (m_args) {
        m_args->evaluateList(exec, argList);
        KJS_CHECKEXCEPTIONVALUE
    }

    if (!v->isObject())
        return throwError(exec, TypeError, "Value %s (result of expression %s) is not an object. Cannot be used with new.", v, m_expr.get());

    JSObject* constr = static_cast<JSObject*>(v);
    if (!constr->implementsConstruct())
        return throwError(exec, TypeError, "Value %s (result of expression %s) is not a constructor. Cannot be used with new.", v, m_expr.get());

    return constr->construct(exec, argList);
}

JSValue* NewExprNode::evaluate(ExecState* exec)
{
    return inlineEvaluate(exec);
}

double NewExprNode::evaluateToNumber(ExecState* exec)
{
    JSValue* v = inlineEvaluate(exec);
    KJS_CHECKEXCEPTIONNUMBER
    return v->toNumber(exec);
}

bool NewExprNode::evaluateToBoolean(ExecState* exec)
{
    JSValue* v = inlineEvaluate(exec);
    KJS_CHECKEXCEPTIONBOOLEAN
    return v->toBoolean(exec);
}

int32_t NewExprNode::evaluateToInt32(ExecState* exec)
{
    JSValue* v = inlineEvaluate(exec);
    KJS_CHECKEXCEPTIONNUMBER
    return v->toInt32(exec);
}

uint32_t NewExprNode::evaluateToUInt32(ExecState* exec)
{
    JSValue* v = inlineEvaluate(exec);
    KJS_CHECKEXCEPTIONNUMBER
    return v->toUInt32(exec);
}

void FunctionCallValueNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_args.get());
    nodeStack.append(m_expr.get());
}

// ECMA 11.2.3
JSValue* FunctionCallValueNode::evaluate(ExecState* exec)
{
    JSValue* v = m_expr->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE

    if (!v->isObject()) {
        return throwError(exec, TypeError, "Value %s (result of expression %s) is not object.", v, m_expr.get());
    }

    JSObject* func = static_cast<JSObject*>(v);

    if (!func->implementsCall()) {
        return throwError(exec, TypeError, "Object %s (result of expression %s) does not allow calls.", v, m_expr.get());
    }

    List argList;
    m_args->evaluateList(exec, argList);
    KJS_CHECKEXCEPTIONVALUE

    JSObject* thisObj =  exec->dynamicGlobalObject();

    return func->call(exec, thisObj, argList);
}

void FunctionCallResolveNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_args.get());

    size_t index = symbolTable.get(m_ident.ustring().rep());
    if (index != missingSymbolMarker())
        new (this) LocalVarFunctionCallNode(index);
}

// ECMA 11.2.3
JSValue* FunctionCallResolveNode::inlineEvaluate(ExecState* exec)
{
    // Check for missed optimization opportunity.
    ASSERT(!canSkipLookup(exec, m_ident));

    const ScopeChain& chain = exec->scopeChain();
    ScopeChainIterator iter = chain.begin();
    ScopeChainIterator end = chain.end();

    // we must always have something in the scope chain
    ASSERT(iter != end);

    PropertySlot slot;
    JSObject* base;
    do {
        base = *iter;
        if (base->getPropertySlot(exec, m_ident, slot)) {
            JSValue* v = slot.getValue(exec, base, m_ident);
            KJS_CHECKEXCEPTIONVALUE

            if (!v->isObject())
                return throwError(exec, TypeError, "Value %s (result of expression %s) is not object.", v, m_ident);

            JSObject* func = static_cast<JSObject*>(v);

            if (!func->implementsCall())
                return throwError(exec, TypeError, "Object %s (result of expression %s) does not allow calls.", v, m_ident);

            List argList;
            m_args->evaluateList(exec, argList);
            KJS_CHECKEXCEPTIONVALUE

            JSObject* thisObj = base;
            // ECMA 11.2.3 says that in this situation 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.
            if (thisObj->isActivationObject())
                thisObj = exec->dynamicGlobalObject();

            return func->call(exec, thisObj, argList);
        }
        ++iter;
    } while (iter != end);

    return throwUndefinedVariableError(exec, m_ident);
}

JSValue* FunctionCallResolveNode::evaluate(ExecState* exec)
{
    return inlineEvaluate(exec);
}

double FunctionCallResolveNode::evaluateToNumber(ExecState* exec)
{
    JSValue* v = inlineEvaluate(exec);
    KJS_CHECKEXCEPTIONNUMBER
    return v->toNumber(exec);
}

bool FunctionCallResolveNode::evaluateToBoolean(ExecState* exec)
{
    JSValue* v = inlineEvaluate(exec);
    KJS_CHECKEXCEPTIONBOOLEAN
    return v->toBoolean(exec);
}

int32_t FunctionCallResolveNode::evaluateToInt32(ExecState* exec)
{
    JSValue* v = inlineEvaluate(exec);
    KJS_CHECKEXCEPTIONNUMBER
    return v->toInt32(exec);
}

uint32_t FunctionCallResolveNode::evaluateToUInt32(ExecState* exec)
{
    JSValue* v = inlineEvaluate(exec);
    KJS_CHECKEXCEPTIONNUMBER
    return v->toUInt32(exec);
}

JSValue* LocalVarFunctionCallNode::inlineEvaluate(ExecState* exec)
{
    ASSERT(exec->variableObject() == exec->scopeChain().top());

    JSValue* v = exec->localStorage()[m_index].value;

    if (!v->isObject())
        return throwError(exec, TypeError, "Value %s (result of expression %s) is not object.", v, m_ident);

    JSObject* func = static_cast<JSObject*>(v);
    if (!func->implementsCall())
        return throwError(exec, TypeError, "Object %s (result of expression %s) does not allow calls.", v, m_ident);

    List argList;
    m_args->evaluateList(exec, argList);
    KJS_CHECKEXCEPTIONVALUE

    return func->call(exec, exec->dynamicGlobalObject(), argList);
}

JSValue* LocalVarFunctionCallNode::evaluate(ExecState* exec)
{
    return inlineEvaluate(exec);
}

double LocalVarFunctionCallNode::evaluateToNumber(ExecState* exec)
{
    JSValue* v = inlineEvaluate(exec);
    KJS_CHECKEXCEPTIONNUMBER
    return v->toNumber(exec);
}

bool LocalVarFunctionCallNode::evaluateToBoolean(ExecState* exec)
{
    JSValue* v = inlineEvaluate(exec);
    KJS_CHECKEXCEPTIONBOOLEAN
    return v->toBoolean(exec);
}

int32_t LocalVarFunctionCallNode::evaluateToInt32(ExecState* exec)
{
    JSValue* v = inlineEvaluate(exec);
    KJS_CHECKEXCEPTIONNUMBER
    return v->toInt32(exec);
}

uint32_t LocalVarFunctionCallNode::evaluateToUInt32(ExecState* exec)
{
    JSValue* v = inlineEvaluate(exec);
    KJS_CHECKEXCEPTIONNUMBER
    return v->toUInt32(exec);
}

void FunctionCallBracketNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_args.get());
    nodeStack.append(m_subscript.get());
    nodeStack.append(m_base.get());
}

// ECMA 11.2.3
JSValue* FunctionCallBracketNode::evaluate(ExecState* exec)
{
    JSValue* baseVal = m_base->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE

    JSValue* subscriptVal = m_subscript->evaluate(exec);

    JSObject* baseObj = baseVal->toObject(exec);
    uint32_t i;
    PropertySlot slot;

    JSValue* funcVal;
    if (subscriptVal->getUInt32(i)) {
        if (baseObj->getPropertySlot(exec, i, slot))
            funcVal = slot.getValue(exec, baseObj, i);
        else
            funcVal = jsUndefined();
    } else {
        Identifier ident(subscriptVal->toString(exec));
        if (baseObj->getPropertySlot(exec, ident, slot))
            funcVal = baseObj->get(exec, ident);
        else
            funcVal = jsUndefined();
    }

    KJS_CHECKEXCEPTIONVALUE

    if (!funcVal->isObject())
        return throwError(exec, TypeError, "Value %s (result of expression %s[%s]) is not object.", funcVal, m_base.get(), m_subscript.get());

    JSObject* func = static_cast<JSObject*>(funcVal);

    if (!func->implementsCall())
        return throwError(exec, TypeError, "Object %s (result of expression %s[%s]) does not allow calls.", funcVal, m_base.get(), m_subscript.get());

    List argList;
    m_args->evaluateList(exec, argList);
    KJS_CHECKEXCEPTIONVALUE

    JSObject* thisObj = baseObj;
    ASSERT(thisObj);
    ASSERT(thisObj->isObject());
    ASSERT(!thisObj->isActivationObject());

    return func->call(exec, thisObj, argList);
}

static const char* dotExprNotAnObjectString() KJS_FAST_CALL;
static const char* dotExprNotAnObjectString()
{
    return "Value %s (result of expression %s.%s) is not object.";
}

static const char* dotExprDoesNotAllowCallsString() KJS_FAST_CALL;
static const char* dotExprDoesNotAllowCallsString()
{
    return "Object %s (result of expression %s.%s) does not allow calls.";
}

void FunctionCallDotNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_args.get());
    nodeStack.append(m_base.get());
}

// ECMA 11.2.3
JSValue* FunctionCallDotNode::inlineEvaluate(ExecState* exec)
{
    JSValue* baseVal = m_base->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE

    JSObject* baseObj = baseVal->toObject(exec);
    PropertySlot slot;
    JSValue* funcVal = baseObj->getPropertySlot(exec, m_ident, slot) ? slot.getValue(exec, baseObj, m_ident) : jsUndefined();
    KJS_CHECKEXCEPTIONVALUE

    if (!funcVal->isObject())
        return throwError(exec, TypeError, dotExprNotAnObjectString(), funcVal, m_base.get(), m_ident);

    JSObject* func = static_cast<JSObject*>(funcVal);

    if (!func->implementsCall())
        return throwError(exec, TypeError, dotExprDoesNotAllowCallsString(), funcVal, m_base.get(), m_ident);

    List argList;
    m_args->evaluateList(exec, argList);
    KJS_CHECKEXCEPTIONVALUE

    JSObject* thisObj = baseObj;
    ASSERT(thisObj);
    ASSERT(thisObj->isObject());
    ASSERT(!thisObj->isActivationObject());

    return func->call(exec, thisObj, argList);
}

JSValue* FunctionCallDotNode::evaluate(ExecState* exec)
{
    return inlineEvaluate(exec);
}

double FunctionCallDotNode::evaluateToNumber(ExecState* exec)
{
    JSValue* v = inlineEvaluate(exec);
    KJS_CHECKEXCEPTIONNUMBER
    return v->toNumber(exec);
}

bool FunctionCallDotNode::evaluateToBoolean(ExecState* exec)
{
    JSValue* v = inlineEvaluate(exec);
    KJS_CHECKEXCEPTIONBOOLEAN
    return v->toBoolean(exec);
}

int32_t FunctionCallDotNode::evaluateToInt32(ExecState* exec)
{
    JSValue* v = inlineEvaluate(exec);
    KJS_CHECKEXCEPTIONNUMBER
    return v->toInt32(exec);
}

uint32_t FunctionCallDotNode::evaluateToUInt32(ExecState* exec)
{
    JSValue* v = inlineEvaluate(exec);
    KJS_CHECKEXCEPTIONNUMBER
    return v->toUInt32(exec);
}

// ECMA 11.3

// ------------------------------ PostfixResolveNode ----------------------------------

// Increment
void PostIncResolveNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage& localStorage, NodeStack&)
{
    size_t index = symbolTable.get(m_ident.ustring().rep());
    if (index != missingSymbolMarker()) {
        if (isConstant(localStorage, index))
            new (this) PostIncConstNode(index);
        else
            new (this) PostIncLocalVarNode(index);
    }
}

JSValue* PostIncResolveNode::evaluate(ExecState* exec)
{
    // Check for missed optimization opportunity.
    ASSERT(!canSkipLookup(exec, m_ident));

    const ScopeChain& chain = exec->scopeChain();
    ScopeChainIterator iter = chain.begin();
    ScopeChainIterator end = chain.end();

    // we must always have something in the scope chain
    ASSERT(iter != end);

    PropertySlot slot;
    do {
        if ((*iter)->getPropertySlot(exec, m_ident, slot)) {
            // If m_ident is 'arguments', the base->getPropertySlot() may cause 
            // base (which must be an ActivationImp in such this case) to be torn
            // off from the activation stack, in which case we need to get it again
            // from the ScopeChainIterator.
            
            JSObject* base = *iter;
            JSValue* v = slot.getValue(exec, base, m_ident)->toJSNumber(exec);
            base->put(exec, m_ident, jsNumber(v->toNumber(exec) + 1));
            return v;
        }

        ++iter;
    } while (iter != end);

    return throwUndefinedVariableError(exec, m_ident);
}

void PostIncResolveNode::optimizeForUnnecessaryResult()
{
    new (this) PreIncResolveNode(PlacementNewAdopt);
}

JSValue* PostIncLocalVarNode::evaluate(ExecState* exec)
{
    ASSERT(exec->variableObject() == exec->scopeChain().top());

    JSValue** slot = &exec->localStorage()[m_index].value;
    JSValue* v = (*slot)->toJSNumber(exec);
    *slot = jsNumber(v->toNumber(exec) + 1);
    return v;
}

void PostIncLocalVarNode::optimizeForUnnecessaryResult()
{
    new (this) PreIncLocalVarNode(m_index);
}

// Decrement
void PostDecResolveNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage& localStorage, NodeStack&)
{
    size_t index = symbolTable.get(m_ident.ustring().rep());
    if (index != missingSymbolMarker()) {
        if (isConstant(localStorage, index))
            new (this) PostDecConstNode(index);
        else
            new (this) PostDecLocalVarNode(index);
    }
}

JSValue* PostDecResolveNode::evaluate(ExecState* exec)
{
    // Check for missed optimization opportunity.
    ASSERT(!canSkipLookup(exec, m_ident));

    const ScopeChain& chain = exec->scopeChain();
    ScopeChainIterator iter = chain.begin();
    ScopeChainIterator end = chain.end();

    // we must always have something in the scope chain
    ASSERT(iter != end);

    PropertySlot slot;
    do {
        if ((*iter)->getPropertySlot(exec, m_ident, slot)) {
            // See the comment in PostIncResolveNode::evaluate().

            JSObject* base = *iter;
            JSValue* v = slot.getValue(exec, base, m_ident)->toJSNumber(exec);
            base->put(exec, m_ident, jsNumber(v->toNumber(exec) - 1));
            return v;
        }

        ++iter;
    } while (iter != end);

    return throwUndefinedVariableError(exec, m_ident);
}

void PostDecResolveNode::optimizeForUnnecessaryResult()
{
    new (this) PreDecResolveNode(PlacementNewAdopt);
}

JSValue* PostDecLocalVarNode::evaluate(ExecState* exec)
{
    ASSERT(exec->variableObject() == exec->scopeChain().top());

    JSValue** slot = &exec->localStorage()[m_index].value;
    JSValue* v = (*slot)->toJSNumber(exec);
    *slot = jsNumber(v->toNumber(exec) - 1);
    return v;
}

double PostDecLocalVarNode::inlineEvaluateToNumber(ExecState* exec)
{
    ASSERT(exec->variableObject() == exec->scopeChain().top());

    JSValue** slot = &exec->localStorage()[m_index].value;
    double n = (*slot)->toNumber(exec);
    *slot = jsNumber(n - 1);
    return n;
}

double PostDecLocalVarNode::evaluateToNumber(ExecState* exec)
{
    return inlineEvaluateToNumber(exec);
}

bool PostDecLocalVarNode::evaluateToBoolean(ExecState* exec)
{
    double result = inlineEvaluateToNumber(exec);
    return  result > 0.0 || 0.0 > result; // NaN produces false as well
}

int32_t PostDecLocalVarNode::evaluateToInt32(ExecState* exec)
{
    return JSValue::toInt32(inlineEvaluateToNumber(exec));
}

uint32_t PostDecLocalVarNode::evaluateToUInt32(ExecState* exec)
{
    return JSValue::toUInt32(inlineEvaluateToNumber(exec));
}

void PostDecLocalVarNode::optimizeForUnnecessaryResult()
{
    new (this) PreDecLocalVarNode(m_index);
}

// ------------------------------ PostfixBracketNode ----------------------------------

void PostfixBracketNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_subscript.get());
    nodeStack.append(m_base.get());
}

JSValue* PostIncBracketNode::evaluate(ExecState* exec)
{
    JSValue* baseValue = m_base->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE
    JSValue* subscript = m_subscript->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE

    JSObject* base = baseValue->toObject(exec);

    uint32_t propertyIndex;
    if (subscript->getUInt32(propertyIndex)) {
        PropertySlot slot;
        JSValue* v = base->getPropertySlot(exec, propertyIndex, slot) ? slot.getValue(exec, base, propertyIndex) : jsUndefined();
        KJS_CHECKEXCEPTIONVALUE

        JSValue* v2 = v->toJSNumber(exec);
        base->put(exec, propertyIndex, jsNumber(v2->toNumber(exec) + 1));

        return v2;
    }

    Identifier propertyName(subscript->toString(exec));
    PropertySlot slot;
    JSValue* v = base->getPropertySlot(exec, propertyName, slot) ? slot.getValue(exec, base, propertyName) : jsUndefined();
    KJS_CHECKEXCEPTIONVALUE

    JSValue* v2 = v->toJSNumber(exec);
    base->put(exec, propertyName, jsNumber(v2->toNumber(exec) + 1));
    return v2;
}

JSValue* PostDecBracketNode::evaluate(ExecState* exec)
{
    JSValue* baseValue = m_base->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE
    JSValue* subscript = m_subscript->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE

    JSObject* base = baseValue->toObject(exec);

    uint32_t propertyIndex;
    if (subscript->getUInt32(propertyIndex)) {
        PropertySlot slot;
        JSValue* v = base->getPropertySlot(exec, propertyIndex, slot) ? slot.getValue(exec, base, propertyIndex) : jsUndefined();
        KJS_CHECKEXCEPTIONVALUE

        JSValue* v2 = v->toJSNumber(exec);
        base->put(exec, propertyIndex, jsNumber(v2->toNumber(exec) - 1));
        return v2;
    }

    Identifier propertyName(subscript->toString(exec));
    PropertySlot slot;
    JSValue* v = base->getPropertySlot(exec, propertyName, slot) ? slot.getValue(exec, base, propertyName) : jsUndefined();
    KJS_CHECKEXCEPTIONVALUE

    JSValue* v2 = v->toJSNumber(exec);
    base->put(exec, propertyName, jsNumber(v2->toNumber(exec) - 1));
    return v2;
}

// ------------------------------ PostfixDotNode ----------------------------------

void PostfixDotNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_base.get());
}

JSValue* PostIncDotNode::evaluate(ExecState* exec)
{
    JSValue* baseValue = m_base->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE
    JSObject* base = baseValue->toObject(exec);

    PropertySlot slot;
    JSValue* v = base->getPropertySlot(exec, m_ident, slot) ? slot.getValue(exec, base, m_ident) : jsUndefined();
    KJS_CHECKEXCEPTIONVALUE

    JSValue* v2 = v->toJSNumber(exec);
    base->put(exec, m_ident, jsNumber(v2->toNumber(exec) + 1));
    return v2;
}

JSValue* PostDecDotNode::evaluate(ExecState* exec)
{
    JSValue* baseValue = m_base->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE
    JSObject* base = baseValue->toObject(exec);

    PropertySlot slot;
    JSValue* v = base->getPropertySlot(exec, m_ident, slot) ? slot.getValue(exec, base, m_ident) : jsUndefined();
    KJS_CHECKEXCEPTIONVALUE

    JSValue* v2 = v->toJSNumber(exec);
    base->put(exec, m_ident, jsNumber(v2->toNumber(exec) - 1));
    return v2;
}

// ------------------------------ PostfixErrorNode -----------------------------------

JSValue* PostfixErrorNode::evaluate(ExecState* exec)
{
    throwError(exec, ReferenceError, "Postfix %s operator applied to value that is not a reference.",
               m_operator == OpPlusPlus ? "++" : "--");
    handleException(exec);
    return jsUndefined();
}

// ------------------------------ DeleteResolveNode -----------------------------------

void DeleteResolveNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage&, NodeStack&)
{
    size_t index = symbolTable.get(m_ident.ustring().rep());
    if (index != missingSymbolMarker())
        new (this) LocalVarDeleteNode();
}

// ECMA 11.4.1

JSValue* DeleteResolveNode::evaluate(ExecState* exec)
{
    // Check for missed optimization opportunity.
    ASSERT(!canSkipLookup(exec, m_ident));

    const ScopeChain& chain = exec->scopeChain();
    ScopeChainIterator iter = chain.begin();
    ScopeChainIterator end = chain.end();

    // We must always have something in the scope chain
    ASSERT(iter != end);

    PropertySlot slot;
    JSObject* base;
    do {
        base = *iter;
        if (base->getPropertySlot(exec, m_ident, slot))
            return jsBoolean(base->deleteProperty(exec, m_ident));

        ++iter;
    } while (iter != end);

    return jsBoolean(true);
}

JSValue* LocalVarDeleteNode::evaluate(ExecState*)
{
    return jsBoolean(false);
}

// ------------------------------ DeleteBracketNode -----------------------------------

void DeleteBracketNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_subscript.get());
    nodeStack.append(m_base.get());
}

JSValue* DeleteBracketNode::evaluate(ExecState* exec)
{
    JSValue* baseValue = m_base->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE
    JSValue* subscript = m_subscript->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE

    JSObject* base = baseValue->toObject(exec);

    uint32_t propertyIndex;
    if (subscript->getUInt32(propertyIndex))
        return jsBoolean(base->deleteProperty(exec, propertyIndex));

    Identifier propertyName(subscript->toString(exec));
    return jsBoolean(base->deleteProperty(exec, propertyName));
}

// ------------------------------ DeleteDotNode -----------------------------------

void DeleteDotNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_base.get());
}

JSValue* DeleteDotNode::evaluate(ExecState* exec)
{
    JSValue* baseValue = m_base->evaluate(exec);
    JSObject* base = baseValue->toObject(exec);
    KJS_CHECKEXCEPTIONVALUE

    return jsBoolean(base->deleteProperty(exec, m_ident));
}

// ------------------------------ DeleteValueNode -----------------------------------

void DeleteValueNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_expr.get());
}

JSValue* DeleteValueNode::evaluate(ExecState* exec)
{
    m_expr->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE

    // delete on a non-location expression ignores the value and returns true
    return jsBoolean(true);
}

// ------------------------------ VoidNode -------------------------------------

void VoidNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_expr.get());
}

// ECMA 11.4.2
JSValue* VoidNode::evaluate(ExecState* exec)
{
    m_expr->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE

    return jsUndefined();
}

// ECMA 11.4.3

// ------------------------------ TypeOfValueNode -----------------------------------

void TypeOfValueNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_expr.get());
}

static JSValue* typeStringForValue(JSValue* v) KJS_FAST_CALL;
static JSValue* typeStringForValue(JSValue* v)
{
    switch (v->type()) {
        case UndefinedType:
            return jsString("undefined");
        case NullType:
            return jsString("object");
        case BooleanType:
            return jsString("boolean");
        case NumberType:
            return jsString("number");
        case StringType:
            return jsString("string");
        default:
            if (v->isObject()) {
                // Return "undefined" for objects that should be treated
                // as null when doing comparisons.
                if (static_cast<JSObject*>(v)->masqueradeAsUndefined())
                    return jsString("undefined");
                else if (static_cast<JSObject*>(v)->implementsCall())
                    return jsString("function");
            }

            return jsString("object");
    }
}

void TypeOfResolveNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage&, NodeStack&)
{
    size_t index = symbolTable.get(m_ident.ustring().rep());
    if (index != missingSymbolMarker())
        new (this) LocalVarTypeOfNode(index);
}

JSValue* LocalVarTypeOfNode::evaluate(ExecState* exec)
{
    ASSERT(exec->variableObject() == exec->scopeChain().top());

    return typeStringForValue(exec->localStorage()[m_index].value);
}

JSValue* TypeOfResolveNode::evaluate(ExecState* exec)
{
    const ScopeChain& chain = exec->scopeChain();
    ScopeChainIterator iter = chain.begin();
    ScopeChainIterator end = chain.end();

    // We must always have something in the scope chain
    ASSERT(iter != end);

    PropertySlot slot;
    JSObject* base;
    do {
        base = *iter;
        if (base->getPropertySlot(exec, m_ident, slot)) {
            JSValue* v = slot.getValue(exec, base, m_ident);
            return typeStringForValue(v);
        }

        ++iter;
    } while (iter != end);

    return jsString("undefined");
}

// ------------------------------ TypeOfValueNode -----------------------------------

JSValue* TypeOfValueNode::evaluate(ExecState* exec)
{
    JSValue* v = m_expr->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE

    return typeStringForValue(v);
}

// ECMA 11.4.4 and 11.4.5

// ------------------------------ PrefixResolveNode ----------------------------------

void PreIncResolveNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage& localStorage, NodeStack&)
{
    size_t index = symbolTable.get(m_ident.ustring().rep());
    if (index != missingSymbolMarker()) {
        if (isConstant(localStorage, index))
            new (this) PreIncConstNode(index);
        else
            new (this) PreIncLocalVarNode(index);
    }
}

JSValue* PreIncLocalVarNode::evaluate(ExecState* exec)
{
    ASSERT(exec->variableObject() == exec->scopeChain().top());
    JSValue** slot = &exec->localStorage()[m_index].value;

    double n = (*slot)->toNumber(exec);
    JSValue* n2 = jsNumber(n + 1);
    *slot = n2;
    return n2;
}

JSValue* PreIncResolveNode::evaluate(ExecState* exec)
{
    const ScopeChain& chain = exec->scopeChain();
    ScopeChainIterator iter = chain.begin();
    ScopeChainIterator end = chain.end();

    // we must always have something in the scope chain
    ASSERT(iter != end);

    PropertySlot slot;
    do {
        if ((*iter)->getPropertySlot(exec, m_ident, slot)) {
            // See the comment in PostIncResolveNode::evaluate().

            JSObject* base = *iter;
            JSValue* v = slot.getValue(exec, base, m_ident);

            double n = v->toNumber(exec);
            JSValue* n2 = jsNumber(n + 1);
            base->put(exec, m_ident, n2);

            return n2;
        }

        ++iter;
    } while (iter != end);

    return throwUndefinedVariableError(exec, m_ident);
}

void PreDecResolveNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage& localStorage, NodeStack&)
{
    size_t index = symbolTable.get(m_ident.ustring().rep());
    if (index != missingSymbolMarker()) {
        if (isConstant(localStorage, index))
            new (this) PreDecConstNode(index);
        else
            new (this) PreDecLocalVarNode(index);
    }
}

JSValue* PreDecLocalVarNode::evaluate(ExecState* exec)
{
    ASSERT(exec->variableObject() == exec->scopeChain().top());
    JSValue** slot = &exec->localStorage()[m_index].value;

    double n = (*slot)->toNumber(exec);
    JSValue* n2 = jsNumber(n - 1);
    *slot = n2;
    return n2;
}

JSValue* PreDecResolveNode::evaluate(ExecState* exec)
{
    const ScopeChain& chain = exec->scopeChain();
    ScopeChainIterator iter = chain.begin();
    ScopeChainIterator end = chain.end();

    // we must always have something in the scope chain
    ASSERT(iter != end);

    PropertySlot slot;
    do {
        if ((*iter)->getPropertySlot(exec, m_ident, slot)) {
            // See the comment in PostIncResolveNode::evaluate().

            JSObject* base = *iter;
            JSValue* v = slot.getValue(exec, base, m_ident);

            double n = v->toNumber(exec);
            JSValue* n2 = jsNumber(n - 1);
            base->put(exec, m_ident, n2);

            return n2;
        }

        ++iter;
    } while (iter != end);

    return throwUndefinedVariableError(exec, m_ident);
}

// ------------------------------ PreIncConstNode ----------------------------------

JSValue* PreIncConstNode::evaluate(ExecState* exec)
{
    ASSERT(exec->variableObject() == exec->scopeChain().top());
    return jsNumber(exec->localStorage()[m_index].value->toNumber(exec) + 1);
}

// ------------------------------ PreDecConstNode ----------------------------------

JSValue* PreDecConstNode::evaluate(ExecState* exec)
{
    ASSERT(exec->variableObject() == exec->scopeChain().top());
    return jsNumber(exec->localStorage()[m_index].value->toNumber(exec) - 1);
}

// ------------------------------ PostIncConstNode ----------------------------------

JSValue* PostIncConstNode::evaluate(ExecState* exec)
{
    ASSERT(exec->variableObject() == exec->scopeChain().top());
    return jsNumber(exec->localStorage()[m_index].value->toNumber(exec));
}

// ------------------------------ PostDecConstNode ----------------------------------

JSValue* PostDecConstNode::evaluate(ExecState* exec)
{
    ASSERT(exec->variableObject() == exec->scopeChain().top());
    return jsNumber(exec->localStorage()[m_index].value->toNumber(exec));
}

// ------------------------------ PrefixBracketNode ----------------------------------

void PrefixBracketNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_subscript.get());
    nodeStack.append(m_base.get());
}

JSValue* PreIncBracketNode::evaluate(ExecState* exec)
{
    JSValue* baseValue = m_base->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE
    JSValue* subscript = m_subscript->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE

    JSObject* base = baseValue->toObject(exec);

    uint32_t propertyIndex;
    if (subscript->getUInt32(propertyIndex)) {
        PropertySlot slot;
        JSValue* v = base->getPropertySlot(exec, propertyIndex, slot) ? slot.getValue(exec, base, propertyIndex) : jsUndefined();
        KJS_CHECKEXCEPTIONVALUE

        JSValue* n2 = jsNumber(v->toNumber(exec) + 1);
        base->put(exec, propertyIndex, n2);

        return n2;
    }

    Identifier propertyName(subscript->toString(exec));
    PropertySlot slot;
    JSValue* v = base->getPropertySlot(exec, propertyName, slot) ? slot.getValue(exec, base, propertyName) : jsUndefined();
    KJS_CHECKEXCEPTIONVALUE

    JSValue* n2 = jsNumber(v->toNumber(exec) + 1);
    base->put(exec, propertyName, n2);

    return n2;
}

JSValue* PreDecBracketNode::evaluate(ExecState* exec)
{
    JSValue* baseValue = m_base->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE
    JSValue* subscript = m_subscript->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE

    JSObject* base = baseValue->toObject(exec);

    uint32_t propertyIndex;
    if (subscript->getUInt32(propertyIndex)) {
        PropertySlot slot;
        JSValue* v = base->getPropertySlot(exec, propertyIndex, slot) ? slot.getValue(exec, base, propertyIndex) : jsUndefined();
        KJS_CHECKEXCEPTIONVALUE

        JSValue* n2 = jsNumber(v->toNumber(exec) - 1);
        base->put(exec, propertyIndex, n2);

        return n2;
    }

    Identifier propertyName(subscript->toString(exec));
    PropertySlot slot;
    JSValue* v = base->getPropertySlot(exec, propertyName, slot) ? slot.getValue(exec, base, propertyName) : jsUndefined();
    KJS_CHECKEXCEPTIONVALUE

    JSValue* n2 = jsNumber(v->toNumber(exec) - 1);
    base->put(exec, propertyName, n2);

    return n2;
}

// ------------------------------ PrefixDotNode ----------------------------------

void PrefixDotNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_base.get());
}

JSValue* PreIncDotNode::evaluate(ExecState* exec)
{
    JSValue* baseValue = m_base->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE
    JSObject* base = baseValue->toObject(exec);

    PropertySlot slot;
    JSValue* v = base->getPropertySlot(exec, m_ident, slot) ? slot.getValue(exec, base, m_ident) : jsUndefined();
    KJS_CHECKEXCEPTIONVALUE

    double n = v->toNumber(exec);
    JSValue* n2 = jsNumber(n + 1);
    base->put(exec, m_ident, n2);

    return n2;
}

JSValue* PreDecDotNode::evaluate(ExecState* exec)
{
    JSValue* baseValue = m_base->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE
    JSObject* base = baseValue->toObject(exec);

    PropertySlot slot;
    JSValue* v = base->getPropertySlot(exec, m_ident, slot) ? slot.getValue(exec, base, m_ident) : jsUndefined();
    KJS_CHECKEXCEPTIONVALUE

    double n = v->toNumber(exec);
    JSValue* n2 = jsNumber(n - 1);
    base->put(exec, m_ident, n2);

    return n2;
}

// ------------------------------ PrefixErrorNode -----------------------------------

JSValue* PrefixErrorNode::evaluate(ExecState* exec)
{
    throwError(exec, ReferenceError, "Prefix %s operator applied to value that is not a reference.",
               m_operator == OpPlusPlus ? "++" : "--");
    handleException(exec);
    return jsUndefined();
}

// ------------------------------ UnaryPlusNode --------------------------------

void UnaryPlusNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_expr.get());
}

// ECMA 11.4.6
JSValue* UnaryPlusNode::evaluate(ExecState* exec)
{
    JSValue* v = m_expr->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE
    return v->toJSNumber(exec);
}

bool UnaryPlusNode::evaluateToBoolean(ExecState* exec)
{
    return m_expr->evaluateToBoolean(exec);
}

double UnaryPlusNode::evaluateToNumber(ExecState* exec)
{
    return m_expr->evaluateToNumber(exec);
}

int32_t UnaryPlusNode::evaluateToInt32(ExecState* exec)
{
    return m_expr->evaluateToInt32(exec);
}

uint32_t UnaryPlusNode::evaluateToUInt32(ExecState* exec)
{
    return m_expr->evaluateToInt32(exec);
}

// ------------------------------ NegateNode -----------------------------------

void NegateNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_expr.get());
}

// ECMA 11.4.7
JSValue* NegateNode::evaluate(ExecState* exec)
{
    // No need to check exception, caller will do so right after evaluate()
    return jsNumber(-m_expr->evaluateToNumber(exec));
}

double NegateNode::evaluateToNumber(ExecState* exec)
{
    // No need to check exception, caller will do so right after evaluateToNumber()
    return -m_expr->evaluateToNumber(exec);
}

// ------------------------------ BitwiseNotNode -------------------------------

void BitwiseNotNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_expr.get());
}

// ECMA 11.4.8
int32_t BitwiseNotNode::inlineEvaluateToInt32(ExecState* exec)
{
    return ~m_expr->evaluateToInt32(exec);
}

JSValue* BitwiseNotNode::evaluate(ExecState* exec)
{
    return jsNumber(inlineEvaluateToInt32(exec));
}

double BitwiseNotNode::evaluateToNumber(ExecState* exec)
{
    return inlineEvaluateToInt32(exec);
}

bool BitwiseNotNode::evaluateToBoolean(ExecState* exec)
{
    return inlineEvaluateToInt32(exec);
}

int32_t BitwiseNotNode::evaluateToInt32(ExecState* exec)
{
    return inlineEvaluateToInt32(exec);
}

uint32_t BitwiseNotNode::evaluateToUInt32(ExecState* exec)
{
    return inlineEvaluateToInt32(exec);
}

// ------------------------------ LogicalNotNode -------------------------------

void LogicalNotNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_expr.get());
}

// ECMA 11.4.9
JSValue* LogicalNotNode::evaluate(ExecState* exec)
{
    return jsBoolean(!m_expr->evaluateToBoolean(exec));
}

bool LogicalNotNode::evaluateToBoolean(ExecState* exec)
{
    return !m_expr->evaluateToBoolean(exec);
}

// ------------------------------ Multiplicative Nodes -----------------------------------

void MultNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_term1.get());
    nodeStack.append(m_term2.get());
}

// ECMA 11.5.1
double MultNode::inlineEvaluateToNumber(ExecState* exec)
{
    double n1 = m_term1->evaluateToNumber(exec);
    KJS_CHECKEXCEPTIONNUMBER
    double n2 = m_term2->evaluateToNumber(exec);
    return n1 * n2;
}

JSValue* MultNode::evaluate(ExecState* exec)
{
    return jsNumber(inlineEvaluateToNumber(exec));
}

double MultNode::evaluateToNumber(ExecState* exec)
{
    return inlineEvaluateToNumber(exec);
}

bool MultNode::evaluateToBoolean(ExecState* exec)
{
    double result = inlineEvaluateToNumber(exec);
    return  result > 0.0 || 0.0 > result; // NaN produces false as well
}

int32_t MultNode::evaluateToInt32(ExecState* exec)
{
    return JSValue::toInt32(inlineEvaluateToNumber(exec));
}

uint32_t MultNode::evaluateToUInt32(ExecState* exec)
{
    return JSValue::toUInt32(inlineEvaluateToNumber(exec));
}

void DivNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_term1.get());
    nodeStack.append(m_term2.get());
}

// ECMA 11.5.2
double DivNode::inlineEvaluateToNumber(ExecState* exec)
{
    double n1 = m_term1->evaluateToNumber(exec);
    KJS_CHECKEXCEPTIONNUMBER
    double n2 = m_term2->evaluateToNumber(exec);
    return n1 / n2;
}

JSValue* DivNode::evaluate(ExecState* exec)
{
    return jsNumber(inlineEvaluateToNumber(exec));
}

double DivNode::evaluateToNumber(ExecState* exec)
{
    return inlineEvaluateToNumber(exec);
}

int32_t DivNode::evaluateToInt32(ExecState* exec)
{
    return JSValue::toInt32(inlineEvaluateToNumber(exec));
}

uint32_t DivNode::evaluateToUInt32(ExecState* exec)
{
    return JSValue::toUInt32(inlineEvaluateToNumber(exec));
}

void ModNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_term1.get());
    nodeStack.append(m_term2.get());
}

// ECMA 11.5.3
double ModNode::inlineEvaluateToNumber(ExecState* exec)
{
    double n1 = m_term1->evaluateToNumber(exec);
    KJS_CHECKEXCEPTIONNUMBER
    double n2 = m_term2->evaluateToNumber(exec);
    return fmod(n1, n2);
}

JSValue* ModNode::evaluate(ExecState* exec)
{
    return jsNumber(inlineEvaluateToNumber(exec));
}

double ModNode::evaluateToNumber(ExecState* exec)
{
    return inlineEvaluateToNumber(exec);
}

bool ModNode::evaluateToBoolean(ExecState* exec)
{
    double result = inlineEvaluateToNumber(exec);
    return  result > 0.0 || 0.0 > result; // NaN produces false as well
}

int32_t ModNode::evaluateToInt32(ExecState* exec)
{
    return JSValue::toInt32(inlineEvaluateToNumber(exec));
}

uint32_t ModNode::evaluateToUInt32(ExecState* exec)
{
    return JSValue::toUInt32(inlineEvaluateToNumber(exec));
}

// ------------------------------ Additive Nodes --------------------------------------

static JSValue* throwOutOfMemoryError(ExecState* exec)
{
    JSObject* error = Error::create(exec, GeneralError, "Out of memory");
    exec->setException(error);
    return error;
}

static double throwOutOfMemoryErrorToNumber(ExecState* exec)
{
    JSObject* error = Error::create(exec, GeneralError, "Out of memory");
    exec->setException(error);
    return 0.0;
}

// ECMA 11.6
static JSValue* addSlowCase(ExecState* exec, JSValue* v1, JSValue* v2)
{
    // exception for the Date exception in defaultValue()
    JSValue* p1 = v1->toPrimitive(exec, UnspecifiedType);
    JSValue* p2 = v2->toPrimitive(exec, UnspecifiedType);

    if (p1->isString() || p2->isString()) {
        UString value = p1->toString(exec) + p2->toString(exec);
        if (value.isNull())
            return throwOutOfMemoryError(exec);
        return jsString(value);
    }

    return jsNumber(p1->toNumber(exec) + p2->toNumber(exec));
}

static double addSlowCaseToNumber(ExecState* exec, JSValue* v1, JSValue* v2)
{
    // exception for the Date exception in defaultValue()
    JSValue* p1 = v1->toPrimitive(exec, UnspecifiedType);
    JSValue* p2 = v2->toPrimitive(exec, UnspecifiedType);

    if (p1->isString() || p2->isString()) {
        UString value = p1->toString(exec) + p2->toString(exec);
        if (value.isNull())
            return throwOutOfMemoryErrorToNumber(exec);
        return value.toDouble();
    }

    return p1->toNumber(exec) + p2->toNumber(exec);
}

// Fast-path choices here are based on frequency data from SunSpider:
//    <times> Add case: <t1> <t2>
//    ---------------------------
//    5627160 Add case: 1 1
//    247427  Add case: 5 5
//    20901   Add case: 5 6
//    13978   Add case: 5 1
//    4000    Add case: 1 5
//    1       Add case: 3 5

static inline JSValue* add(ExecState* exec, JSValue* v1, JSValue* v2)
{
    JSType t1 = v1->type();
    JSType t2 = v2->type();
    const unsigned bothTypes = (t1 << 3) | t2;

    if (bothTypes == ((NumberType << 3) | NumberType))
        return jsNumber(v1->toNumber(exec) + v2->toNumber(exec));
    if (bothTypes == ((StringType << 3) | StringType)) {
        UString value = static_cast<StringImp*>(v1)->value() + static_cast<StringImp*>(v2)->value();
        if (value.isNull())
            return throwOutOfMemoryError(exec);
        return jsString(value);
    }

    // All other cases are pretty uncommon
    return addSlowCase(exec, v1, v2);
}

static inline double addToNumber(ExecState* exec, JSValue* v1, JSValue* v2)
{
    JSType t1 = v1->type();
    JSType t2 = v2->type();
    const unsigned bothTypes = (t1 << 3) | t2;

    if (bothTypes == ((NumberType << 3) | NumberType))
        return v1->toNumber(exec) + v2->toNumber(exec);
    if (bothTypes == ((StringType << 3) | StringType)) {
        UString value = static_cast<StringImp*>(v1)->value() + static_cast<StringImp*>(v2)->value();
        if (value.isNull())
            return throwOutOfMemoryErrorToNumber(exec);
        return value.toDouble();
    }

    // All other cases are pretty uncommon
    return addSlowCaseToNumber(exec, v1, v2);
}

void AddNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_term1.get());
    nodeStack.append(m_term2.get());
}

// ECMA 11.6.1
JSValue* AddNode::evaluate(ExecState* exec)
{
    JSValue* v1 = m_term1->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE

    JSValue* v2 = m_term2->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE

    return add(exec, v1, v2);
}

double AddNode::inlineEvaluateToNumber(ExecState* exec)
{
    JSValue* v1 = m_term1->evaluate(exec);
    KJS_CHECKEXCEPTIONNUMBER

    JSValue* v2 = m_term2->evaluate(exec);
    KJS_CHECKEXCEPTIONNUMBER

    return addToNumber(exec, v1, v2);
}

double AddNode::evaluateToNumber(ExecState* exec)
{
    return inlineEvaluateToNumber(exec);
}

int32_t AddNode::evaluateToInt32(ExecState* exec)
{
    return JSValue::toInt32(inlineEvaluateToNumber(exec));
}

uint32_t AddNode::evaluateToUInt32(ExecState* exec)
{
    return JSValue::toUInt32(inlineEvaluateToNumber(exec));
}

double AddNumbersNode::inlineEvaluateToNumber(ExecState* exec)
{
    double n1 = m_term1->evaluateToNumber(exec);
    KJS_CHECKEXCEPTIONNUMBER
    double n2 = m_term2->evaluateToNumber(exec);
    return n1 + n2;
}

JSValue* AddNumbersNode::evaluate(ExecState* exec)
{
    return jsNumber(inlineEvaluateToNumber(exec));
}

double AddNumbersNode::evaluateToNumber(ExecState* exec)
{
    return inlineEvaluateToNumber(exec);
}

int32_t AddNumbersNode::evaluateToInt32(ExecState* exec)
{
    return JSValue::toInt32(inlineEvaluateToNumber(exec));
}

uint32_t AddNumbersNode::evaluateToUInt32(ExecState* exec)
{
    return JSValue::toUInt32(inlineEvaluateToNumber(exec));
}

JSValue* AddStringsNode::evaluate(ExecState* exec)
{
    JSValue* v1 = m_term1->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE

    JSValue* v2 = m_term2->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE

    return jsString(static_cast<StringImp*>(v1)->value() + static_cast<StringImp*>(v2)->value());
}

JSValue* AddStringLeftNode::evaluate(ExecState* exec)
{
    JSValue* v1 = m_term1->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE

    JSValue* v2 = m_term2->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE

    JSValue* p2 = v2->toPrimitive(exec, UnspecifiedType);
    return jsString(static_cast<StringImp*>(v1)->value() + p2->toString(exec));
}

JSValue* AddStringRightNode::evaluate(ExecState* exec)
{
    JSValue* v1 = m_term1->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE

    JSValue* v2 = m_term2->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE

    JSValue* p1 = v1->toPrimitive(exec, UnspecifiedType);
    return jsString(p1->toString(exec) + static_cast<StringImp*>(v2)->value());
}

void SubNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_term1.get());
    nodeStack.append(m_term2.get());
}

// ECMA 11.6.2
double SubNode::inlineEvaluateToNumber(ExecState* exec)
{
    double n1 = m_term1->evaluateToNumber(exec);
    KJS_CHECKEXCEPTIONNUMBER
    double n2 = m_term2->evaluateToNumber(exec);
    return n1 - n2;
}

JSValue* SubNode::evaluate(ExecState* exec)
{
    return jsNumber(inlineEvaluateToNumber(exec));
}

double SubNode::evaluateToNumber(ExecState* exec)
{
    return inlineEvaluateToNumber(exec);
}

int32_t SubNode::evaluateToInt32(ExecState* exec)
{
    return JSValue::toInt32(inlineEvaluateToNumber(exec));
}

uint32_t SubNode::evaluateToUInt32(ExecState* exec)
{
    return JSValue::toUInt32(inlineEvaluateToNumber(exec));
}

// ------------------------------ Shift Nodes ------------------------------------

void LeftShiftNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_term1.get());
    nodeStack.append(m_term2.get());
}

// ECMA 11.7.1
int32_t LeftShiftNode::inlineEvaluateToInt32(ExecState* exec)
{
    int i1 = m_term1->evaluateToInt32(exec);
    KJS_CHECKEXCEPTIONNUMBER
    unsigned int i2 = m_term2->evaluateToUInt32(exec) & 0x1f;
    return (i1 << i2);
}

JSValue* LeftShiftNode::evaluate(ExecState* exec)
{
    return jsNumber(inlineEvaluateToInt32(exec));
}

double LeftShiftNode::evaluateToNumber(ExecState* exec)
{
    return inlineEvaluateToInt32(exec);
}

int32_t LeftShiftNode::evaluateToInt32(ExecState* exec)
{
    return inlineEvaluateToInt32(exec);
}

uint32_t LeftShiftNode::evaluateToUInt32(ExecState* exec)
{
    return inlineEvaluateToInt32(exec);
}

void RightShiftNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_term1.get());
    nodeStack.append(m_term2.get());
}

// ECMA 11.7.2
int32_t RightShiftNode::inlineEvaluateToInt32(ExecState* exec)
{
    int i1 = m_term1->evaluateToInt32(exec);
    KJS_CHECKEXCEPTIONNUMBER
    unsigned int i2 = m_term2->evaluateToUInt32(exec) & 0x1f;
    return (i1 >> i2);
}

JSValue* RightShiftNode::evaluate(ExecState* exec)
{
    return jsNumber(inlineEvaluateToInt32(exec));
}

double RightShiftNode::evaluateToNumber(ExecState* exec)
{
    return inlineEvaluateToInt32(exec);
}

int32_t RightShiftNode::evaluateToInt32(ExecState* exec)
{
    return inlineEvaluateToInt32(exec);
}

uint32_t RightShiftNode::evaluateToUInt32(ExecState* exec)
{
    return inlineEvaluateToInt32(exec);
}

void UnsignedRightShiftNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_term1.get());
    nodeStack.append(m_term2.get());
}

// ECMA 11.7.3
uint32_t UnsignedRightShiftNode::inlineEvaluateToUInt32(ExecState* exec)
{
    unsigned int i1 = m_term1->evaluateToUInt32(exec);
    KJS_CHECKEXCEPTIONNUMBER
    unsigned int i2 = m_term2->evaluateToUInt32(exec) & 0x1f;
    return (i1 >> i2);
}

JSValue* UnsignedRightShiftNode::evaluate(ExecState* exec)
{
    return jsNumber(inlineEvaluateToUInt32(exec));
}

double UnsignedRightShiftNode::evaluateToNumber(ExecState* exec)
{
    return inlineEvaluateToUInt32(exec);
}

int32_t UnsignedRightShiftNode::evaluateToInt32(ExecState* exec)
{
    return inlineEvaluateToUInt32(exec);
}

uint32_t UnsignedRightShiftNode::evaluateToUInt32(ExecState* exec)
{
    return inlineEvaluateToUInt32(exec);
}

// ------------------------------ Relational Nodes -------------------------------

static inline bool lessThan(ExecState* exec, JSValue* v1, JSValue* v2)
{
    double n1;
    double n2;
    JSValue* p1;
    JSValue* p2;
    bool wasNotString1 = v1->getPrimitiveNumber(exec, n1, p1);
    bool wasNotString2 = v2->getPrimitiveNumber(exec, n2, p2);

    if (wasNotString1 | wasNotString2)
        return n1 < n2;

    return static_cast<const StringImp*>(p1)->value() < static_cast<const StringImp*>(p2)->value();
}

static inline bool lessThanEq(ExecState* exec, JSValue* v1, JSValue* v2)
{
    double n1;
    double n2;
    JSValue* p1;
    JSValue* p2;
    bool wasNotString1 = v1->getPrimitiveNumber(exec, n1, p1);
    bool wasNotString2 = v2->getPrimitiveNumber(exec, n2, p2);

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

    return !(static_cast<const StringImp*>(p2)->value() < static_cast<const StringImp*>(p1)->value());
}

void LessNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_expr2.get());
    nodeStack.append(m_expr1.get());
}

// ECMA 11.8.1
bool LessNode::inlineEvaluateToBoolean(ExecState* exec)
{
    JSValue* v1 = m_expr1->evaluate(exec);
    KJS_CHECKEXCEPTIONBOOLEAN
    JSValue* v2 = m_expr2->evaluate(exec);
    KJS_CHECKEXCEPTIONBOOLEAN
    return lessThan(exec, v1, v2);
}

JSValue* LessNode::evaluate(ExecState* exec)
{
    return jsBoolean(inlineEvaluateToBoolean(exec));
}

bool LessNode::evaluateToBoolean(ExecState* exec)
{
    return inlineEvaluateToBoolean(exec);
}

bool LessNumbersNode::inlineEvaluateToBoolean(ExecState* exec)
{
    double n1 = m_expr1->evaluateToNumber(exec);
    KJS_CHECKEXCEPTIONVALUE
    double n2 = m_expr2->evaluateToNumber(exec);
    return n1 < n2;
}

JSValue* LessNumbersNode::evaluate(ExecState* exec)
{
    return jsBoolean(inlineEvaluateToBoolean(exec));
}

bool LessNumbersNode::evaluateToBoolean(ExecState* exec)
{
    return inlineEvaluateToBoolean(exec);
}

bool LessStringsNode::inlineEvaluateToBoolean(ExecState* exec)
{
    JSValue* v1 = m_expr1->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE
    JSValue* v2 = m_expr2->evaluate(exec);
    return static_cast<StringImp*>(v1)->value() < static_cast<StringImp*>(v2)->value();
}

JSValue* LessStringsNode::evaluate(ExecState* exec)
{
    return jsBoolean(inlineEvaluateToBoolean(exec));
}

bool LessStringsNode::evaluateToBoolean(ExecState* exec)
{
    return inlineEvaluateToBoolean(exec);
}

void GreaterNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_expr2.get());
    nodeStack.append(m_expr1.get());
}

// ECMA 11.8.2
bool GreaterNode::inlineEvaluateToBoolean(ExecState* exec)
{
    JSValue* v1 = m_expr1->evaluate(exec);
    KJS_CHECKEXCEPTIONBOOLEAN
    JSValue* v2 = m_expr2->evaluate(exec);
    KJS_CHECKEXCEPTIONBOOLEAN
    return lessThan(exec, v2, v1);
}

JSValue* GreaterNode::evaluate(ExecState* exec)
{
    return jsBoolean(inlineEvaluateToBoolean(exec));
}

bool GreaterNode::evaluateToBoolean(ExecState* exec)
{
    return inlineEvaluateToBoolean(exec);
}

void LessEqNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_expr2.get());
    nodeStack.append(m_expr1.get());
}

// ECMA 11.8.3
bool LessEqNode::inlineEvaluateToBoolean(ExecState* exec)
{
    JSValue* v1 = m_expr1->evaluate(exec);
    KJS_CHECKEXCEPTIONBOOLEAN
    JSValue* v2 = m_expr2->evaluate(exec);
    KJS_CHECKEXCEPTIONBOOLEAN
    return lessThanEq(exec, v1, v2);
}

JSValue* LessEqNode::evaluate(ExecState* exec)
{
    return jsBoolean(inlineEvaluateToBoolean(exec));
}

bool LessEqNode::evaluateToBoolean(ExecState* exec)
{
    return inlineEvaluateToBoolean(exec);
}

void GreaterEqNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_expr2.get());
    nodeStack.append(m_expr1.get());
}

// ECMA 11.8.4
bool GreaterEqNode::inlineEvaluateToBoolean(ExecState* exec)
{
    JSValue* v1 = m_expr1->evaluate(exec);
    KJS_CHECKEXCEPTIONBOOLEAN
    JSValue* v2 = m_expr2->evaluate(exec);
    KJS_CHECKEXCEPTIONBOOLEAN
    return lessThanEq(exec, v2, v1);
}

JSValue* GreaterEqNode::evaluate(ExecState* exec)
{
    return jsBoolean(inlineEvaluateToBoolean(exec));
}

bool GreaterEqNode::evaluateToBoolean(ExecState* exec)
{
    return inlineEvaluateToBoolean(exec);
}

void InstanceOfNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_expr2.get());
    nodeStack.append(m_expr1.get());
}

// ECMA 11.8.6
JSValue* InstanceOfNode::evaluate(ExecState* exec)
{
    JSValue* v1 = m_expr1->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE
    JSValue* v2 = m_expr2->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE

    if (!v2->isObject())
        return throwError(exec, TypeError, "Value %s (result of expression %s) is not an object. Cannot be used with instanceof operator.", v2, m_expr2.get());

    JSObject* o2 = static_cast<JSObject*>(v2);

    // According to the spec, only some types of objects "implement" the [[HasInstance]] property.
    // But we are supposed to throw an exception where the object does not "have" the [[HasInstance]]
    // property. It seems that all objects have the property, but not all implement it, so in this
    // case we return false (consistent with Mozilla).
    if (!o2->implementsHasInstance())
        return jsBoolean(false);

    return jsBoolean(o2->hasInstance(exec, v1));
}

bool InstanceOfNode::evaluateToBoolean(ExecState* exec)
{
    JSValue* v1 = m_expr1->evaluate(exec);
    KJS_CHECKEXCEPTIONBOOLEAN
    JSValue* v2 = m_expr2->evaluate(exec);
    KJS_CHECKEXCEPTIONBOOLEAN

    if (!v2->isObject()) {
        throwError(exec, TypeError, "Value %s (result of expression %s) is not an object. Cannot be used with 'instanceof' operator.", v2, m_expr2.get());
        return false;
    }

    JSObject* o2 = static_cast<JSObject*>(v2);

    // According to the spec, only some types of objects "implement" the [[HasInstance]] property.
    // But we are supposed to throw an exception where the object does not "have" the [[HasInstance]]
    // property. It seems that all objects have the property, but not all implement it, so in this
    // case we return false (consistent with Mozilla).
    if (!o2->implementsHasInstance())
        return false;

    return o2->hasInstance(exec, v1);
}

void InNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_expr2.get());
    nodeStack.append(m_expr1.get());
}

// ECMA 11.8.7
JSValue* InNode::evaluate(ExecState* exec)
{
    JSValue* v1 = m_expr1->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE
    JSValue* v2 = m_expr2->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE

    if (!v2->isObject())
        return throwError(exec, TypeError, "Value %s (result of expression %s) is not an object. Cannot be used with 'in' operator.", v2, m_expr2.get());

    return jsBoolean(static_cast<JSObject*>(v2)->hasProperty(exec, Identifier(v1->toString(exec))));
}

bool InNode::evaluateToBoolean(ExecState* exec)
{
    JSValue* v1 = m_expr1->evaluate(exec);
    KJS_CHECKEXCEPTIONBOOLEAN
    JSValue* v2 = m_expr2->evaluate(exec);
    KJS_CHECKEXCEPTIONBOOLEAN

    if (!v2->isObject()) {
        throwError(exec, TypeError, "Value %s (result of expression %s) is not an object. Cannot be used with 'in' operator.", v2, m_expr2.get());
        return false;
    }

    return static_cast<JSObject*>(v2)->hasProperty(exec, Identifier(v1->toString(exec)));
}

// ------------------------------ Equality Nodes ------------------------------------

void EqualNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_expr2.get());
    nodeStack.append(m_expr1.get());
}

// ECMA 11.9.1
bool EqualNode::inlineEvaluateToBoolean(ExecState* exec)
{
    JSValue* v1 = m_expr1->evaluate(exec);
    KJS_CHECKEXCEPTIONBOOLEAN
    JSValue* v2 = m_expr2->evaluate(exec);
    KJS_CHECKEXCEPTIONBOOLEAN

    return equal(exec, v1, v2);
}

JSValue* EqualNode::evaluate(ExecState* exec)
{
    return jsBoolean(inlineEvaluateToBoolean(exec));
}

bool EqualNode::evaluateToBoolean(ExecState* exec)
{
    return inlineEvaluateToBoolean(exec);
}

void NotEqualNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_expr2.get());
    nodeStack.append(m_expr1.get());
}

// ECMA 11.9.2
bool NotEqualNode::inlineEvaluateToBoolean(ExecState* exec)
{
    JSValue* v1 = m_expr1->evaluate(exec);
    KJS_CHECKEXCEPTIONBOOLEAN
    JSValue* v2 = m_expr2->evaluate(exec);
    KJS_CHECKEXCEPTIONBOOLEAN

    return !equal(exec,v1, v2);
}

JSValue* NotEqualNode::evaluate(ExecState* exec)
{
    return jsBoolean(inlineEvaluateToBoolean(exec));
}

bool NotEqualNode::evaluateToBoolean(ExecState* exec)
{
    return inlineEvaluateToBoolean(exec);
}

void StrictEqualNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_expr2.get());
    nodeStack.append(m_expr1.get());
}

// ECMA 11.9.4
bool StrictEqualNode::inlineEvaluateToBoolean(ExecState* exec)
{
    JSValue* v1 = m_expr1->evaluate(exec);
    KJS_CHECKEXCEPTIONBOOLEAN
    JSValue* v2 = m_expr2->evaluate(exec);
    KJS_CHECKEXCEPTIONBOOLEAN

    return strictEqual(exec,v1, v2);
}

JSValue* StrictEqualNode::evaluate(ExecState* exec)
{
    return jsBoolean(inlineEvaluateToBoolean(exec));
}

bool StrictEqualNode::evaluateToBoolean(ExecState* exec)
{
    return inlineEvaluateToBoolean(exec);
}

void NotStrictEqualNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_expr2.get());
    nodeStack.append(m_expr1.get());
}

// ECMA 11.9.5
bool NotStrictEqualNode::inlineEvaluateToBoolean(ExecState* exec)
{
    JSValue* v1 = m_expr1->evaluate(exec);
    KJS_CHECKEXCEPTIONBOOLEAN
    JSValue* v2 = m_expr2->evaluate(exec);
    KJS_CHECKEXCEPTIONBOOLEAN

    return !strictEqual(exec,v1, v2);
}

JSValue* NotStrictEqualNode::evaluate(ExecState* exec)
{
    return jsBoolean(inlineEvaluateToBoolean(exec));
}

bool NotStrictEqualNode::evaluateToBoolean(ExecState* exec)
{
    return inlineEvaluateToBoolean(exec);
}

// ------------------------------ Bit Operation Nodes ----------------------------------

void BitAndNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_expr2.get());
    nodeStack.append(m_expr1.get());
}

// ECMA 11.10
JSValue* BitAndNode::evaluate(ExecState* exec)
{
    JSValue* v1 = m_expr1->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE
    JSValue* v2 = m_expr2->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE

    return jsNumberFromAnd(exec, v1, v2);
}

int32_t BitAndNode::inlineEvaluateToInt32(ExecState* exec)
{
    int32_t i1 = m_expr1->evaluateToInt32(exec);
    KJS_CHECKEXCEPTIONNUMBER
    int32_t i2 = m_expr2->evaluateToInt32(exec);
    return (i1 & i2);
}

double BitAndNode::evaluateToNumber(ExecState* exec)
{
    return inlineEvaluateToInt32(exec);
}

bool BitAndNode::evaluateToBoolean(ExecState* exec)
{
    return inlineEvaluateToInt32(exec);
}

int32_t BitAndNode::evaluateToInt32(ExecState* exec)
{
    return inlineEvaluateToInt32(exec);
}

uint32_t BitAndNode::evaluateToUInt32(ExecState* exec)
{
    return inlineEvaluateToInt32(exec);
}

void BitXOrNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_expr2.get());
    nodeStack.append(m_expr1.get());
}

int32_t BitXOrNode::inlineEvaluateToInt32(ExecState* exec)
{
    int i1 = m_expr1->evaluateToInt32(exec);
    KJS_CHECKEXCEPTIONNUMBER
    int i2 = m_expr2->evaluateToInt32(exec);
    return (i1 ^ i2);
}

JSValue* BitXOrNode::evaluate(ExecState* exec)
{
    return jsNumber(inlineEvaluateToInt32(exec));
}

double BitXOrNode::evaluateToNumber(ExecState* exec)
{
    return inlineEvaluateToInt32(exec);
}

bool BitXOrNode::evaluateToBoolean(ExecState* exec)
{
    return inlineEvaluateToInt32(exec);
}

int32_t BitXOrNode::evaluateToInt32(ExecState* exec)
{
    return inlineEvaluateToInt32(exec);
}

uint32_t BitXOrNode::evaluateToUInt32(ExecState* exec)
{
    return inlineEvaluateToInt32(exec);
}

void BitOrNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_expr2.get());
    nodeStack.append(m_expr1.get());
}

int32_t BitOrNode::inlineEvaluateToInt32(ExecState* exec)
{
    int i1 = m_expr1->evaluateToInt32(exec);
    KJS_CHECKEXCEPTIONNUMBER
    int i2 = m_expr2->evaluateToInt32(exec);
    return (i1 | i2);
}

JSValue* BitOrNode::evaluate(ExecState* exec)
{
    return jsNumber(inlineEvaluateToInt32(exec));
}

double BitOrNode::evaluateToNumber(ExecState* exec)
{
    return inlineEvaluateToInt32(exec);
}

bool BitOrNode::evaluateToBoolean(ExecState* exec)
{
    return inlineEvaluateToInt32(exec);
}

int32_t BitOrNode::evaluateToInt32(ExecState* exec)
{
    return inlineEvaluateToInt32(exec);
}

uint32_t BitOrNode::evaluateToUInt32(ExecState* exec)
{
    return inlineEvaluateToInt32(exec);
}

// ------------------------------ Binary Logical Nodes ----------------------------

void LogicalAndNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_expr2.get());
    nodeStack.append(m_expr1.get());
}

// ECMA 11.11
JSValue* LogicalAndNode::evaluate(ExecState* exec)
{
    JSValue* v1 = m_expr1->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE
    bool b1 = v1->toBoolean(exec);
    KJS_CHECKEXCEPTIONVALUE
    if (!b1)
        return v1;
    JSValue* v2 = m_expr2->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE
    return v2;
}

bool LogicalAndNode::evaluateToBoolean(ExecState* exec)
{
    bool b = m_expr1->evaluateToBoolean(exec);
    KJS_CHECKEXCEPTIONBOOLEAN
    return b && m_expr2->evaluateToBoolean(exec);
}

void LogicalOrNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_expr2.get());
    nodeStack.append(m_expr1.get());
}

JSValue* LogicalOrNode::evaluate(ExecState* exec)
{
    JSValue* v1 = m_expr1->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE
    if (v1->toBoolean(exec))
        return v1;
    return m_expr2->evaluate(exec);
}

bool LogicalOrNode::evaluateToBoolean(ExecState* exec)
{
    bool b = m_expr1->evaluateToBoolean(exec);
    KJS_CHECKEXCEPTIONBOOLEAN
    return b || m_expr2->evaluateToBoolean(exec);
}

// ------------------------------ ConditionalNode ------------------------------

void ConditionalNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_expr2.get());
    nodeStack.append(m_expr1.get());
    nodeStack.append(m_logical.get());
}

// ECMA 11.12
JSValue* ConditionalNode::evaluate(ExecState* exec)
{
    bool b = m_logical->evaluateToBoolean(exec);
    KJS_CHECKEXCEPTIONVALUE
    return b ? m_expr1->evaluate(exec) : m_expr2->evaluate(exec);
}

bool ConditionalNode::evaluateToBoolean(ExecState* exec)
{
    bool b = m_logical->evaluateToBoolean(exec);
    KJS_CHECKEXCEPTIONBOOLEAN
    return b ? m_expr1->evaluateToBoolean(exec) : m_expr2->evaluateToBoolean(exec);
}

double ConditionalNode::evaluateToNumber(ExecState* exec)
{
    bool b = m_logical->evaluateToBoolean(exec);
    KJS_CHECKEXCEPTIONNUMBER
    return b ? m_expr1->evaluateToNumber(exec) : m_expr2->evaluateToNumber(exec);
}

int32_t ConditionalNode::evaluateToInt32(ExecState* exec)
{
    bool b = m_logical->evaluateToBoolean(exec);
    KJS_CHECKEXCEPTIONNUMBER
    return b ? m_expr1->evaluateToInt32(exec) : m_expr2->evaluateToInt32(exec);
}

uint32_t ConditionalNode::evaluateToUInt32(ExecState* exec)
{
    bool b = m_logical->evaluateToBoolean(exec);
    KJS_CHECKEXCEPTIONNUMBER
    return b ? m_expr1->evaluateToUInt32(exec) : m_expr2->evaluateToUInt32(exec);
}

// ECMA 11.13

static ALWAYS_INLINE JSValue* valueForReadModifyAssignment(ExecState* exec, JSValue* current, ExpressionNode* right, Operator oper) KJS_FAST_CALL;
static ALWAYS_INLINE JSValue* valueForReadModifyAssignment(ExecState* exec, JSValue* current, ExpressionNode* right, Operator oper)
{
    JSValue* v;
    int i1;
    int i2;
    unsigned int ui;
    switch (oper) {
        case OpMultEq:
            v = jsNumber(current->toNumber(exec) * right->evaluateToNumber(exec));
            break;
        case OpDivEq:
            v = jsNumber(current->toNumber(exec) / right->evaluateToNumber(exec));
            break;
        case OpPlusEq:
            v = add(exec, current, right->evaluate(exec));
            break;
        case OpMinusEq:
            v = jsNumber(current->toNumber(exec) - right->evaluateToNumber(exec));
            break;
        case OpLShift:
            i1 = current->toInt32(exec);
            i2 = right->evaluateToInt32(exec);
            v = jsNumber(i1 << i2);
            break;
        case OpRShift:
            i1 = current->toInt32(exec);
            i2 = right->evaluateToInt32(exec);
            v = jsNumber(i1 >> i2);
            break;
        case OpURShift:
            ui = current->toUInt32(exec);
            i2 = right->evaluateToInt32(exec);
            v = jsNumber(ui >> i2);
            break;
        case OpAndEq:
            i1 = current->toInt32(exec);
            i2 = right->evaluateToInt32(exec);
            v = jsNumber(i1 & i2);
            break;
        case OpXOrEq:
            i1 = current->toInt32(exec);
            i2 = right->evaluateToInt32(exec);
            v = jsNumber(i1 ^ i2);
            break;
        case OpOrEq:
            i1 = current->toInt32(exec);
            i2 = right->evaluateToInt32(exec);
            v = jsNumber(i1 | i2);
            break;
        case OpModEq: {
            double d1 = current->toNumber(exec);
            double d2 = right->evaluateToNumber(exec);
            v = jsNumber(fmod(d1, d2));
        }
            break;
        default:
            ASSERT_NOT_REACHED();
            v = jsUndefined();
    }

    return v;
}

// ------------------------------ ReadModifyResolveNode -----------------------------------

void ReadModifyResolveNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage& localStorage, NodeStack& nodeStack)
{
    nodeStack.append(m_right.get());
    size_t index = symbolTable.get(m_ident.ustring().rep());
    if (index != missingSymbolMarker()) {
        if (isConstant(localStorage, index))
            new (this) ReadModifyConstNode(index);
        else
            new (this) ReadModifyLocalVarNode(index);
    }
}

// ------------------------------ AssignResolveNode -----------------------------------

void AssignResolveNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage& localStorage, NodeStack& nodeStack)
{
    nodeStack.append(m_right.get());
    size_t index = symbolTable.get(m_ident.ustring().rep());
    if (index != missingSymbolMarker()) {
        if (isConstant(localStorage, index))
            new (this) AssignConstNode;
        else
            new (this) AssignLocalVarNode(index);
    }
}

// ------------------------------ ReadModifyLocalVarNode -----------------------------------

JSValue* ReadModifyLocalVarNode::evaluate(ExecState* exec)
{
    ASSERT(exec->variableObject() == exec->scopeChain().top());

    ASSERT(m_operator != OpEqual);
    JSValue* v = valueForReadModifyAssignment(exec, exec->localStorage()[m_index].value, m_right.get(), m_operator);

    KJS_CHECKEXCEPTIONVALUE
    
    // We can't store a pointer into localStorage() and use it throughout the function
    // body, because valueForReadModifyAssignment() might cause an ActivationImp tear-off,
    // changing the value of localStorage().
    
    exec->localStorage()[m_index].value = v;
    return v;
}

// ------------------------------ AssignLocalVarNode -----------------------------------

JSValue* AssignLocalVarNode::evaluate(ExecState* exec)
{
    ASSERT(exec->variableObject() == exec->scopeChain().top());
    JSValue* v = m_right->evaluate(exec);

    KJS_CHECKEXCEPTIONVALUE

    exec->localStorage()[m_index].value = v;

    return v;
}

// ------------------------------ ReadModifyConstNode -----------------------------------

JSValue* ReadModifyConstNode::evaluate(ExecState* exec)
{
    ASSERT(exec->variableObject() == exec->scopeChain().top());
    JSValue* left = exec->localStorage()[m_index].value;
    ASSERT(m_operator != OpEqual);
    JSValue* result = valueForReadModifyAssignment(exec, left, m_right.get(), m_operator);
    KJS_CHECKEXCEPTIONVALUE
    return result;
}

// ------------------------------ AssignConstNode -----------------------------------

JSValue* AssignConstNode::evaluate(ExecState* exec)
{
    return m_right->evaluate(exec);
}

JSValue* ReadModifyResolveNode::evaluate(ExecState* exec)
{
    const ScopeChain& chain = exec->scopeChain();
    ScopeChainIterator iter = chain.begin();
    ScopeChainIterator end = chain.end();

    // We must always have something in the scope chain
    ASSERT(iter != end);

    PropertySlot slot;
    JSObject* base;
    do {
        base = *iter;
        if (base->getPropertySlot(exec, m_ident, slot)) {
            // See the comment in PostIncResolveNode::evaluate().

            base = *iter;
            goto found;
        }

        ++iter;
    } while (iter != end);

    ASSERT(m_operator != OpEqual);
    return throwUndefinedVariableError(exec, m_ident);

found:
    JSValue* v;

    ASSERT(m_operator != OpEqual);
    JSValue* v1 = slot.getValue(exec, base, m_ident);
    KJS_CHECKEXCEPTIONVALUE
    v = valueForReadModifyAssignment(exec, v1, m_right.get(), m_operator);

    KJS_CHECKEXCEPTIONVALUE
    
    // Since valueForReadModifyAssignment() might cause an ActivationImp tear-off,
    // we need to get the base from the ScopeChainIterator again.
    
    (*iter)->put(exec, m_ident, v);
    return v;
}

JSValue* AssignResolveNode::evaluate(ExecState* exec)
{
    const ScopeChain& chain = exec->scopeChain();
    ScopeChainIterator iter = chain.begin();
    ScopeChainIterator end = chain.end();

    // we must always have something in the scope chain
    ASSERT(iter != end);

    PropertySlot slot;
    JSObject* base;
    do {
        base = *iter;
        if (base->getPropertySlot(exec, m_ident, slot)) {
            // See the comment in PostIncResolveNode::evaluate().

            base = *iter;
            goto found;
        }

        ++iter;
    } while (iter != end);

found:
    JSValue* v = m_right->evaluate(exec);

    KJS_CHECKEXCEPTIONVALUE

    base->put(exec, m_ident, v);
    return v;
}

// ------------------------------ ReadModifyDotNode -----------------------------------

void AssignDotNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_right.get());
    nodeStack.append(m_base.get());
}

JSValue* AssignDotNode::evaluate(ExecState* exec)
{
    JSValue* baseValue = m_base->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE
    JSObject* base = baseValue->toObject(exec);

    JSValue* v = m_right->evaluate(exec);

    KJS_CHECKEXCEPTIONVALUE

    base->put(exec, m_ident, v);
    return v;
}

void ReadModifyDotNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_right.get());
    nodeStack.append(m_base.get());
}

JSValue* ReadModifyDotNode::evaluate(ExecState* exec)
{
    JSValue* baseValue = m_base->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE
    JSObject* base = baseValue->toObject(exec);

    JSValue* v;

    ASSERT(m_operator != OpEqual);
    PropertySlot slot;
    JSValue* v1 = base->getPropertySlot(exec, m_ident, slot) ? slot.getValue(exec, base, m_ident) : jsUndefined();
    KJS_CHECKEXCEPTIONVALUE
    v = valueForReadModifyAssignment(exec, v1, m_right.get(), m_operator);

    KJS_CHECKEXCEPTIONVALUE

    base->put(exec, m_ident, v);
    return v;
}

// ------------------------------ AssignErrorNode -----------------------------------

JSValue* AssignErrorNode::evaluate(ExecState* exec)
{
    throwError(exec, ReferenceError, "Left side of assignment is not a reference.");
    handleException(exec);
    return jsUndefined();
}

// ------------------------------ AssignBracketNode -----------------------------------

void AssignBracketNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_right.get());
    nodeStack.append(m_subscript.get());
    nodeStack.append(m_base.get());
}

JSValue* AssignBracketNode::evaluate(ExecState* exec)
{
    JSValue* baseValue = m_base->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE
    JSValue* subscript = m_subscript->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE

    JSObject* base = baseValue->toObject(exec);

    uint32_t propertyIndex;
    if (subscript->getUInt32(propertyIndex)) {
        JSValue* v = m_right->evaluate(exec);
        KJS_CHECKEXCEPTIONVALUE

        base->put(exec, propertyIndex, v);
        return v;
    }

    Identifier propertyName(subscript->toString(exec));
    JSValue* v = m_right->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE

    base->put(exec, propertyName, v);
    return v;
}
void ReadModifyBracketNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_right.get());
    nodeStack.append(m_subscript.get());
    nodeStack.append(m_base.get());
}

JSValue* ReadModifyBracketNode::evaluate(ExecState* exec)
{
    JSValue* baseValue = m_base->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE
    JSValue* subscript = m_subscript->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE

    JSObject* base = baseValue->toObject(exec);

    uint32_t propertyIndex;
    if (subscript->getUInt32(propertyIndex)) {
        JSValue* v;
        ASSERT(m_operator != OpEqual);
        PropertySlot slot;
        JSValue* v1 = base->getPropertySlot(exec, propertyIndex, slot) ? slot.getValue(exec, base, propertyIndex) : jsUndefined();
        KJS_CHECKEXCEPTIONVALUE
        v = valueForReadModifyAssignment(exec, v1, m_right.get(), m_operator);

        KJS_CHECKEXCEPTIONVALUE

        base->put(exec, propertyIndex, v);
        return v;
    }

    Identifier propertyName(subscript->toString(exec));
    JSValue* v;

    ASSERT(m_operator != OpEqual);
    PropertySlot slot;
    JSValue* v1 = base->getPropertySlot(exec, propertyName, slot) ? slot.getValue(exec, base, propertyName) : jsUndefined();
    KJS_CHECKEXCEPTIONVALUE
    v = valueForReadModifyAssignment(exec, v1, m_right.get(), m_operator);

    KJS_CHECKEXCEPTIONVALUE

    base->put(exec, propertyName, v);
    return v;
}

// ------------------------------ CommaNode ------------------------------------

void CommaNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_expr2.get());
    nodeStack.append(m_expr1.get());
}

// ECMA 11.14
JSValue* CommaNode::evaluate(ExecState* exec)
{
    m_expr1->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE
    return m_expr2->evaluate(exec);
}

// ------------------------------ ConstDeclNode ----------------------------------

ConstDeclNode::ConstDeclNode(const Identifier& ident, ExpressionNode* init)
    : m_ident(ident)
    , m_init(init)
{
}

void ConstDeclNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    if (m_next)
        nodeStack.append(m_next.get());
    if (m_init)
        nodeStack.append(m_init.get());
}

void ConstDeclNode::handleSlowCase(ExecState* exec, const ScopeChain& chain, JSValue* val)
{
    ScopeChainIterator iter = chain.begin();
    ScopeChainIterator end = chain.end();

    // We must always have something in the scope chain
    ASSERT(iter != end);

    PropertySlot slot;
    JSObject* base;

    do {
        base = *iter;
        if (base->getPropertySlot(exec, m_ident, slot))
            break;

        ++iter;
    } while (iter != end);

    unsigned flags = 0;
    base->getPropertyAttributes(m_ident, flags);
    flags |= ReadOnly;

    base->put(exec, m_ident, val, flags);
}

// ECMA 12.2
inline void ConstDeclNode::evaluateSingle(ExecState* exec)
{
    ASSERT(exec->variableObject()->hasOwnProperty(exec, m_ident) || exec->codeType() == EvalCode); // Guaranteed by processDeclarations.
    const ScopeChain& chain = exec->scopeChain();
    JSObject* variableObject = exec->variableObject();

    ASSERT(!chain.isEmpty());

    bool inGlobalScope = ++chain.begin() == chain.end();

    if (m_init) {
        if (inGlobalScope) {
            JSValue* val = m_init->evaluate(exec);
            int flags = Internal;
            if (exec->codeType() != EvalCode)
                flags |= DontDelete;
            flags |= ReadOnly;
            variableObject->put(exec, m_ident, val, flags);
        } else {
            JSValue* val = m_init->evaluate(exec);
            KJS_CHECKEXCEPTIONVOID

            // if the variable object is the top of the scope chain, then that must
            // be where this variable is declared, processVarDecls would have put
            // it there. Don't search the scope chain, to optimize this very common case.
            if (chain.top() != variableObject)
                return handleSlowCase(exec, chain, val);

            unsigned flags = 0;
            variableObject->getPropertyAttributes(m_ident, flags);
            flags |= ReadOnly;

            variableObject->put(exec, m_ident, val, flags);
        }
    }
}

JSValue* ConstDeclNode::evaluate(ExecState* exec)
{
    evaluateSingle(exec);

    if (ConstDeclNode* n = m_next.get()) {
        do {
            n->evaluateSingle(exec);
            KJS_CHECKEXCEPTIONVALUE
            n = n->m_next.get();
        } while (n);
    }
    return jsUndefined();
}

// ------------------------------ ConstStatementNode -----------------------------

void ConstStatementNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    ASSERT(m_next);
    nodeStack.append(m_next.get());
}

// ECMA 12.2
JSValue* ConstStatementNode::execute(ExecState* exec)
{
    m_next->evaluate(exec);
    KJS_CHECKEXCEPTION

    return exec->setNormalCompletion();
}

// ------------------------------ Helper functions for handling Vectors of StatementNode -------------------------------

static inline void statementListPushFIFO(StatementVector& statements, DeclarationStacks::NodeStack& stack)
{
    StatementVector::iterator it = statements.end();
    StatementVector::iterator begin = statements.begin();
    while (it != begin) {
        --it;
        stack.append((*it).get());
    }
}

static inline Node* statementListInitializeVariableAccessStack(StatementVector& statements, DeclarationStacks::NodeStack& stack)
{
    if (statements.isEmpty())
        return 0;

    StatementVector::iterator it = statements.end();
    StatementVector::iterator begin = statements.begin();
    StatementVector::iterator beginPlusOne = begin + 1;

    while (it != beginPlusOne) {
        --it;
        stack.append((*it).get());
    }

    return (*begin).get();
}

static inline JSValue* statementListExecute(StatementVector& statements, ExecState* exec)
{
    JSValue* value = 0;
    size_t size = statements.size();
    for (size_t i = 0; i != size; ++i) {
        JSValue* statementValue = statements[i]->execute(exec);
        if (statementValue)
            value = statementValue;
        if (exec->completionType() != Normal)
            return value;
    }
    return exec->setNormalCompletion(value);
}

// ------------------------------ BlockNode ------------------------------------

BlockNode::BlockNode(SourceElements* children)
{
    if (children)
        children->releaseContentsIntoVector(m_children);
}

void BlockNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    statementListPushFIFO(m_children, nodeStack);
}

// ECMA 12.1
JSValue* BlockNode::execute(ExecState* exec)
{
    return statementListExecute(m_children, exec);
}

// ------------------------------ EmptyStatementNode ---------------------------

// ECMA 12.3
JSValue* EmptyStatementNode::execute(ExecState* exec)
{
    return exec->setNormalCompletion();
}

// ------------------------------ ExprStatementNode ----------------------------

void ExprStatementNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    ASSERT(m_expr);
    nodeStack.append(m_expr.get());
}

// ECMA 12.4
JSValue* ExprStatementNode::execute(ExecState* exec)
{
    JSValue* value = m_expr->evaluate(exec);
    KJS_CHECKEXCEPTION

    return exec->setNormalCompletion(value);
}

// ------------------------------ VarStatementNode ----------------------------

void VarStatementNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    ASSERT(m_expr);
    nodeStack.append(m_expr.get());
}

JSValue* VarStatementNode::execute(ExecState* exec)
{
    m_expr->evaluate(exec);
    KJS_CHECKEXCEPTION

    return exec->setNormalCompletion();
}

// ------------------------------ IfNode ---------------------------------------

void IfNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_ifBlock.get());
    nodeStack.append(m_condition.get());
}

// ECMA 12.5
JSValue* IfNode::execute(ExecState* exec)
{
    bool b = m_condition->evaluateToBoolean(exec);
    KJS_CHECKEXCEPTION

    if (b)
        return m_ifBlock->execute(exec);
    return exec->setNormalCompletion();
}

void IfElseNode::optimizeVariableAccess(const SymbolTable& symbolTable, const LocalStorage& localStorage, NodeStack& nodeStack)
{
    nodeStack.append(m_elseBlock.get());
    IfNode::optimizeVariableAccess(symbolTable, localStorage, nodeStack);
}

// ECMA 12.5
JSValue* IfElseNode::execute(ExecState* exec)
{
    bool b = m_condition->evaluateToBoolean(exec);
    KJS_CHECKEXCEPTION

    if (b)
        return m_ifBlock->execute(exec);
    return m_elseBlock->execute(exec);
}

// ------------------------------ DoWhileNode ----------------------------------

void DoWhileNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_statement.get());
    nodeStack.append(m_expr.get());
}

// ECMA 12.6.1
JSValue* DoWhileNode::execute(ExecState* exec)
{
    JSValue* value = 0;

    while (1) {
        exec->pushIteration();
        JSValue* statementValue = m_statement->execute(exec);
        exec->popIteration();

        if (exec->dynamicGlobalObject()->timedOut())
            return setInterruptedCompletion(exec);

        if (statementValue)
            value = statementValue;

        if (exec->completionType() != Normal) {
            if (exec->completionType() == Continue && m_labelStack.contains(exec->breakOrContinueTarget()))
                goto continueDoWhileLoop;
            if (exec->completionType() == Break && m_labelStack.contains(exec->breakOrContinueTarget()))
                break;
            return statementValue;
        }

    continueDoWhileLoop:
        bool b = m_expr->evaluateToBoolean(exec);
        KJS_CHECKEXCEPTION
        if (!b)
            break;
    }

    return exec->setNormalCompletion(value);
}

// ------------------------------ WhileNode ------------------------------------

void WhileNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_statement.get());
    nodeStack.append(m_expr.get());
}

// ECMA 12.6.2
JSValue* WhileNode::execute(ExecState* exec)
{
    JSValue* value = 0;

    while (1) {
        bool b = m_expr->evaluateToBoolean(exec);
        KJS_CHECKEXCEPTION
        if (!b)
            break;

        exec->pushIteration();
        JSValue* statementValue = m_statement->execute(exec);
        exec->popIteration();

        if (exec->dynamicGlobalObject()->timedOut())
            return setInterruptedCompletion(exec);

        if (statementValue)
            value = statementValue;

        if (exec->completionType() != Normal) {
            if (exec->completionType() == Continue && m_labelStack.contains(exec->breakOrContinueTarget()))
                continue;
            if (exec->completionType() == Break && m_labelStack.contains(exec->breakOrContinueTarget()))
                break;
            return statementValue;
        }
    }

    return exec->setNormalCompletion(value);
}

// ------------------------------ ForNode --------------------------------------

void ForNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_statement.get());
    nodeStack.append(m_expr3.get());
    nodeStack.append(m_expr2.get());
    nodeStack.append(m_expr1.get());
}

// ECMA 12.6.3
JSValue* ForNode::execute(ExecState* exec)
{
    JSValue* value = 0;

    m_expr1->evaluate(exec);
    KJS_CHECKEXCEPTION

    while (1) {
        bool b = m_expr2->evaluateToBoolean(exec);
        KJS_CHECKEXCEPTION
        if (!b)
            break;

        exec->pushIteration();
        JSValue* statementValue = m_statement->execute(exec);
        exec->popIteration();
        if (statementValue)
            value = statementValue;

        if (exec->dynamicGlobalObject()->timedOut())
            return setInterruptedCompletion(exec);

        if (exec->completionType() != Normal) {
            if (exec->completionType() == Continue && m_labelStack.contains(exec->breakOrContinueTarget()))
                goto continueForLoop;
            if (exec->completionType() == Break && m_labelStack.contains(exec->breakOrContinueTarget()))
                break;
            return statementValue;
        }

    continueForLoop:
        m_expr3->evaluate(exec);
        KJS_CHECKEXCEPTION
    }

    return exec->setNormalCompletion(value);
}

// ------------------------------ ForInNode ------------------------------------

ForInNode::ForInNode(ExpressionNode* l, ExpressionNode* expr, StatementNode* statement)
    : m_init(0L)
    , m_lexpr(l)
    , m_expr(expr)
    , m_statement(statement)
    , m_identIsVarDecl(false)
{
}

ForInNode::ForInNode(const Identifier& ident, ExpressionNode* in, ExpressionNode* expr, StatementNode* statement)
    : m_ident(ident)
    , m_lexpr(new ResolveNode(ident))
    , m_expr(expr)
    , m_statement(statement)
    , m_identIsVarDecl(true)
{
    if (in)
        m_init = new AssignResolveNode(ident, in);
    // for( var foo = bar in baz )
}

void ForInNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_statement.get());
    nodeStack.append(m_expr.get());
    nodeStack.append(m_lexpr.get());
    if (m_init)
        nodeStack.append(m_init.get());
}

// ECMA 12.6.4
JSValue* ForInNode::execute(ExecState* exec)
{
    JSValue* value = 0;

    if (m_init) {
        m_init->evaluate(exec);
        KJS_CHECKEXCEPTION
    }

    JSValue* e = m_expr->evaluate(exec);
    KJS_CHECKEXCEPTION

    // For Null and Undefined, we want to make sure not to go through
    // the loop at all, because toObject will throw an exception.
    if (e->isUndefinedOrNull())
        return exec->setNormalCompletion();

    JSObject* v = e->toObject(exec);
    PropertyNameArray propertyNames;
    v->getPropertyNames(exec, propertyNames);

    PropertyNameArray::const_iterator end = propertyNames.end();
    for (PropertyNameArray::const_iterator it = propertyNames.begin(); it != end; ++it) {
        const Identifier& name = *it;
        if (!v->hasProperty(exec, name))
            continue;

        JSValue* str = jsOwnedString(name.ustring());

        if (m_lexpr->isResolveNode()) {
            const Identifier& ident = static_cast<ResolveNode*>(m_lexpr.get())->identifier();

            const ScopeChain& chain = exec->scopeChain();
            ScopeChainIterator iter = chain.begin();
            ScopeChainIterator end = chain.end();

            // we must always have something in the scope chain
            ASSERT(iter != end);

            PropertySlot slot;
            JSObject* o;
            do {
                o = *iter;
                if (o->getPropertySlot(exec, ident, slot)) {
                    o->put(exec, ident, str);
                    break;
                }
                ++iter;
            } while (iter != end);

            if (iter == end)
                o->put(exec, ident, str);
        } else if (m_lexpr->isDotAccessorNode()) {
            const Identifier& ident = static_cast<DotAccessorNode*>(m_lexpr.get())->identifier();
            JSValue* v = static_cast<DotAccessorNode*>(m_lexpr.get())->base()->evaluate(exec);
            KJS_CHECKEXCEPTION
            JSObject* o = v->toObject(exec);

            o->put(exec, ident, str);
        } else {
            ASSERT(m_lexpr->isBracketAccessorNode());
            JSValue* v = static_cast<BracketAccessorNode*>(m_lexpr.get())->base()->evaluate(exec);
            KJS_CHECKEXCEPTION
            JSValue* v2 = static_cast<BracketAccessorNode*>(m_lexpr.get())->subscript()->evaluate(exec);
            KJS_CHECKEXCEPTION
            JSObject* o = v->toObject(exec);

            uint32_t i;
            if (v2->getUInt32(i))
                o->put(exec, i, str);
            o->put(exec, Identifier(v2->toString(exec)), str);
        }

        KJS_CHECKEXCEPTION

        exec->pushIteration();
        JSValue* statementValue = m_statement->execute(exec);
        exec->popIteration();
        if (statementValue)
            value = statementValue;

        if (exec->completionType() != Normal) {
            if (exec->completionType() == Continue && m_labelStack.contains(exec->breakOrContinueTarget()))
                continue;
            if (exec->completionType() == Break && m_labelStack.contains(exec->breakOrContinueTarget()))
                break;
            return statementValue;
        }
    }

    return exec->setNormalCompletion(value);
}

// ------------------------------ ContinueNode ---------------------------------

// ECMA 12.7
JSValue* ContinueNode::execute(ExecState* exec)
{
    if (m_ident.isEmpty() && !exec->inIteration())
        return setErrorCompletion(exec, SyntaxError, "Invalid continue statement.");
    if (!m_ident.isEmpty() && !exec->seenLabels().contains(m_ident))
        return setErrorCompletion(exec, SyntaxError, "Label %s not found.", m_ident);
    return exec->setContinueCompletion(&m_ident);
}

// ------------------------------ BreakNode ------------------------------------

// ECMA 12.8
JSValue* BreakNode::execute(ExecState* exec)
{
    if (m_ident.isEmpty() && !exec->inIteration() && !exec->inSwitch())
        return setErrorCompletion(exec, SyntaxError, "Invalid break statement.");
    if (!m_ident.isEmpty() && !exec->seenLabels().contains(m_ident))
        return setErrorCompletion(exec, SyntaxError, "Label %s not found.");
    return exec->setBreakCompletion(&m_ident);
}

// ------------------------------ ReturnNode -----------------------------------

void ReturnNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    if (m_value)
        nodeStack.append(m_value.get());
}

// ECMA 12.9
JSValue* ReturnNode::execute(ExecState* exec)
{
    CodeType codeType = exec->codeType();
    if (codeType != FunctionCode)
        return setErrorCompletion(exec, SyntaxError, "Invalid return statement.");

    if (!m_value)
        return exec->setReturnValueCompletion(jsUndefined());

    JSValue* v = m_value->evaluate(exec);
    KJS_CHECKEXCEPTION

    return exec->setReturnValueCompletion(v);
}

// ------------------------------ WithNode -------------------------------------

void WithNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    // Can't optimize within statement because "with" introduces a dynamic scope.
    nodeStack.append(m_expr.get());
}

// ECMA 12.10
JSValue* WithNode::execute(ExecState* exec)
{
    JSValue* v = m_expr->evaluate(exec);
    KJS_CHECKEXCEPTION
    JSObject* o = v->toObject(exec);
    KJS_CHECKEXCEPTION
    exec->dynamicGlobalObject()->tearOffActivation(exec);
    exec->pushScope(o);
    JSValue* value = m_statement->execute(exec);
    exec->popScope();

    return value;
}

// ------------------------------ CaseClauseNode -------------------------------

void CaseClauseNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    if (m_expr)
        nodeStack.append(m_expr.get());
    statementListPushFIFO(m_children, nodeStack);
}

// ECMA 12.11
JSValue* CaseClauseNode::evaluate(ExecState* exec)
{
    JSValue* v = m_expr->evaluate(exec);
    KJS_CHECKEXCEPTIONVALUE

    return v;
}

// ECMA 12.11
JSValue* CaseClauseNode::executeStatements(ExecState* exec)
{
    return statementListExecute(m_children, exec);
}

// ------------------------------ ClauseListNode -------------------------------

void ClauseListNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    if (m_next)
        nodeStack.append(m_next.get());
    nodeStack.append(m_clause.get());
}

// ------------------------------ CaseBlockNode --------------------------------

CaseBlockNode::CaseBlockNode(ClauseListNode* list1, CaseClauseNode* defaultClause, ClauseListNode* list2)
    : m_list1(list1)
    , m_defaultClause(defaultClause)
    , m_list2(list2)
{
}

void CaseBlockNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    if (m_list2)
        nodeStack.append(m_list2.get());
    if (m_defaultClause)
        nodeStack.append(m_defaultClause.get());
    if (m_list1)
        nodeStack.append(m_list1.get());
}

// ECMA 12.11
JSValue* CaseBlockNode::executeBlock(ExecState* exec, JSValue* input)
{
    ClauseListNode* a = m_list1.get();
    while (a) {
        CaseClauseNode* clause = a->getClause();
        a = a->getNext();
        JSValue* v = clause->evaluate(exec);
        KJS_CHECKEXCEPTION
        if (strictEqual(exec, input, v)) {
            JSValue* res = clause->executeStatements(exec);
            if (exec->completionType() != Normal)
                return res;
            for (; a; a = a->getNext()) {
                JSValue* res = a->getClause()->executeStatements(exec);
                if (exec->completionType() != Normal)
                    return res;
            }
            break;
        }
    }

    ClauseListNode* b = m_list2.get();
    while (b) {
        CaseClauseNode* clause = b->getClause();
        b = b->getNext();
        JSValue* v = clause->evaluate(exec);
        KJS_CHECKEXCEPTION
        if (strictEqual(exec, input, v)) {
            JSValue* res = clause->executeStatements(exec);
            if (exec->completionType() != Normal)
                return res;
            goto step18;
        }
    }

    // default clause
    if (m_defaultClause) {
        JSValue* res = m_defaultClause->executeStatements(exec);
        if (exec->completionType() != Normal)
            return res;
    }
    b = m_list2.get();
step18:
    while (b) {
        CaseClauseNode* clause = b->getClause();
        JSValue* res = clause->executeStatements(exec);
        if (exec->completionType() != Normal)
            return res;
        b = b->getNext();
    }

    // bail out on error
    KJS_CHECKEXCEPTION

    return exec->setNormalCompletion();
}

// ------------------------------ SwitchNode -----------------------------------

void SwitchNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_block.get());
    nodeStack.append(m_expr.get());
}

// ECMA 12.11
JSValue* SwitchNode::execute(ExecState* exec)
{
    JSValue* v = m_expr->evaluate(exec);
    KJS_CHECKEXCEPTION

    exec->pushSwitch();
    JSValue* result = m_block->executeBlock(exec, v);
    exec->popSwitch();

    if (exec->completionType() == Break && m_labelStack.contains(exec->breakOrContinueTarget()))
        exec->setCompletionType(Normal);
    return result;
}

// ------------------------------ LabelNode ------------------------------------

void LabelNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_statement.get());
}

// ECMA 12.12
JSValue* LabelNode::execute(ExecState* exec)
{
    if (!exec->seenLabels().push(m_label))
        return setErrorCompletion(exec, SyntaxError, "Duplicated label %s found.", m_label);
    JSValue* result = m_statement->execute(exec);
    exec->seenLabels().pop();

    if (exec->completionType() == Break && exec->breakOrContinueTarget() == m_label)
        exec->setCompletionType(Normal);
    return result;
}

// ------------------------------ ThrowNode ------------------------------------

void ThrowNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    nodeStack.append(m_expr.get());
}

// ECMA 12.13
JSValue* ThrowNode::execute(ExecState* exec)
{
    JSValue* v = m_expr->evaluate(exec);
    KJS_CHECKEXCEPTION

    handleException(exec, v);
    return exec->setThrowCompletion(v);
}

// ------------------------------ TryNode --------------------------------------

void TryNode::optimizeVariableAccess(const SymbolTable&, const LocalStorage&, NodeStack& nodeStack)
{
    // Can't optimize within catchBlock because "catch" introduces a dynamic scope.
    if (m_finallyBlock)
        nodeStack.append(m_finallyBlock.get());
    nodeStack.append(m_tryBlock.get());
}

// ECMA 12.14
JSValue* TryNode::execute(ExecState* exec)
{
    JSValue* result = m_tryBlock->execute(exec);

    if (m_catchBlock && exec->completionType() == Throw) {
        JSObject* obj = new JSObject;
        obj->put(exec, m_exceptionIdent, result, DontDelete);
        exec->dynamicGlobalObject()->tearOffActivation(exec);
        exec->pushScope(obj);
        result = m_catchBlock->execute(exec);
        exec->popScope();
    }

    if (m_finallyBlock) {
        ComplType savedCompletionType = exec->completionType();
        JSValue* finallyResult = m_finallyBlock->execute(exec);
        if (exec->completionType() != Normal)
            result = finallyResult;
        else
            exec->setCompletionType(savedCompletionType);
    }

    return result;
}

// ------------------------------ FunctionBodyNode -----------------------------

ScopeNode::ScopeNode(SourceElements* children, VarStack* varStack, FunctionStack* funcStack)
    : BlockNode(children)
    , m_sourceURL(parser().sourceURL())
    , m_sourceId(parser().sourceId())
{
    if (varStack)
        m_varStack = *varStack;
    if (funcStack)
        m_functionStack = *funcStack;
}

// ------------------------------ ProgramNode -----------------------------

ProgramNode::ProgramNode(SourceElements* children, VarStack* varStack, FunctionStack* funcStack)
    : ScopeNode(children, varStack, funcStack)
{
}

ProgramNode* ProgramNode::create(SourceElements* children, VarStack* varStack, FunctionStack* funcStack)
{
    return new ProgramNode(children, varStack, funcStack);
}

// ------------------------------ EvalNode -----------------------------

EvalNode::EvalNode(SourceElements* children, VarStack* varStack, FunctionStack* funcStack)
    : ScopeNode(children, varStack, funcStack)
{
}

EvalNode* EvalNode::create(SourceElements* children, VarStack* varStack, FunctionStack* funcStack)
{
    return new EvalNode(children, varStack, funcStack);
}

// ------------------------------ FunctionBodyNode -----------------------------

FunctionBodyNode::FunctionBodyNode(SourceElements* children, VarStack* varStack, FunctionStack* funcStack)
    : ScopeNode(children, varStack, funcStack)
    , m_initialized(false)
{
}

FunctionBodyNode* FunctionBodyNode::create(SourceElements* children, VarStack* varStack, FunctionStack* funcStack)
{
    if (Debugger::debuggersPresent)
        return new FunctionBodyNodeWithDebuggerHooks(children, varStack, funcStack);
    return new FunctionBodyNode(children, varStack, funcStack);
}

void FunctionBodyNode::initializeSymbolTable(ExecState* exec)
{
    SymbolTable& symbolTable = exec->variableObject()->symbolTable();
    ASSERT(symbolTable.isEmpty());

    size_t localStorageIndex = 0;

    // Order must match the order in processDeclarations.

    for (size_t i = 0, size = m_parameters.size(); i < size; ++i, ++localStorageIndex) {
        UString::Rep* rep = m_parameters[i].ustring().rep();
        symbolTable.set(rep, localStorageIndex);
    }

    for (size_t i = 0, size = m_functionStack.size(); i < size; ++i, ++localStorageIndex) {
        UString::Rep* rep = m_functionStack[i]->m_ident.ustring().rep();
        symbolTable.set(rep, localStorageIndex);
    }

    for (size_t i = 0, size = m_varStack.size(); i < size; ++i, ++localStorageIndex) {
        Identifier& ident = m_varStack[i].first;
        if (ident == exec->propertyNames().arguments)
            continue;
        symbolTable.add(ident.ustring().rep(), localStorageIndex);
    }
}

void ProgramNode::initializeSymbolTable(ExecState* exec)
{
    // If a previous script defined a symbol with the same name as one of our
    // symbols, to avoid breaking previously optimized nodes, we need to reuse
    // the symbol's existing storage index. So, we can't be as efficient as
    // FunctionBodyNode::initializeSymbolTable, which knows that no bindings
    // have yet been made.

    JSVariableObject* variableObject = exec->variableObject();
    SymbolTable& symbolTable = variableObject->symbolTable();

    size_t localStorageIndex = symbolTable.size();
    size_t size;

    // Order must match the order in processDeclarations.

    size = m_functionStack.size();
    m_functionIndexes.resize(size);
    for (size_t i = 0; i < size; ++i) {
        UString::Rep* rep = m_functionStack[i]->m_ident.ustring().rep();
        pair<SymbolTable::iterator, bool> result = symbolTable.add(rep, localStorageIndex);
        m_functionIndexes[i] = result.first->second;
        if (result.second)
            ++localStorageIndex;
    }

    size = m_varStack.size();
    m_varIndexes.resize(size);
    for (size_t i = 0; i < size; ++i) {
        const Identifier& ident = m_varStack[i].first;
        if (variableObject->hasProperty(exec, ident)) {
            m_varIndexes[i] = missingSymbolMarker(); // Signal not to initialize this declaration.
            continue;
        }

        UString::Rep* rep = ident.ustring().rep();
        pair<SymbolTable::iterator, bool> result = symbolTable.add(rep, localStorageIndex);
        if (!result.second) {
            m_varIndexes[i] = missingSymbolMarker(); // Signal not to initialize this declaration.
            continue;
        }

        m_varIndexes[i] = result.first->second;
        ++localStorageIndex;
    }
}

void ScopeNode::optimizeVariableAccess(ExecState* exec)
{
    NodeStack nodeStack;
    Node* node = statementListInitializeVariableAccessStack(m_children, nodeStack);
    if (!node)
        return;

    const SymbolTable& symbolTable = exec->variableObject()->symbolTable();
    const LocalStorage& localStorage = exec->variableObject()->localStorage();
    while (true) {
        node->optimizeVariableAccess(symbolTable, localStorage, nodeStack);

        size_t size = nodeStack.size();
        if (!size)
            break;
        --size;
        node = nodeStack[size];
        nodeStack.shrink(size);
    }
}

void FunctionBodyNode::processDeclarations(ExecState* exec)
{
    if (!m_initialized)
        initializeSymbolTable(exec);

    if (!m_functionStack.isEmpty())
        exec->dynamicGlobalObject()->tearOffActivation(exec);

    LocalStorage& localStorage = exec->variableObject()->localStorage();

    // We can't just resize localStorage here because that would temporarily
    // leave uninitialized entries, which would crash GC during the mark phase.
    size_t totalSize = m_varStack.size() + m_parameters.size() + m_functionStack.size();
    if (totalSize > localStorage.capacity()) // Doing this check inline avoids function call overhead.
        localStorage.reserveCapacity(totalSize);

    int minAttributes = Internal | DontDelete;

    // In order for our localStorage indexes to be correct, we must match the
    // order of addition in initializeSymbolTable().

    const List& args = *exec->arguments();
    for (size_t i = 0, size = m_parameters.size(); i < size; ++i)
        localStorage.uncheckedAppend(LocalStorageEntry(args[i], DontDelete));

    for (size_t i = 0, size = m_functionStack.size(); i < size; ++i) {
        FuncDeclNode* node = m_functionStack[i];
        localStorage.uncheckedAppend(LocalStorageEntry(node->makeFunction(exec), minAttributes));
    }

    for (size_t i = 0, size = m_varStack.size(); i < size; ++i) {
        int attributes = minAttributes;
        if (m_varStack[i].second & DeclarationStacks::IsConstant)
            attributes |= ReadOnly;
        localStorage.uncheckedAppend(LocalStorageEntry(jsUndefined(), attributes));
    }

    if (!m_initialized) {
        optimizeVariableAccess(exec);
        m_initialized = true;
    }
}

static void gccIsCrazy() KJS_FAST_CALL;
static void gccIsCrazy()
{
}

void ProgramNode::processDeclarations(ExecState* exec)
{
    // If you remove this call, some SunSpider tests, including
    // bitops-nsieve-bits.js, will regress substantially on Mac, due to a ~40%
    // increase in L2 cache misses. FIXME: <rdar://problem/5657439> WTF?
    gccIsCrazy();

    initializeSymbolTable(exec);

    LocalStorage& localStorage = exec->variableObject()->localStorage();

    // We can't just resize localStorage here because that would temporarily
    // leave uninitialized entries, which would crash GC during the mark phase.
    localStorage.reserveCapacity(localStorage.size() + m_varStack.size() + m_functionStack.size());

    int minAttributes = Internal | DontDelete;

    // In order for our localStorage indexes to be correct, we must match the
    // order of addition in initializeSymbolTable().

    for (size_t i = 0, size = m_functionStack.size(); i < size; ++i) {
        FuncDeclNode* node = m_functionStack[i];
        LocalStorageEntry entry = LocalStorageEntry(node->makeFunction(exec), minAttributes);
        size_t index = m_functionIndexes[i];

        if (index == localStorage.size())
            localStorage.uncheckedAppend(entry);
        else {
            ASSERT(index < localStorage.size());
            localStorage[index] = entry;
        }
    }

    for (size_t i = 0, size = m_varStack.size(); i < size; ++i) {
        size_t index = m_varIndexes[i];
        if (index == missingSymbolMarker())
            continue;

        int attributes = minAttributes;
        if (m_varStack[i].second & DeclarationStacks::IsConstant)
            attributes |= ReadOnly;
        LocalStorageEntry entry = LocalStorageEntry(jsUndefined(), attributes);

        ASSERT(index == localStorage.size());
        localStorage.uncheckedAppend(entry);
    }

    optimizeVariableAccess(exec);
}

void EvalNode::processDeclarations(ExecState* exec)
{
    // We could optimize access to pre-existing symbols here, but SunSpider
    // reports that to be a net loss.

    size_t i;
    size_t size;

    JSVariableObject* variableObject = exec->variableObject();

    int minAttributes = Internal;

    for (i = 0, size = m_varStack.size(); i < size; ++i) {
        Identifier& ident = m_varStack[i].first;
        if (variableObject->hasProperty(exec, ident))
            continue;
        int attributes = minAttributes;
        if (m_varStack[i].second & DeclarationStacks::IsConstant)
            attributes |= ReadOnly;
        variableObject->put(exec, ident, jsUndefined(), attributes);
    }

    for (i = 0, size = m_functionStack.size(); i < size; ++i) {
        FuncDeclNode* node = m_functionStack[i];
        variableObject->put(exec, node->m_ident, node->makeFunction(exec), minAttributes);
    }
}

UString FunctionBodyNode::paramString() const
{
    UString s("");
    size_t count = m_parameters.size();
    for (size_t pos = 0; pos < count; ++pos) {
        if (!s.isEmpty())
            s += ", ";
        s += m_parameters[pos].ustring();
    }

    return s;
}

JSValue* ProgramNode::execute(ExecState* exec)
{
    processDeclarations(exec);
    return ScopeNode::execute(exec);
}

JSValue* EvalNode::execute(ExecState* exec)
{
    processDeclarations(exec);
    return ScopeNode::execute(exec);
}

JSValue* FunctionBodyNode::execute(ExecState* exec)
{
    processDeclarations(exec);
    return ScopeNode::execute(exec);
}

// ------------------------------ FunctionBodyNodeWithDebuggerHooks ---------------------------------

FunctionBodyNodeWithDebuggerHooks::FunctionBodyNodeWithDebuggerHooks(SourceElements* children, DeclarationStacks::VarStack* varStack, DeclarationStacks::FunctionStack* funcStack)
    : FunctionBodyNode(children, varStack, funcStack)
{
}

JSValue* FunctionBodyNodeWithDebuggerHooks::execute(ExecState* exec)
{
    if (Debugger* dbg = exec->dynamicGlobalObject()->debugger()) {
        if (!dbg->callEvent(exec, sourceId(), lineNo(), exec->function(), *exec->arguments())) {
            dbg->imp()->abort();
            return exec->setInterruptedCompletion();
        }
    }

    JSValue* result = FunctionBodyNode::execute(exec);

    if (Debugger* dbg = exec->dynamicGlobalObject()->debugger()) {
        if (exec->completionType() == Throw)
            exec->setException(result);
        if (!dbg->returnEvent(exec, sourceId(), lastLine(), exec->function())) {
            dbg->imp()->abort();
            return exec->setInterruptedCompletion();
        }
    }

    return result;
}

// ------------------------------ FuncDeclNode ---------------------------------

void FuncDeclNode::addParams()
{
    for (ParameterNode* p = m_parameter.get(); p; p = p->nextParam())
        m_body->parameters().append(p->ident());
}

FunctionImp* FuncDeclNode::makeFunction(ExecState* exec)
{
    FunctionImp* func = new FunctionImp(exec, m_ident, m_body.get(), exec->scopeChain());

    JSObject* proto = exec->lexicalGlobalObject()->objectConstructor()->construct(exec, exec->emptyList());
    proto->putDirect(exec->propertyNames().constructor, func, ReadOnly | DontDelete | DontEnum);
    func->putDirect(exec->propertyNames().prototype, proto, Internal | DontDelete);
    func->putDirect(exec->propertyNames().length, jsNumber(m_body->parameters().size()), ReadOnly | DontDelete | DontEnum);
    return func;
}

JSValue* FuncDeclNode::execute(ExecState* exec)
{
    return exec->setNormalCompletion();
}

// ------------------------------ FuncExprNode ---------------------------------

// ECMA 13
void FuncExprNode::addParams()
{
    for (ParameterNode* p = m_parameter.get(); p; p = p->nextParam())
        m_body->parameters().append(p->ident());
}

JSValue* FuncExprNode::evaluate(ExecState* exec)
{
    exec->dynamicGlobalObject()->tearOffActivation(exec);

    bool named = !m_ident.isNull();
    JSObject* functionScopeObject = 0;

    if (named) {
        // named FunctionExpressions can recursively call themselves,
        // but they won't register with the current scope chain and should
        // be contained as single property in an anonymous object.
        functionScopeObject = new JSObject;
        exec->pushScope(functionScopeObject);
    }

    FunctionImp* func = new FunctionImp(exec, m_ident, m_body.get(), exec->scopeChain());
    JSObject* proto = exec->lexicalGlobalObject()->objectConstructor()->construct(exec, exec->emptyList());
    proto->putDirect(exec->propertyNames().constructor, func, ReadOnly | DontDelete | DontEnum);
    func->putDirect(exec->propertyNames().prototype, proto, Internal | DontDelete);

    if (named) {
        functionScopeObject->putDirect(m_ident, func, Internal | ReadOnly | (exec->codeType() == EvalCode ? 0 : DontDelete));
        exec->popScope();
    }

    return func;
}

} // namespace KJS