| /* |
| * 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 |