blob: f9122420f894accbdd48c5bdb7e005b6f3cbff56 [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 vm_Shape_inl_h
#define vm_Shape_inl_h
#include "mozilla/PodOperations.h"
#include "jsarray.h"
#include "jsbool.h"
#include "jscntxt.h"
#include "jsdbgapi.h"
#include "jsfun.h"
#include "jsgc.h"
#include "jsobj.h"
#include "gc/Marking.h"
#include "vm/ArgumentsObject.h"
#include "vm/ScopeObject.h"
#include "vm/StringObject.h"
#include "jsatominlines.h"
#include "jscntxtinlines.h"
#include "jsgcinlines.h"
#include "vm/ScopeObject-inl.h"
namespace js {
static inline void
GetterSetterWriteBarrierPost(JSRuntime *rt, JSObject **objp)
{
#ifdef JSGC_GENERATIONAL
rt->gcStoreBuffer.putRelocatableCell(reinterpret_cast<gc::Cell **>(objp));
#endif
}
static inline void
GetterSetterWriteBarrierPostRemove(JSRuntime *rt, JSObject **objp)
{
#ifdef JSGC_GENERATIONAL
rt->gcStoreBuffer.removeRelocatableCell(reinterpret_cast<gc::Cell **>(objp));
#endif
}
inline
BaseShape::BaseShape(JSCompartment *comp, Class *clasp, JSObject *parent, JSObject *metadata,
uint32_t objectFlags)
{
JS_ASSERT(!(objectFlags & ~OBJECT_FLAG_MASK));
mozilla::PodZero(this);
this->clasp = clasp;
this->parent = parent;
this->metadata = metadata;
this->flags = objectFlags;
this->compartment_ = comp;
}
inline
BaseShape::BaseShape(JSCompartment *comp, Class *clasp, JSObject *parent, JSObject *metadata,
uint32_t objectFlags, uint8_t attrs,
PropertyOp rawGetter, StrictPropertyOp rawSetter)
{
JS_ASSERT(!(objectFlags & ~OBJECT_FLAG_MASK));
mozilla::PodZero(this);
this->clasp = clasp;
this->parent = parent;
this->metadata = metadata;
this->flags = objectFlags;
this->rawGetter = rawGetter;
this->rawSetter = rawSetter;
if ((attrs & JSPROP_GETTER) && rawGetter) {
this->flags |= HAS_GETTER_OBJECT;
GetterSetterWriteBarrierPost(runtime(), &this->getterObj);
}
if ((attrs & JSPROP_SETTER) && rawSetter) {
this->flags |= HAS_SETTER_OBJECT;
GetterSetterWriteBarrierPost(runtime(), &this->setterObj);
}
this->compartment_ = comp;
}
inline
BaseShape::BaseShape(const StackBaseShape &base)
{
mozilla::PodZero(this);
this->clasp = base.clasp;
this->parent = base.parent;
this->metadata = base.metadata;
this->flags = base.flags;
this->rawGetter = base.rawGetter;
this->rawSetter = base.rawSetter;
if ((base.flags & HAS_GETTER_OBJECT) && base.rawGetter)
GetterSetterWriteBarrierPost(runtime(), &this->getterObj);
if ((base.flags & HAS_SETTER_OBJECT) && base.rawSetter)
GetterSetterWriteBarrierPost(runtime(), &this->setterObj);
this->compartment_ = base.compartment;
}
inline BaseShape &
BaseShape::operator=(const BaseShape &other)
{
clasp = other.clasp;
parent = other.parent;
metadata = other.metadata;
flags = other.flags;
slotSpan_ = other.slotSpan_;
if (flags & HAS_GETTER_OBJECT) {
getterObj = other.getterObj;
GetterSetterWriteBarrierPost(runtime(), &getterObj);
} else {
if (rawGetter)
GetterSetterWriteBarrierPostRemove(runtime(), &getterObj);
rawGetter = other.rawGetter;
}
if (flags & HAS_SETTER_OBJECT) {
setterObj = other.setterObj;
GetterSetterWriteBarrierPost(runtime(), &setterObj);
} else {
if (rawSetter)
GetterSetterWriteBarrierPostRemove(runtime(), &setterObj);
rawSetter = other.rawSetter;
}
compartment_ = other.compartment_;
return *this;
}
inline bool
BaseShape::matchesGetterSetter(PropertyOp rawGetter, StrictPropertyOp rawSetter) const
{
return rawGetter == this->rawGetter && rawSetter == this->rawSetter;
}
inline
StackBaseShape::StackBaseShape(Shape *shape)
: flags(shape->getObjectFlags()),
clasp(shape->getObjectClass()),
parent(shape->getObjectParent()),
metadata(shape->getObjectMetadata()),
compartment(shape->compartment())
{
updateGetterSetter(shape->attrs, shape->getter(), shape->setter());
}
inline void
StackBaseShape::updateGetterSetter(uint8_t attrs,
PropertyOp rawGetter,
StrictPropertyOp rawSetter)
{
flags &= ~(BaseShape::HAS_GETTER_OBJECT | BaseShape::HAS_SETTER_OBJECT);
if ((attrs & JSPROP_GETTER) && rawGetter) {
JS_ASSERT(!IsPoisonedPtr(rawGetter));
flags |= BaseShape::HAS_GETTER_OBJECT;
}
if ((attrs & JSPROP_SETTER) && rawSetter) {
JS_ASSERT(!IsPoisonedPtr(rawSetter));
flags |= BaseShape::HAS_SETTER_OBJECT;
}
this->rawGetter = rawGetter;
this->rawSetter = rawSetter;
}
inline void
BaseShape::adoptUnowned(UnownedBaseShape *other)
{
/*
* This is a base shape owned by a dictionary object, update it to reflect the
* unowned base shape of a new last property.
*/
JS_ASSERT(isOwned());
uint32_t span = slotSpan();
ShapeTable *table = &this->table();
*this = *other;
setOwned(other);
setTable(table);
setSlotSpan(span);
assertConsistency();
}
inline void
BaseShape::setOwned(UnownedBaseShape *unowned)
{
flags |= OWNED_SHAPE;
this->unowned_ = unowned;
}
inline void
BaseShape::assertConsistency()
{
#ifdef DEBUG
if (isOwned()) {
UnownedBaseShape *unowned = baseUnowned();
JS_ASSERT(hasGetterObject() == unowned->hasGetterObject());
JS_ASSERT(hasSetterObject() == unowned->hasSetterObject());
JS_ASSERT_IF(hasGetterObject(), getterObject() == unowned->getterObject());
JS_ASSERT_IF(hasSetterObject(), setterObject() == unowned->setterObject());
JS_ASSERT(getObjectParent() == unowned->getObjectParent());
JS_ASSERT(getObjectMetadata() == unowned->getObjectMetadata());
JS_ASSERT(getObjectFlags() == unowned->getObjectFlags());
}
#endif
}
inline
Shape::Shape(const StackShape &other, uint32_t nfixed)
: base_(other.base),
propid_(other.propid),
slotInfo(other.maybeSlot() | (nfixed << FIXED_SLOTS_SHIFT)),
attrs(other.attrs),
flags(other.flags),
shortid_(other.shortid),
parent(NULL)
{
kids.setNull();
}
inline
Shape::Shape(UnownedBaseShape *base, uint32_t nfixed)
: base_(base),
propid_(JSID_EMPTY),
slotInfo(SHAPE_INVALID_SLOT | (nfixed << FIXED_SLOTS_SHIFT)),
attrs(JSPROP_SHARED),
flags(0),
shortid_(0),
parent(NULL)
{
JS_ASSERT(base);
kids.setNull();
}
inline HashNumber
StackShape::hash() const
{
HashNumber hash = uintptr_t(base);
/* Accumulate from least to most random so the low bits are most random. */
hash = JS_ROTATE_LEFT32(hash, 4) ^ (flags & Shape::PUBLIC_FLAGS);
hash = JS_ROTATE_LEFT32(hash, 4) ^ attrs;
hash = JS_ROTATE_LEFT32(hash, 4) ^ shortid;
hash = JS_ROTATE_LEFT32(hash, 4) ^ slot_;
hash = JS_ROTATE_LEFT32(hash, 4) ^ JSID_BITS(propid);
return hash;
}
inline bool
Shape::matches(const Shape *other) const
{
return propid_.get() == other->propid_.get() &&
matchesParamsAfterId(other->base(), other->maybeSlot(), other->attrs,
other->flags, other->shortid_);
}
inline bool
Shape::matches(const StackShape &other) const
{
return propid_.get() == other.propid &&
matchesParamsAfterId(other.base, other.slot_, other.attrs, other.flags, other.shortid);
}
inline bool
Shape::matchesParamsAfterId(BaseShape *base, uint32_t aslot,
unsigned aattrs, unsigned aflags, int ashortid) const
{
return base->unowned() == this->base()->unowned() &&
maybeSlot() == aslot &&
attrs == aattrs &&
((flags ^ aflags) & PUBLIC_FLAGS) == 0 &&
shortid_ == ashortid;
}
inline bool
Shape::getUserId(JSContext *cx, MutableHandleId idp) const
{
const Shape *self = this;
#ifdef DEBUG
{
SkipRoot skip(cx, &self);
MaybeCheckStackRoots(cx);
}
#endif
if (self->hasShortID()) {
int16_t id = self->shortid();
if (id < 0) {
RootedValue v(cx, Int32Value(id));
return ValueToId<CanGC>(cx, v, idp);
}
idp.set(INT_TO_JSID(id));
} else {
idp.set(self->propid());
}
return true;
}
inline bool
Shape::get(JSContext* cx, HandleObject receiver, JSObject* obj, JSObject *pobj, MutableHandleValue vp)
{
JS_ASSERT(!hasDefaultGetter());
if (hasGetterValue()) {
Value fval = getterValue();
return InvokeGetterOrSetter(cx, receiver, fval, 0, 0, vp.address());
}
Rooted<Shape *> self(cx, this);
RootedId id(cx);
if (!self->getUserId(cx, &id))
return false;
return CallJSPropertyOp(cx, self->getterOp(), receiver, id, vp);
}
inline bool
Shape::set(JSContext* cx, HandleObject obj, HandleObject receiver, bool strict, MutableHandleValue vp)
{
JS_ASSERT_IF(hasDefaultSetter(), hasGetterValue());
if (attrs & JSPROP_SETTER) {
Value fval = setterValue();
return InvokeGetterOrSetter(cx, receiver, fval, 1, vp.address(), vp.address());
}
if (attrs & JSPROP_GETTER)
return js_ReportGetterOnlyAssignment(cx);
Rooted<Shape *> self(cx, this);
RootedId id(cx);
if (!self->getUserId(cx, &id))
return false;
/*
* |with (it) color='red';| ends up here.
* Avoid exposing the With object to native setters.
*/
if (obj->is<WithObject>()) {
RootedObject nobj(cx, &obj->as<WithObject>().object());
return CallJSPropertyOpSetter(cx, self->setterOp(), nobj, id, strict, vp);
}
return CallJSPropertyOpSetter(cx, self->setterOp(), obj, id, strict, vp);
}
inline void
Shape::setParent(Shape *p)
{
JS_ASSERT_IF(p && !p->hasMissingSlot() && !inDictionary(),
p->maybeSlot() <= maybeSlot());
JS_ASSERT_IF(p && !inDictionary(),
hasSlot() == (p->maybeSlot() != maybeSlot()));
parent = p;
}
inline void
Shape::removeFromDictionary(ObjectImpl *obj)
{
JS_ASSERT(inDictionary());
JS_ASSERT(obj->inDictionaryMode());
JS_ASSERT(listp);
JS_ASSERT(obj->shape_->inDictionary());
JS_ASSERT(obj->shape_->listp == &obj->shape_);
if (parent)
parent->listp = listp;
*listp = parent;
listp = NULL;
}
inline void
Shape::insertIntoDictionary(HeapPtrShape *dictp)
{
/*
* Don't assert inDictionaryMode() here because we may be called from
* JSObject::toDictionaryMode via JSObject::newDictionaryShape.
*/
JS_ASSERT(inDictionary());
JS_ASSERT(!listp);
JS_ASSERT_IF(*dictp, (*dictp)->inDictionary());
JS_ASSERT_IF(*dictp, (*dictp)->listp == dictp);
JS_ASSERT_IF(*dictp, compartment() == (*dictp)->compartment());
setParent(dictp->get());
if (parent)
parent->listp = &parent;
listp = (HeapPtrShape *) dictp;
*dictp = this;
}
void
Shape::initDictionaryShape(const StackShape &child, uint32_t nfixed, HeapPtrShape *dictp)
{
new (this) Shape(child, nfixed);
this->flags |= IN_DICTIONARY;
this->listp = NULL;
insertIntoDictionary(dictp);
}
inline
EmptyShape::EmptyShape(UnownedBaseShape *base, uint32_t nfixed)
: js::Shape(base, nfixed)
{
/* Only empty shapes can be NON_NATIVE. */
if (!getObjectClass()->isNative())
flags |= NON_NATIVE;
}
inline void
Shape::writeBarrierPre(Shape *shape)
{
#ifdef JSGC_INCREMENTAL
if (!shape || !shape->runtime()->needsBarrier())
return;
JS::Zone *zone = shape->zone();
if (zone->needsBarrier()) {
Shape *tmp = shape;
MarkShapeUnbarriered(zone->barrierTracer(), &tmp, "write barrier");
JS_ASSERT(tmp == shape);
}
#endif
}
inline void
Shape::writeBarrierPost(Shape *shape, void *addr)
{
}
inline void
Shape::readBarrier(Shape *shape)
{
#ifdef JSGC_INCREMENTAL
JS::Zone *zone = shape->zone();
if (zone->needsBarrier()) {
Shape *tmp = shape;
MarkShapeUnbarriered(zone->barrierTracer(), &tmp, "read barrier");
JS_ASSERT(tmp == shape);
}
#endif
}
inline void
Shape::markChildren(JSTracer *trc)
{
MarkBaseShape(trc, &base_, "base");
gc::MarkId(trc, &propidRef(), "propid");
if (parent)
MarkShape(trc, &parent, "parent");
}
inline void
BaseShape::writeBarrierPre(BaseShape *base)
{
#ifdef JSGC_INCREMENTAL
if (!base || !base->runtime()->needsBarrier())
return;
JS::Zone *zone = base->zone();
if (zone->needsBarrier()) {
BaseShape *tmp = base;
MarkBaseShapeUnbarriered(zone->barrierTracer(), &tmp, "write barrier");
JS_ASSERT(tmp == base);
}
#endif
}
inline void
BaseShape::writeBarrierPost(BaseShape *shape, void *addr)
{
}
inline void
BaseShape::readBarrier(BaseShape *base)
{
#ifdef JSGC_INCREMENTAL
JS::Zone *zone = base->zone();
if (zone->needsBarrier()) {
BaseShape *tmp = base;
MarkBaseShapeUnbarriered(zone->barrierTracer(), &tmp, "read barrier");
JS_ASSERT(tmp == base);
}
#endif
}
inline void
BaseShape::markChildren(JSTracer *trc)
{
if (hasGetterObject())
MarkObjectUnbarriered(trc, &getterObj, "getter");
if (hasSetterObject())
MarkObjectUnbarriered(trc, &setterObj, "setter");
if (isOwned())
MarkBaseShape(trc, &unowned_, "base");
if (parent)
MarkObject(trc, &parent, "parent");
if (metadata)
MarkObject(trc, &metadata, "metadata");
}
/*
* Property lookup hooks on objects are required to return a non-NULL shape to
* signify that the property has been found. For cases where the property is
* not actually represented by a Shape, use a dummy value. This includes all
* properties of non-native objects, and dense elements for native objects.
* Use separate APIs for these two cases.
*/
static inline void
MarkNonNativePropertyFound(MutableHandleShape propp)
{
propp.set(reinterpret_cast<Shape*>(1));
}
template <AllowGC allowGC>
static inline void
MarkDenseElementFound(typename MaybeRooted<Shape*, allowGC>::MutableHandleType propp)
{
propp.set(reinterpret_cast<Shape*>(1));
}
static inline bool
IsImplicitDenseElement(Shape *prop)
{
return prop == reinterpret_cast<Shape*>(1);
}
static inline uint8_t
GetShapeAttributes(HandleShape shape)
{
return IsImplicitDenseElement(shape) ? JSPROP_ENUMERATE : shape->attributes();
}
} /* namespace js */
#endif /* vm_Shape_inl_h */