JSScope.cpp   [plain text]


/*
 * Copyright (C) 2012 Apple Inc. All Rights Reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
 */

#include "config.h"
#include "JSScope.h"

#include "JSActivation.h"
#include "JSGlobalObject.h"
#include "JSNameScope.h"
#include "JSWithScope.h"
#include "Operations.h"

namespace JSC {

ASSERT_HAS_TRIVIAL_DESTRUCTOR(JSScope);

void JSScope::visitChildren(JSCell* cell, SlotVisitor& visitor)
{
    JSScope* thisObject = jsCast<JSScope*>(cell);
    ASSERT_GC_OBJECT_INHERITS(thisObject, &s_info);
    COMPILE_ASSERT(StructureFlags & OverridesVisitChildren, OverridesVisitChildrenWithoutSettingFlag);
    ASSERT(thisObject->structure()->typeInfo().overridesVisitChildren());

    Base::visitChildren(thisObject, visitor);
    visitor.append(&thisObject->m_next);
}

bool JSScope::isDynamicScope(bool& requiresDynamicChecks) const
{
    switch (structure()->typeInfo().type()) {
    case GlobalObjectType:
        return static_cast<const JSGlobalObject*>(this)->isDynamicScope(requiresDynamicChecks);
    case ActivationObjectType:
        return static_cast<const JSActivation*>(this)->isDynamicScope(requiresDynamicChecks);
    case NameScopeObjectType:
        return static_cast<const JSNameScope*>(this)->isDynamicScope(requiresDynamicChecks);
    default:
        RELEASE_ASSERT_NOT_REACHED();
        break;
    }

    return false;
}

JSObject* JSScope::objectAtScope(JSScope* scope)
{
    JSObject* object = scope;
    if (object->structure()->typeInfo().type() == WithScopeType)
        return jsCast<JSWithScope*>(object)->object();

    return object;
}

int JSScope::localDepth()
{
    int scopeDepth = 0;
    ScopeChainIterator iter = this->begin();
    ScopeChainIterator end = this->end();
    while (!iter->inherits(&JSActivation::s_info)) {
        ++iter;
        if (iter == end)
            break;
        ++scopeDepth;
    }
    return scopeDepth;
}

struct LookupResult {
    JSValue base() const { return m_base; }
    JSValue value() const { return m_value; }
    void setBase(JSValue base) { ASSERT(base); m_base = base; }
    void setValue(JSValue value) { ASSERT(value); m_value = value; }

private:
    JSValue m_base;
    JSValue m_value;
};


static void setPutPropertyAccessOffset(PutToBaseOperation* operation, PropertyOffset offset)
{
    ASSERT(isOutOfLineOffset(offset));
    operation->m_offset = offset;
    operation->m_offsetInButterfly = offsetInButterfly(offset);
}

static bool executeResolveOperations(CallFrame* callFrame, JSScope* scope, const Identifier& propertyName, ResolveOperation* pc, LookupResult& result)
{
    while (true) {
        switch (pc->m_operation) {
        case ResolveOperation::Fail:
            return false;
        case ResolveOperation::CheckForDynamicEntriesBeforeGlobalScope: {
            while (JSScope* nextScope = scope->next()) {
                if (scope->isActivationObject() && scope->structure() != scope->globalObject()->activationStructure())
                    return false;
                ASSERT(scope->isNameScopeObject() || scope->isVariableObject() || scope->isGlobalObject());
                scope = nextScope;
            }
            pc++;
            break;
        }
        case ResolveOperation::SetBaseToUndefined:
            result.setBase(jsUndefined());
            pc++;
            continue;
        case ResolveOperation::SetBaseToScope:
            result.setBase(scope);
            pc++;
            continue;
        case ResolveOperation::ReturnScopeAsBase:
            result.setBase(scope);
            return true;
        case ResolveOperation::SetBaseToGlobal:
            result.setBase(scope->globalObject());
            pc++;
            continue;
        case ResolveOperation::SkipScopes: {
            int count = pc->m_scopesToSkip;
            while (count--)
                scope = scope->next();
            ASSERT(scope);
            pc++;
            continue;
        }
        case ResolveOperation::SkipTopScopeNode:
            if (callFrame->r(pc->m_activationRegister).jsValue())
                scope = scope->next();
            ASSERT(scope);
            pc++;
            continue;
        case ResolveOperation::GetAndReturnScopedVar:
            ASSERT(jsCast<JSVariableObject*>(scope)->registerAt(pc->m_offset).get());
            result.setValue(jsCast<JSVariableObject*>(scope)->registerAt(pc->m_offset).get());
            return true;
        case ResolveOperation::GetAndReturnGlobalVar:
            result.setValue(pc->m_registerAddress->get());
            return true;
        case ResolveOperation::GetAndReturnGlobalVarWatchable:
            result.setValue(pc->m_registerAddress->get());
            return true;
        case ResolveOperation::ReturnGlobalObjectAsBase:
            result.setBase(callFrame->lexicalGlobalObject());
            return true;
        case ResolveOperation::GetAndReturnGlobalProperty: {
            JSGlobalObject* globalObject = scope->globalObject();
            if (globalObject->structure() == pc->m_structure.get()) {
                result.setValue(globalObject->getDirect(pc->m_offset));
                return true;
            }

            PropertySlot slot(globalObject);
            if (!globalObject->getPropertySlot(callFrame, propertyName, slot))
                return false;

            JSValue value = slot.getValue(callFrame, propertyName);
            if (callFrame->hadException())
                return false;

            Structure* structure = globalObject->structure();

            // Don't try to cache prototype lookups
            if (globalObject != slot.slotBase() || !slot.isCacheableValue() || !structure->propertyAccessesAreCacheable()) {
                result.setValue(value);
                return true;
            }

            pc->m_structure.set(callFrame->vm(), callFrame->codeBlock()->ownerExecutable(), structure);
            pc->m_offset = slot.cachedOffset();
            result.setValue(value);
            return true;
        }
        }
    }
}

template <JSScope::LookupMode mode, JSScope::ReturnValues returnValues> JSObject* JSScope::resolveContainingScopeInternal(CallFrame* callFrame, const Identifier& identifier, PropertySlot& slot, Vector<ResolveOperation>* operations, PutToBaseOperation* putToBaseOperation, bool )
{
    JSScope* scope = callFrame->scope();
    ASSERT(scope);
    int scopeCount = 0;
    bool seenGenericObjectScope = false;
    bool requiresDynamicChecks = false;
    bool skipTopScopeNode = false;
    int activationRegister = 0;
    CodeBlock* codeBlock = callFrame->codeBlock();
    if (mode == UnknownResolve) {
        ASSERT(operations->isEmpty());
        if (codeBlock->codeType() == FunctionCode && codeBlock->needsActivation()) {
            activationRegister = codeBlock->activationRegister();
            JSValue activation = callFrame->r(activationRegister).jsValue();
            
            // If the activation register doesn't match our actual scope, a dynamic
            // scope has been inserted so we shouldn't skip the top scope node.
            if (activation == scope) {
                jsCast<JSActivation*>(activation.asCell())->isDynamicScope(requiresDynamicChecks);
                if (!requiresDynamicChecks) {
                    ASSERT(jsCast<JSActivation*>(activation.asCell())->symbolTable()->get(identifier.impl()).isNull());
                    scope = scope->next();
                    ASSERT(scope);
                    skipTopScopeNode = true;
                }
            } else if (!activation)
                skipTopScopeNode = true;
        }
    } else
        ASSERT(operations->size());

    if (codeBlock->codeType() == EvalCode && scope->next())
        requiresDynamicChecks = true;

    if (mode == UnknownResolve && putToBaseOperation)
        putToBaseOperation->m_kind = PutToBaseOperation::Generic;

    do {
        JSObject* object = JSScope::objectAtScope(scope);
        slot = PropertySlot(object);

        bool currentScopeNeedsDynamicChecks = false;
        if (!(scope->isVariableObject() || scope->isNameScopeObject()) || (scope->next() && scope->isDynamicScope(currentScopeNeedsDynamicChecks)))
            seenGenericObjectScope = true;

        requiresDynamicChecks = requiresDynamicChecks || currentScopeNeedsDynamicChecks;

        if (object->getPropertySlot(callFrame, identifier, slot)) {
            if (mode == UnknownResolve) {
                if (seenGenericObjectScope)
                    goto fail;
                if (putToBaseOperation)
                    putToBaseOperation->m_isDynamic = requiresDynamicChecks;
                if (!scope->next()) {
                    // Global lookup of some kind
                    JSGlobalObject* globalObject = jsCast<JSGlobalObject*>(scope);
                    SymbolTableEntry entry = globalObject->symbolTable()->get(identifier.impl());
                    if (!entry.isNull()) {
                        if (requiresDynamicChecks)
                            operations->append(ResolveOperation::checkForDynamicEntriesBeforeGlobalScope());

                        if (putToBaseOperation) {
                            putToBaseOperation->m_isDynamic = requiresDynamicChecks;
                            if (entry.isReadOnly())
                                putToBaseOperation->m_kind = PutToBaseOperation::Readonly;
                            else if (entry.couldBeWatched()) {
                                putToBaseOperation->m_kind = PutToBaseOperation::GlobalVariablePutChecked;
                                putToBaseOperation->m_predicatePointer = entry.addressOfIsWatched();
                            } else
                                putToBaseOperation->m_kind = PutToBaseOperation::GlobalVariablePut;
                            putToBaseOperation->m_registerAddress = &globalObject->registerAt(entry.getIndex());
                        }
                        // Override custom accessor behaviour that the DOM introduces for some
                        // event handlers declared on function declarations.
                        if (!requiresDynamicChecks)
                            slot.setValue(globalObject, globalObject->registerAt(entry.getIndex()).get());
                        switch (returnValues) {
                        case ReturnValue:
                            ASSERT(!putToBaseOperation);
                            operations->append(ResolveOperation::getAndReturnGlobalVar(&globalObject->registerAt(entry.getIndex()), entry.couldBeWatched()));
                            break;
                        case ReturnBase:
                            ASSERT(putToBaseOperation);
                            operations->append(ResolveOperation::returnGlobalObjectAsBase());
                            break;
                        case ReturnBaseAndValue:
                            ASSERT(putToBaseOperation);
                            operations->append(ResolveOperation::setBaseToGlobal());
                            operations->append(ResolveOperation::getAndReturnGlobalVar(&globalObject->registerAt(entry.getIndex()), entry.couldBeWatched()));
                            break;
                        case ReturnThisAndValue:
                            ASSERT(!putToBaseOperation);
                            operations->append(ResolveOperation::setBaseToUndefined());
                            operations->append(ResolveOperation::getAndReturnGlobalVar(&globalObject->registerAt(entry.getIndex()), entry.couldBeWatched()));
                            break;
                        }
                    } else {
                        if (!slot.isCacheableValue() || slot.slotBase() != globalObject)
                            goto fail;

                        if (requiresDynamicChecks)
                            operations->append(ResolveOperation::checkForDynamicEntriesBeforeGlobalScope());

                        if (putToBaseOperation) {
                            putToBaseOperation->m_isDynamic = requiresDynamicChecks;
                            putToBaseOperation->m_kind = PutToBaseOperation::GlobalPropertyPut;
                            putToBaseOperation->m_structure.set(callFrame->vm(), callFrame->codeBlock()->ownerExecutable(), globalObject->structure());
                            setPutPropertyAccessOffset(putToBaseOperation, slot.cachedOffset());
                        }
                        switch (returnValues) {
                        case ReturnValue:
                            ASSERT(!putToBaseOperation);
                            operations->append(ResolveOperation::getAndReturnGlobalProperty());
                            break;
                        case ReturnBase:
                            ASSERT(putToBaseOperation);
                            operations->append(ResolveOperation::returnGlobalObjectAsBase());
                            break;
                        case ReturnBaseAndValue:
                            ASSERT(putToBaseOperation);
                            operations->append(ResolveOperation::setBaseToGlobal());
                            operations->append(ResolveOperation::getAndReturnGlobalProperty());
                            break;
                        case ReturnThisAndValue:
                            ASSERT(!putToBaseOperation);
                            operations->append(ResolveOperation::setBaseToUndefined());
                            operations->append(ResolveOperation::getAndReturnGlobalProperty());
                            break;
                        }
                    }
                    return object;
                }
                if (!requiresDynamicChecks) {
                    // Normal lexical lookup
                    JSVariableObject* variableObject = jsCast<JSVariableObject*>(scope);
                    ASSERT(variableObject);
                    ASSERT(variableObject->symbolTable());
                    SymbolTableEntry entry = variableObject->symbolTable()->get(identifier.impl());
                    // Defend against the variable being actually inserted by eval.
                    if (entry.isNull()) {
                        ASSERT(!jsDynamicCast<JSNameScope*>(variableObject));
                        goto fail;
                    }
                    // If we're getting the 'arguments' then give up on life.
                    if (identifier == callFrame->propertyNames().arguments)
                        goto fail;

                    if (putToBaseOperation) {
                        putToBaseOperation->m_kind = entry.isReadOnly() ? PutToBaseOperation::Readonly : PutToBaseOperation::VariablePut;
                        putToBaseOperation->m_structure.set(callFrame->vm(), callFrame->codeBlock()->ownerExecutable(), callFrame->lexicalGlobalObject()->activationStructure());
                        putToBaseOperation->m_offset = entry.getIndex();
                        putToBaseOperation->m_scopeDepth = (skipTopScopeNode ? 1 : 0) + scopeCount;
                    }

                    if (skipTopScopeNode)
                        operations->append(ResolveOperation::skipTopScopeNode(activationRegister));

                    operations->append(ResolveOperation::skipScopes(scopeCount));
                    switch (returnValues) {
                    case ReturnBaseAndValue:
                        operations->append(ResolveOperation::setBaseToScope());
                        operations->append(ResolveOperation::getAndReturnScopedVar(entry.getIndex()));
                        break;

                    case ReturnBase:
                        operations->append(ResolveOperation::returnScopeAsBase());
                        break;

                    case ReturnThisAndValue:
                        operations->append(ResolveOperation::setBaseToUndefined());
                        // fallthrough
                    case ReturnValue:
                        operations->append(ResolveOperation::getAndReturnScopedVar(entry.getIndex()));
                        break;
                    }
                    return object;
                }
            fail:
                if (!operations->size())
                    operations->append(ResolveOperation::resolveFail());
            }
            return object;
        }
        scopeCount++;
    } while ((scope = scope->next()));
    
    if (mode == UnknownResolve) {
        ASSERT(operations->isEmpty());
        if (seenGenericObjectScope) {
            operations->append(ResolveOperation::resolveFail());
            return 0;
        }
        if (putToBaseOperation) {
            putToBaseOperation->m_isDynamic = requiresDynamicChecks;
            putToBaseOperation->m_kind = PutToBaseOperation::GlobalPropertyPut;
            putToBaseOperation->m_structure.clear();
            putToBaseOperation->m_offset = -1;
        }
        if (requiresDynamicChecks)
            operations->append(ResolveOperation::checkForDynamicEntriesBeforeGlobalScope());
        switch (returnValues) {
        case ReturnValue:
            ASSERT(!putToBaseOperation);
            operations->append(ResolveOperation::getAndReturnGlobalProperty());
            break;
        case ReturnBase:
            ASSERT(putToBaseOperation);
            operations->append(ResolveOperation::returnGlobalObjectAsBase());
            break;
        case ReturnBaseAndValue:
            ASSERT(putToBaseOperation);
            operations->append(ResolveOperation::setBaseToGlobal());
            operations->append(ResolveOperation::getAndReturnGlobalProperty());
            break;
        case ReturnThisAndValue:
            ASSERT(!putToBaseOperation);
            operations->append(ResolveOperation::setBaseToUndefined());
            operations->append(ResolveOperation::getAndReturnGlobalProperty());
            break;
        }
    }
    return 0;
}

template <JSScope::ReturnValues returnValues> JSObject* JSScope::resolveContainingScope(CallFrame* callFrame, const Identifier& identifier, PropertySlot& slot, Vector<ResolveOperation>* operations, PutToBaseOperation* putToBaseOperation, bool isStrict)
{
    if (operations->size())
        return resolveContainingScopeInternal<KnownResolve, returnValues>(callFrame, identifier, slot, operations, putToBaseOperation, isStrict);
    JSObject* result = resolveContainingScopeInternal<UnknownResolve, returnValues>(callFrame, identifier, slot, operations, putToBaseOperation, isStrict);
    operations->shrinkToFit();
    return result;
}

JSValue JSScope::resolve(CallFrame* callFrame, const Identifier& identifier, ResolveOperations* operations)
{
    ASSERT(operations);
    LookupResult fastResult;
    if (operations->size() && executeResolveOperations(callFrame, callFrame->scope(), identifier, operations->data(), fastResult)) {
        ASSERT(fastResult.value());
        ASSERT(!callFrame->hadException());
        return fastResult.value();
    }

    if (callFrame->hadException())
        return JSValue();

    PropertySlot slot;
    if (JSScope::resolveContainingScope<ReturnValue>(callFrame, identifier, slot, operations, 0, false)) {
        ASSERT(operations->size());
        return slot.getValue(callFrame, identifier);
    }
    ASSERT(operations->size());

    return throwError(callFrame, createUndefinedVariableError(callFrame, identifier));
}

JSValue JSScope::resolveBase(CallFrame* callFrame, const Identifier& identifier, bool isStrict, ResolveOperations* operations, PutToBaseOperation* putToBaseOperations)
{
    ASSERT(operations);
    ASSERT_UNUSED(putToBaseOperations, putToBaseOperations);
    LookupResult fastResult;
    if (operations->size() && executeResolveOperations(callFrame, callFrame->scope(), identifier, operations->data(), fastResult)) {
        ASSERT(fastResult.base());
        ASSERT(!callFrame->hadException());
        return fastResult.base();
    }

    if (callFrame->hadException())
        return JSValue();

    PropertySlot slot;
    if (JSObject* base = JSScope::resolveContainingScope<ReturnBase>(callFrame, identifier, slot, operations, putToBaseOperations, isStrict)) {
        ASSERT(operations->size());
        return base;
    }

    if (!isStrict)
        return callFrame->lexicalGlobalObject();

    return throwError(callFrame, createErrorForInvalidGlobalAssignment(callFrame, identifier.string()));
}

JSValue JSScope::resolveWithBase(CallFrame* callFrame, const Identifier& identifier, Register* base, ResolveOperations* operations, PutToBaseOperation* putToBaseOperations)
{
    ASSERT(operations);
    ASSERT_UNUSED(putToBaseOperations, putToBaseOperations);
    LookupResult fastResult;
    if (operations->size() && executeResolveOperations(callFrame, callFrame->scope(), identifier, operations->data(), fastResult)) {
        ASSERT(fastResult.base());
        ASSERT(fastResult.value());
        ASSERT(!callFrame->hadException());
        *base = fastResult.base();
        return fastResult.value();
    }

    if (callFrame->hadException())
        return JSValue();

    PropertySlot slot;
    if (JSObject* propertyBase = JSScope::resolveContainingScope<ReturnBaseAndValue>(callFrame, identifier, slot, operations, putToBaseOperations, false)) {
        ASSERT(operations->size());
        JSValue value = slot.getValue(callFrame, identifier);
        if (callFrame->vm().exception)
            return JSValue();

        *base = propertyBase;
        return value;
    }
    ASSERT(operations->size());

    return throwError(callFrame, createUndefinedVariableError(callFrame, identifier));
}

JSValue JSScope::resolveWithThis(CallFrame* callFrame, const Identifier& identifier, Register* base, ResolveOperations* operations)
{
    ASSERT(operations);
    LookupResult fastResult;
    if (operations->size() && executeResolveOperations(callFrame, callFrame->scope(), identifier, operations->data(), fastResult)) {
        ASSERT(fastResult.base());
        ASSERT(fastResult.value());
        ASSERT(!callFrame->hadException());
        *base = fastResult.base();
        return fastResult.value();
    }

    if (callFrame->hadException())
        return JSValue();

    PropertySlot slot;
    if (JSObject* propertyBase = JSScope::resolveContainingScope<ReturnThisAndValue>(callFrame, identifier, slot, operations, 0, false)) {
        ASSERT(operations->size());
        JSValue value = slot.getValue(callFrame, identifier);
        if (callFrame->vm().exception)
            return JSValue();
        ASSERT(value);
        *base = propertyBase->structure()->typeInfo().isEnvironmentRecord() ? jsUndefined() : JSValue(propertyBase);
        return value;
    }
    ASSERT(operations->size());

    return throwError(callFrame, createUndefinedVariableError(callFrame, identifier));
}

void JSScope::resolvePut(CallFrame* callFrame, JSValue base, const Identifier& property, JSValue value, PutToBaseOperation* operation)
{
    ASSERT_UNUSED(operation, operation);
    ASSERT(base);
    ASSERT(value);
    switch (operation->m_kind) {
    case PutToBaseOperation::Uninitialised:
        CRASH();

    case PutToBaseOperation::Readonly:
        return;

    case PutToBaseOperation::GlobalVariablePutChecked:
        if (*operation->m_predicatePointer)
            goto genericHandler;
    case PutToBaseOperation::GlobalVariablePut:
        if (operation->m_isDynamic) {
            JSObject* baseObject = jsCast<JSObject*>(base);
            if (baseObject != callFrame->lexicalGlobalObject()) {
                if (baseObject->isGlobalObject())
                    ASSERT(!jsCast<JSGlobalObject*>(baseObject)->assertRegisterIsInThisObject(operation->m_registerAddress));
                goto genericHandler;
            }
        }
        operation->m_registerAddress->set(callFrame->vm(), base.asCell(), value);
        return;

    case PutToBaseOperation::VariablePut: {
        if (operation->m_isDynamic) {
            JSObject* baseObject = jsCast<JSObject*>(base);
            if (baseObject->structure() != operation->m_structure.get())
                goto genericHandler;
        }
        JSVariableObject* variableObject = jsCast<JSVariableObject*>(base);
        variableObject->registerAt(operation->m_offset).set(callFrame->vm(), variableObject, value);
        return;
    }

    case PutToBaseOperation::GlobalPropertyPut: {
        JSObject* object = jsCast<JSObject*>(base);
        if (operation->m_structure.get() != object->structure())
            break;
        object->putDirect(callFrame->vm(), operation->m_offset, value);
        return;
    }

    genericHandler:
    case PutToBaseOperation::Generic:
        PutPropertySlot slot(operation->m_isStrict);
        base.put(callFrame, property, value, slot);
        return;
    }
    ASSERT(operation->m_kind == PutToBaseOperation::GlobalPropertyPut);
    PutPropertySlot slot(operation->m_isStrict);
    base.put(callFrame, property, value, slot);
    if (!slot.isCacheable())
        return;
    if (callFrame->hadException())
        return;
    JSObject* baseObject = jsCast<JSObject*>(base);
    if (!baseObject->structure()->propertyAccessesAreCacheable())
        return;
    if (slot.base() != callFrame->lexicalGlobalObject())
        return;
    if (slot.base() != baseObject)
        return;
    ASSERT(!baseObject->hasInlineStorage());
    operation->m_structure.set(callFrame->vm(), callFrame->codeBlock()->ownerExecutable(), baseObject->structure());
    setPutPropertyAccessOffset(operation, slot.cachedOffset());
    return;
}

JSValue JSScope::resolveGlobal(CallFrame* callFrame, const Identifier& identifier, JSGlobalObject* globalObject, ResolveOperation* resolveOperation)
{
    ASSERT(resolveOperation);
    ASSERT(resolveOperation->m_operation == ResolveOperation::GetAndReturnGlobalProperty);
    ASSERT_UNUSED(globalObject, callFrame->lexicalGlobalObject() == globalObject);

    LookupResult fastResult;
    if (executeResolveOperations(callFrame, callFrame->scope(), identifier, resolveOperation, fastResult)) {
        ASSERT(fastResult.value());
        ASSERT(!callFrame->hadException());
        return fastResult.value();
    }

    if (callFrame->hadException())
        return JSValue();

    return throwError(callFrame, createUndefinedVariableError(callFrame, identifier));
}


} // namespace JSC