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