#ifndef Watchpoint_h
#define Watchpoint_h
#include <wtf/Atomics.h>
#include <wtf/PrintStream.h>
#include <wtf/SentinelLinkedList.h>
#include <wtf/ThreadSafeRefCounted.h>
namespace JSC {
class FireDetail {
void* operator new(size_t) = delete;
public:
FireDetail()
{
}
virtual ~FireDetail()
{
}
virtual void dump(PrintStream&) const = 0;
};
class StringFireDetail : public FireDetail {
public:
StringFireDetail(const char* string)
: m_string(string)
{
}
virtual void dump(PrintStream& out) const override;
private:
const char* m_string;
};
class Watchpoint : public BasicRawSentinelNode<Watchpoint> {
public:
Watchpoint()
{
}
virtual ~Watchpoint();
void fire(const FireDetail& detail) { fireInternal(detail); }
protected:
virtual void fireInternal(const FireDetail&) = 0;
};
enum WatchpointState {
ClearWatchpoint,
IsWatched,
IsInvalidated
};
class InlineWatchpointSet;
class WatchpointSet : public ThreadSafeRefCounted<WatchpointSet> {
friend class LLIntOffsetsExtractor;
public:
JS_EXPORT_PRIVATE WatchpointSet(WatchpointState);
JS_EXPORT_PRIVATE ~WatchpointSet();
WatchpointState stateOnJSThread() const
{
return static_cast<WatchpointState>(m_state);
}
WatchpointState state() const
{
WTF::loadLoadFence();
WatchpointState result = static_cast<WatchpointState>(m_state);
WTF::loadLoadFence();
return result;
}
bool isStillValid() const
{
return state() != IsInvalidated;
}
bool hasBeenInvalidated() const { return !isStillValid(); }
void add(Watchpoint*);
void startWatching()
{
ASSERT(m_state != IsInvalidated);
if (m_state == IsWatched)
return;
WTF::storeStoreFence();
m_state = IsWatched;
WTF::storeStoreFence();
}
void fireAll(const FireDetail& detail)
{
if (LIKELY(m_state != IsWatched))
return;
fireAllSlow(detail);
}
void fireAll(const char* reason)
{
if (LIKELY(m_state != IsWatched))
return;
fireAllSlow(reason);
}
void touch(const FireDetail& detail)
{
if (state() == ClearWatchpoint)
startWatching();
else
fireAll(detail);
}
void touch(const char* reason)
{
touch(StringFireDetail(reason));
}
void invalidate(const FireDetail& detail)
{
if (state() == IsWatched)
fireAll(detail);
m_state = IsInvalidated;
}
void invalidate(const char* reason)
{
invalidate(StringFireDetail(reason));
}
int8_t* addressOfState() { return &m_state; }
int8_t* addressOfSetIsNotEmpty() { return &m_setIsNotEmpty; }
JS_EXPORT_PRIVATE void fireAllSlow(const FireDetail&); JS_EXPORT_PRIVATE void fireAllSlow(const char* reason);
private:
void fireAllWatchpoints(const FireDetail&);
friend class InlineWatchpointSet;
int8_t m_state;
int8_t m_setIsNotEmpty;
SentinelLinkedList<Watchpoint, BasicRawSentinelNode<Watchpoint>> m_set;
};
class InlineWatchpointSet {
WTF_MAKE_NONCOPYABLE(InlineWatchpointSet);
public:
InlineWatchpointSet(WatchpointState state)
: m_data(encodeState(state))
{
}
~InlineWatchpointSet()
{
if (isThin())
return;
freeFat();
}
WatchpointState stateOnJSThread() const
{
uintptr_t data = m_data;
if (isFat(data))
return fat(data)->stateOnJSThread();
return decodeState(data);
}
WatchpointState state() const
{
WTF::loadLoadFence();
uintptr_t data = m_data;
WTF::loadLoadFence();
if (isFat(data))
return fat(data)->state();
return decodeState(data);
}
bool hasBeenInvalidated() const
{
return state() == IsInvalidated;
}
bool isStillValid() const
{
return !hasBeenInvalidated();
}
void add(Watchpoint*);
void startWatching()
{
if (isFat()) {
fat()->startWatching();
return;
}
ASSERT(decodeState(m_data) != IsInvalidated);
m_data = encodeState(IsWatched);
}
void fireAll(const FireDetail& detail)
{
if (isFat()) {
fat()->fireAll(detail);
return;
}
if (decodeState(m_data) == ClearWatchpoint)
return;
m_data = encodeState(IsInvalidated);
WTF::storeStoreFence();
}
void invalidate(const FireDetail& detail)
{
if (isFat())
fat()->invalidate(detail);
else
m_data = encodeState(IsInvalidated);
}
JS_EXPORT_PRIVATE void fireAll(const char* reason);
void touch(const FireDetail& detail)
{
if (isFat()) {
fat()->touch(detail);
return;
}
uintptr_t data = m_data;
if (decodeState(data) == IsInvalidated)
return;
WTF::storeStoreFence();
if (decodeState(data) == ClearWatchpoint)
m_data = encodeState(IsWatched);
else
m_data = encodeState(IsInvalidated);
WTF::storeStoreFence();
}
void touch(const char* reason)
{
touch(StringFireDetail(reason));
}
private:
static const uintptr_t IsThinFlag = 1;
static const uintptr_t StateMask = 6;
static const uintptr_t StateShift = 1;
static bool isThin(uintptr_t data) { return data & IsThinFlag; }
static bool isFat(uintptr_t data) { return !isThin(data); }
static WatchpointState decodeState(uintptr_t data)
{
ASSERT(isThin(data));
return static_cast<WatchpointState>((data & StateMask) >> StateShift);
}
static uintptr_t encodeState(WatchpointState state)
{
return (static_cast<uintptr_t>(state) << StateShift) | IsThinFlag;
}
bool isThin() const { return isThin(m_data); }
bool isFat() const { return isFat(m_data); };
static WatchpointSet* fat(uintptr_t data)
{
return bitwise_cast<WatchpointSet*>(data);
}
WatchpointSet* fat()
{
ASSERT(isFat());
return fat(m_data);
}
const WatchpointSet* fat() const
{
ASSERT(isFat());
return fat(m_data);
}
WatchpointSet* inflate()
{
if (LIKELY(isFat()))
return fat();
return inflateSlow();
}
JS_EXPORT_PRIVATE WatchpointSet* inflateSlow();
JS_EXPORT_PRIVATE void freeFat();
uintptr_t m_data;
};
}
#endif // Watchpoint_h