blob: 7be0c1dfafcd03f23c00f3ff32f8137ef338d808 [file] [log] [blame]
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sts=4 et sw=4 tw=99:
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef gc_Barrier_inl_h
#define gc_Barrier_inl_h
#include "gc/Barrier.h"
#include "gc/Marking.h"
#include "gc/StoreBuffer.h"
#include "vm/String-inl.h"
namespace js {
JS_ALWAYS_INLINE JS::Zone *
ZoneOfValue(const JS::Value &value)
{
JS_ASSERT(value.isMarkable());
if (value.isObject())
return value.toObject().zone();
return static_cast<js::gc::Cell *>(value.toGCThing())->tenuredZone();
}
template <typename T, typename Unioned>
void
EncapsulatedPtr<T, Unioned>::pre()
{
T::writeBarrierPre(value);
}
template <typename T>
inline void
RelocatablePtr<T>::post()
{
#ifdef JSGC_GENERATIONAL
JS_ASSERT(this->value);
this->value->runtime()->gcStoreBuffer.putRelocatableCell((gc::Cell **)&this->value);
#endif
}
template <typename T>
inline void
RelocatablePtr<T>::relocate(JSRuntime *rt)
{
#ifdef JSGC_GENERATIONAL
rt->gcStoreBuffer.removeRelocatableCell((gc::Cell **)&this->value);
#endif
}
inline
EncapsulatedValue::~EncapsulatedValue()
{
pre();
}
inline void
EncapsulatedValue::init(const Value &v)
{
JS_ASSERT(!IsPoisonedValue(v));
value = v;
}
inline void
EncapsulatedValue::init(JSRuntime *rt, const Value &v)
{
JS_ASSERT(!IsPoisonedValue(v));
value = v;
}
inline EncapsulatedValue &
EncapsulatedValue::operator=(const Value &v)
{
pre();
JS_ASSERT(!IsPoisonedValue(v));
value = v;
return *this;
}
inline EncapsulatedValue &
EncapsulatedValue::operator=(const EncapsulatedValue &v)
{
pre();
JS_ASSERT(!IsPoisonedValue(v));
value = v.get();
return *this;
}
inline void
EncapsulatedValue::writeBarrierPre(const Value &value)
{
#ifdef JSGC_INCREMENTAL
if (value.isMarkable() && runtime(value)->needsBarrier())
writeBarrierPre(ZoneOfValue(value), value);
#endif
}
inline void
EncapsulatedValue::writeBarrierPre(Zone *zone, const Value &value)
{
#ifdef JSGC_INCREMENTAL
if (zone->needsBarrier()) {
JS_ASSERT_IF(value.isMarkable(), runtime(value)->needsBarrier());
Value tmp(value);
js::gc::MarkValueUnbarriered(zone->barrierTracer(), &tmp, "write barrier");
JS_ASSERT(tmp == value);
}
#endif
}
inline void
EncapsulatedValue::pre()
{
writeBarrierPre(value);
}
inline void
EncapsulatedValue::pre(Zone *zone)
{
writeBarrierPre(zone, value);
}
inline
HeapValue::HeapValue()
: EncapsulatedValue(UndefinedValue())
{
post();
}
inline
HeapValue::HeapValue(const Value &v)
: EncapsulatedValue(v)
{
JS_ASSERT(!IsPoisonedValue(v));
post();
}
inline
HeapValue::HeapValue(const HeapValue &v)
: EncapsulatedValue(v.value)
{
JS_ASSERT(!IsPoisonedValue(v.value));
post();
}
inline
HeapValue::~HeapValue()
{
pre();
}
inline void
HeapValue::init(const Value &v)
{
JS_ASSERT(!IsPoisonedValue(v));
value = v;
post();
}
inline void
HeapValue::init(JSRuntime *rt, const Value &v)
{
JS_ASSERT(!IsPoisonedValue(v));
value = v;
post(rt);
}
inline HeapValue &
HeapValue::operator=(const Value &v)
{
pre();
JS_ASSERT(!IsPoisonedValue(v));
value = v;
post();
return *this;
}
inline HeapValue &
HeapValue::operator=(const HeapValue &v)
{
pre();
JS_ASSERT(!IsPoisonedValue(v.value));
value = v.value;
post();
return *this;
}
inline void
HeapValue::set(Zone *zone, const Value &v)
{
#ifdef DEBUG
if (value.isMarkable()) {
JS_ASSERT(ZoneOfValue(value) == zone ||
ZoneOfValue(value) == zone->rt->atomsCompartment->zone());
}
#endif
pre(zone);
JS_ASSERT(!IsPoisonedValue(v));
value = v;
post(zone->rt);
}
inline void
HeapValue::writeBarrierPost(const Value &value, Value *addr)
{
#ifdef JSGC_GENERATIONAL
if (value.isMarkable())
runtime(value)->gcStoreBuffer.putValue(addr);
#endif
}
inline void
HeapValue::writeBarrierPost(JSRuntime *rt, const Value &value, Value *addr)
{
#ifdef JSGC_GENERATIONAL
if (value.isMarkable())
rt->gcStoreBuffer.putValue(addr);
#endif
}
inline void
HeapValue::post()
{
writeBarrierPost(value, &value);
}
inline void
HeapValue::post(JSRuntime *rt)
{
writeBarrierPost(rt, value, &value);
}
inline
RelocatableValue::RelocatableValue()
: EncapsulatedValue(UndefinedValue())
{
}
inline
RelocatableValue::RelocatableValue(const Value &v)
: EncapsulatedValue(v)
{
JS_ASSERT(!IsPoisonedValue(v));
if (v.isMarkable())
post();
}
inline
RelocatableValue::RelocatableValue(const RelocatableValue &v)
: EncapsulatedValue(v.value)
{
JS_ASSERT(!IsPoisonedValue(v.value));
if (v.value.isMarkable())
post();
}
inline
RelocatableValue::~RelocatableValue()
{
if (value.isMarkable())
relocate(runtime(value));
}
inline RelocatableValue &
RelocatableValue::operator=(const Value &v)
{
pre();
JS_ASSERT(!IsPoisonedValue(v));
if (v.isMarkable()) {
value = v;
post();
} else if (value.isMarkable()) {
JSRuntime *rt = runtime(value);
value = v;
relocate(rt);
} else {
value = v;
}
return *this;
}
inline RelocatableValue &
RelocatableValue::operator=(const RelocatableValue &v)
{
pre();
JS_ASSERT(!IsPoisonedValue(v.value));
if (v.value.isMarkable()) {
value = v.value;
post();
} else if (value.isMarkable()) {
JSRuntime *rt = runtime(value);
value = v.value;
relocate(rt);
} else {
value = v.value;
}
return *this;
}
inline void
RelocatableValue::post()
{
#ifdef JSGC_GENERATIONAL
JS_ASSERT(value.isMarkable());
runtime(value)->gcStoreBuffer.putRelocatableValue(&value);
#endif
}
inline void
RelocatableValue::relocate(JSRuntime *rt)
{
#ifdef JSGC_GENERATIONAL
rt->gcStoreBuffer.removeRelocatableValue(&value);
#endif
}
inline
HeapSlot::HeapSlot(JSObject *obj, Kind kind, uint32_t slot, const Value &v)
: EncapsulatedValue(v)
{
JS_ASSERT(!IsPoisonedValue(v));
post(obj, kind, slot);
}
inline
HeapSlot::HeapSlot(JSObject *obj, Kind kind, uint32_t slot, const HeapSlot &s)
: EncapsulatedValue(s.value)
{
JS_ASSERT(!IsPoisonedValue(s.value));
post(obj, kind, slot);
}
inline
HeapSlot::~HeapSlot()
{
pre();
}
inline void
HeapSlot::init(JSObject *obj, Kind kind, uint32_t slot, const Value &v)
{
value = v;
post(obj, kind, slot);
}
inline void
HeapSlot::init(JSRuntime *rt, JSObject *obj, Kind kind, uint32_t slot, const Value &v)
{
value = v;
post(rt, obj, kind, slot);
}
inline void
HeapSlot::set(JSObject *obj, Kind kind, uint32_t slot, const Value &v)
{
JS_ASSERT_IF(kind == Slot, &obj->getSlotRef(slot) == this);
JS_ASSERT_IF(kind == Element, &obj->getDenseElement(slot) == (const Value *)this);
pre();
JS_ASSERT(!IsPoisonedValue(v));
value = v;
post(obj, kind, slot);
}
inline void
HeapSlot::set(Zone *zone, JSObject *obj, Kind kind, uint32_t slot, const Value &v)
{
JS_ASSERT_IF(kind == Slot, &obj->getSlotRef(slot) == this);
JS_ASSERT_IF(kind == Element, &obj->getDenseElement(slot) == (const Value *)this);
JS_ASSERT(obj->zone() == zone);
pre(zone);
JS_ASSERT(!IsPoisonedValue(v));
value = v;
post(zone->rt, obj, kind, slot);
}
inline void
HeapSlot::writeBarrierPost(JSObject *obj, Kind kind, uint32_t slot)
{
#ifdef JSGC_GENERATIONAL
obj->runtime()->gcStoreBuffer.putSlot(obj, kind, slot);
#endif
}
inline void
HeapSlot::writeBarrierPost(JSRuntime *rt, JSObject *obj, Kind kind, uint32_t slot)
{
#ifdef JSGC_GENERATIONAL
rt->gcStoreBuffer.putSlot(obj, kind, slot);
#endif
}
inline void
HeapSlot::post(JSObject *owner, Kind kind, uint32_t slot)
{
HeapSlot::writeBarrierPost(owner, kind, slot);
}
inline void
HeapSlot::post(JSRuntime *rt, JSObject *owner, Kind kind, uint32_t slot)
{
HeapSlot::writeBarrierPost(rt, owner, kind, slot);
}
#ifdef JSGC_GENERATIONAL
class DenseRangeRef : public gc::BufferableRef
{
JSObject *owner;
uint32_t start;
uint32_t end;
public:
DenseRangeRef(JSObject *obj, uint32_t start, uint32_t end)
: owner(obj), start(start), end(end)
{
JS_ASSERT(start < end);
}
void mark(JSTracer *trc) {
/* Apply forwarding, if we have already visited owner. */
IsObjectMarked(&owner);
uint32_t initLen = owner->getDenseInitializedLength();
uint32_t clampedStart = Min(start, initLen);
gc::MarkArraySlots(trc, Min(end, initLen) - clampedStart,
owner->getDenseElements() + clampedStart, "element");
}
};
#endif
inline void
DenseRangeWriteBarrierPost(JSRuntime *rt, JSObject *obj, uint32_t start, uint32_t count)
{
#ifdef JSGC_GENERATIONAL
if (count > 0)
rt->gcStoreBuffer.putGeneric(DenseRangeRef(obj, start, start + count));
#endif
}
/*
* This is a post barrier for HashTables whose key can be moved during a GC.
*/
template <class Map, class Key>
inline void
HashTableWriteBarrierPost(JSRuntime *rt, Map *map, const Key &key)
{
#ifdef JSGC_GENERATIONAL
if (key && IsInsideNursery(rt, key))
rt->gcStoreBuffer.putGeneric(gc::HashKeyRef<Map, Key>(map, key));
#endif
}
inline
EncapsulatedId::~EncapsulatedId()
{
pre();
}
inline EncapsulatedId &
EncapsulatedId::operator=(const EncapsulatedId &v)
{
if (v.value != value)
pre();
JS_ASSERT(!IsPoisonedId(v.value));
value = v.value;
return *this;
}
inline void
EncapsulatedId::pre()
{
#ifdef JSGC_INCREMENTAL
if (JSID_IS_OBJECT(value)) {
JSObject *obj = JSID_TO_OBJECT(value);
Zone *zone = obj->zone();
if (zone->needsBarrier()) {
js::gc::MarkObjectUnbarriered(zone->barrierTracer(), &obj, "write barrier");
JS_ASSERT(obj == JSID_TO_OBJECT(value));
}
} else if (JSID_IS_STRING(value)) {
JSString *str = JSID_TO_STRING(value);
Zone *zone = str->zone();
if (zone->needsBarrier()) {
js::gc::MarkStringUnbarriered(zone->barrierTracer(), &str, "write barrier");
JS_ASSERT(str == JSID_TO_STRING(value));
}
}
#endif
}
inline
RelocatableId::~RelocatableId()
{
pre();
}
inline RelocatableId &
RelocatableId::operator=(jsid id)
{
if (id != value)
pre();
JS_ASSERT(!IsPoisonedId(id));
value = id;
return *this;
}
inline RelocatableId &
RelocatableId::operator=(const RelocatableId &v)
{
if (v.value != value)
pre();
JS_ASSERT(!IsPoisonedId(v.value));
value = v.value;
return *this;
}
inline
HeapId::HeapId(jsid id)
: EncapsulatedId(id)
{
JS_ASSERT(!IsPoisonedId(id));
post();
}
inline
HeapId::~HeapId()
{
pre();
}
inline void
HeapId::init(jsid id)
{
JS_ASSERT(!IsPoisonedId(id));
value = id;
post();
}
inline void
HeapId::post()
{
}
inline HeapId &
HeapId::operator=(jsid id)
{
if (id != value)
pre();
JS_ASSERT(!IsPoisonedId(id));
value = id;
post();
return *this;
}
inline HeapId &
HeapId::operator=(const HeapId &v)
{
if (v.value != value)
pre();
JS_ASSERT(!IsPoisonedId(v.value));
value = v.value;
post();
return *this;
}
inline const Value &
ReadBarrieredValue::get() const
{
if (value.isObject())
JSObject::readBarrier(&value.toObject());
else if (value.isString())
JSString::readBarrier(value.toString());
else
JS_ASSERT(!value.isMarkable());
return value;
}
inline
ReadBarrieredValue::operator const Value &() const
{
return get();
}
inline JSObject &
ReadBarrieredValue::toObject() const
{
return get().toObject();
}
} /* namespace js */
#endif /* gc_Barrier_inl_h */