/* * Copyright (C) 2003, 2008, 2009 Apple Inc. All rights reserved. * * 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. * */ #ifndef ScopeChain_h #define ScopeChain_h #include "FastAllocBase.h" namespace JSC { class JSGlobalData; class JSGlobalObject; class JSObject; class MarkStack; class ScopeChainIterator; class ScopeChainNode : public FastAllocBase { public: ScopeChainNode(ScopeChainNode* next, JSObject* object, JSGlobalData* globalData, JSGlobalObject* globalObject, JSObject* globalThis) : next(next) , object(object) , globalData(globalData) , globalObject(globalObject) , globalThis(globalThis) , refCount(1) { ASSERT(globalData); ASSERT(globalObject); } #ifndef NDEBUG // Due to the number of subtle and timing dependent bugs that have occurred due // to deleted but still "valid" ScopeChainNodes we now deliberately clobber the // contents in debug builds. ~ScopeChainNode() { next = 0; object = 0; globalData = 0; globalObject = 0; globalThis = 0; } #endif ScopeChainNode* next; JSObject* object; JSGlobalData* globalData; JSGlobalObject* globalObject; JSObject* globalThis; int refCount; void deref() { ASSERT(refCount); if (--refCount == 0) { release();} } void ref() { ASSERT(refCount); ++refCount; } void release(); // Before calling "push" on a bare ScopeChainNode, a client should // logically "copy" the node. Later, the client can "deref" the head // of its chain of ScopeChainNodes to reclaim all the nodes it added // after the logical copy, leaving nodes added before the logical copy // (nodes shared with other clients) untouched. ScopeChainNode* copy() { ref(); return this; } ScopeChainNode* push(JSObject*); ScopeChainNode* pop(); ScopeChainIterator begin() const; ScopeChainIterator end() const; #ifndef NDEBUG void print() const; #endif }; inline ScopeChainNode* ScopeChainNode::push(JSObject* o) { ASSERT(o); return new ScopeChainNode(this, o, globalData, globalObject, globalThis); } inline ScopeChainNode* ScopeChainNode::pop() { ASSERT(next); ScopeChainNode* result = next; if (--refCount != 0) ++result->refCount; else delete this; return result; } inline void ScopeChainNode::release() { // This function is only called by deref(), // Deref ensures these conditions are true. ASSERT(refCount == 0); ScopeChainNode* n = this; do { ScopeChainNode* next = n->next; delete n; n = next; } while (n && --n->refCount == 0); } class ScopeChainIterator { public: ScopeChainIterator(const ScopeChainNode* node) : m_node(node) { } JSObject* const & operator*() const { return m_node->object; } JSObject* const * operator->() const { return &(operator*()); } ScopeChainIterator& operator++() { m_node = m_node->next; return *this; } // postfix ++ intentionally omitted bool operator==(const ScopeChainIterator& other) const { return m_node == other.m_node; } bool operator!=(const ScopeChainIterator& other) const { return m_node != other.m_node; } private: const ScopeChainNode* m_node; }; inline ScopeChainIterator ScopeChainNode::begin() const { return ScopeChainIterator(this); } inline ScopeChainIterator ScopeChainNode::end() const { return ScopeChainIterator(0); } class NoScopeChain {}; class ScopeChain { friend class JIT; public: ScopeChain(NoScopeChain) : m_node(0) { } ScopeChain(JSObject* o, JSGlobalData* globalData, JSGlobalObject* globalObject, JSObject* globalThis) : m_node(new ScopeChainNode(0, o, globalData, globalObject, globalThis)) { } ScopeChain(const ScopeChain& c) : m_node(c.m_node->copy()) { } ScopeChain& operator=(const ScopeChain& c); explicit ScopeChain(ScopeChainNode* node) : m_node(node->copy()) { } ~ScopeChain() { if (m_node) m_node->deref(); #ifndef NDEBUG m_node = 0; #endif } void swap(ScopeChain&); ScopeChainNode* node() const { return m_node; } JSObject* top() const { return m_node->object; } ScopeChainIterator begin() const { return m_node->begin(); } ScopeChainIterator end() const { return m_node->end(); } void push(JSObject* o) { m_node = m_node->push(o); } void pop() { m_node = m_node->pop(); } void clear() { m_node->deref(); m_node = 0; } JSGlobalObject* globalObject() const { return m_node->globalObject; } void markAggregate(MarkStack&) const; // Caution: this should only be used if the codeblock this is being used // with needs a full scope chain, otherwise this returns the depth of // the preceeding call frame // // Returns the depth of the current call frame's scope chain int localDepth() const; #ifndef NDEBUG void print() const { m_node->print(); } #endif private: ScopeChainNode* m_node; }; inline void ScopeChain::swap(ScopeChain& o) { ScopeChainNode* tmp = m_node; m_node = o.m_node; o.m_node = tmp; } inline ScopeChain& ScopeChain::operator=(const ScopeChain& c) { ScopeChain tmp(c); swap(tmp); return *this; } } // namespace JSC #endif // ScopeChain_h