blob: 2073a53d056b023b303c98e476d3c14831f86da5 [file] [log] [blame]
/*
* 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"
namespace JSC {
ASSERT_CLASS_FITS_IN_CELL(JSScope);
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:
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;
}
JSValue JSScope::resolve(CallFrame* callFrame, const Identifier& identifier)
{
JSScope* scope = callFrame->scope();
ASSERT(scope);
do {
JSObject* object = JSScope::objectAtScope(scope);
PropertySlot slot(object);
if (object->getPropertySlot(callFrame, identifier, slot))
return slot.getValue(callFrame, identifier);
} while ((scope = scope->next()));
return throwError(callFrame, createUndefinedVariableError(callFrame, identifier));
}
JSValue JSScope::resolveSkip(CallFrame* callFrame, const Identifier& identifier, int skip)
{
JSScope* scope = callFrame->scope();
ASSERT(scope);
CodeBlock* codeBlock = callFrame->codeBlock();
bool checkTopLevel = codeBlock->codeType() == FunctionCode && codeBlock->needsFullScopeChain();
ASSERT(skip || !checkTopLevel);
if (checkTopLevel && skip--) {
if (callFrame->uncheckedR(codeBlock->activationRegister()).jsValue())
scope = scope->next();
}
while (skip--) {
scope = scope->next();
ASSERT(scope);
}
do {
JSObject* object = JSScope::objectAtScope(scope);
PropertySlot slot(object);
if (object->getPropertySlot(callFrame, identifier, slot))
return slot.getValue(callFrame, identifier);
} while ((scope = scope->next()));
return throwError(callFrame, createUndefinedVariableError(callFrame, identifier));
}
JSValue JSScope::resolveGlobal(
CallFrame* callFrame,
const Identifier& identifier,
JSGlobalObject* globalObject,
WriteBarrierBase<Structure>* cachedStructure,
PropertyOffset* cachedOffset
)
{
if (globalObject->structure() == cachedStructure->get())
return globalObject->getDirectOffset(*cachedOffset);
PropertySlot slot(globalObject);
if (!globalObject->getPropertySlot(callFrame, identifier, slot))
return throwError(callFrame, createUndefinedVariableError(callFrame, identifier));
JSValue result = slot.getValue(callFrame, identifier);
if (callFrame->globalData().exception)
return JSValue();
if (slot.isCacheableValue() && !globalObject->structure()->isUncacheableDictionary() && slot.slotBase() == globalObject) {
cachedStructure->set(callFrame->globalData(), callFrame->codeBlock()->ownerExecutable(), globalObject->structure());
*cachedOffset = slot.cachedOffset();
}
return result;
}
JSValue JSScope::resolveGlobalDynamic(
CallFrame* callFrame,
const Identifier& identifier,
int skip,
WriteBarrierBase<Structure>* cachedStructure,
PropertyOffset* cachedOffset
)
{
JSScope* scope = callFrame->scope();
ASSERT(scope);
CodeBlock* codeBlock = callFrame->codeBlock();
bool checkTopLevel = codeBlock->codeType() == FunctionCode && codeBlock->needsFullScopeChain();
ASSERT(skip || !checkTopLevel);
if (checkTopLevel && skip--) {
if (callFrame->uncheckedR(codeBlock->activationRegister()).jsValue())
scope = scope->next();
}
while (skip--) {
JSObject* object = JSScope::objectAtScope(scope);
if (!object->hasCustomProperties())
continue;
PropertySlot slot(object);
if (!object->getPropertySlot(callFrame, identifier, slot))
continue;
JSValue result = slot.getValue(callFrame, identifier);
if (callFrame->globalData().exception)
return JSValue();
return result;
}
return resolveGlobal(callFrame, identifier, callFrame->lexicalGlobalObject(), cachedStructure, cachedOffset);
}
JSValue JSScope::resolveBase(CallFrame* callFrame, const Identifier& identifier, bool isStrict)
{
JSScope* scope = callFrame->scope();
ASSERT(scope);
do {
JSObject* object = JSScope::objectAtScope(scope);
PropertySlot slot(object);
if (!object->getPropertySlot(callFrame, identifier, slot))
continue;
return JSValue(object);
} while ((scope = scope->next()));
if (!isStrict)
return callFrame->lexicalGlobalObject();
return throwError(callFrame, createErrorForInvalidGlobalAssignment(callFrame, identifier.string()));
}
JSValue JSScope::resolveWithBase(CallFrame* callFrame, const Identifier& identifier, Register* base)
{
JSScope* scope = callFrame->scope();
ASSERT(scope);
do {
JSObject* object = JSScope::objectAtScope(scope);
PropertySlot slot(object);
if (!object->getPropertySlot(callFrame, identifier, slot))
continue;
JSValue value = slot.getValue(callFrame, identifier);
if (callFrame->globalData().exception)
return JSValue();
*base = JSValue(object);
return value;
} while ((scope = scope->next()));
return throwError(callFrame, createUndefinedVariableError(callFrame, identifier));
}
JSValue JSScope::resolveWithThis(CallFrame* callFrame, const Identifier& identifier, Register* base)
{
JSScope* scope = callFrame->scope();
ASSERT(scope);
do {
JSObject* object = JSScope::objectAtScope(scope);
PropertySlot slot(object);
if (!object->getPropertySlot(callFrame, identifier, slot))
continue;
JSValue value = slot.getValue(callFrame, identifier);
if (callFrame->globalData().exception)
return JSValue();
*base = object->structure()->typeInfo().isEnvironmentRecord() ? jsUndefined() : JSValue(object);
return value;
} while ((scope = scope->next()));
return throwError(callFrame, createUndefinedVariableError(callFrame, identifier));
}
} // namespace JSC