PIC.h
/* -*- 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_PIC_h
#define vm_PIC_h
#include "jsapi.h"
#include "jscntxt.h"
#include "jsfriendapi.h"
#include "jsobj.h"
#include "gc/Barrier.h"
#include "gc/Heap.h"
#include "gc/Marking.h"
#include "js/Value.h"
#include "vm/GlobalObject.h"
namespace js {
class Shape;
template <typename Category> class PICChain;
/*
* The basic PICStub just has a pointer to the next stub.
*/
template <typename Category>
class PICStub
{
friend class PICChain<Category>;
private:
typedef typename Category::Stub CatStub;
typedef typename Category::Chain CatChain;
protected:
CatStub* next_;
PICStub() : next_(nullptr) {}
explicit PICStub(const CatStub* next) : next_(next) {
MOZ_ASSERT(next_);
}
explicit PICStub(const CatStub& other) : next_(other.next_) {}
public:
CatStub* next() const {
return next_;
}
protected:
void append(CatStub* stub) {
MOZ_ASSERT(!next_);
MOZ_ASSERT(!stub->next_);
next_ = stub;
}
};
/*
* The basic PIC just has a pointer to the list of stubs.
*/
template <typename Category>
class PICChain
{
private:
typedef typename Category::Stub CatStub;
typedef typename Category::Chain CatChain;
protected:
CatStub* stubs_;
PICChain() : stubs_(nullptr) {}
// PICs should never be copy constructed.
PICChain(const PICChain<Category>& other) = delete;
public:
CatStub* stubs() const {
return stubs_;
}
void addStub(CatStub* stub) {
MOZ_ASSERT(stub);
MOZ_ASSERT(!stub->next());
if (!stubs_) {
stubs_ = stub;
return;
}
CatStub* cur = stubs_;
while (cur->next())
cur = cur->next();
cur->append(stub);
}
unsigned numStubs() const {
unsigned count = 0;
for (CatStub* stub = stubs_; stub; stub = stub->next())
count++;
return count;
}
void removeStub(CatStub* stub, CatStub* previous) {
if (previous) {
MOZ_ASSERT(previous->next() == stub);
previous->next_ = stub->next();
} else {
MOZ_ASSERT(stub == stubs_);
stubs_ = stub->next();
}
js_delete(stub);
}
};
/*
* ForOfPIC defines a PIC category for optimizing for-of operations.
*/
struct ForOfPIC
{
/* Forward declarations so template-substitution works. */
class Stub;
class Chain;
ForOfPIC() = delete;
ForOfPIC(const ForOfPIC& other) = delete;
typedef PICStub<ForOfPIC> BaseStub;
typedef PICChain<ForOfPIC> BaseChain;
/*
* A ForOfPIC has only one kind of stub for now: one that holds the shape
* of an array object that does not override its @@iterator property.
*/
class Stub : public BaseStub
{
private:
// Shape of matching array object.
Shape* shape_;
public:
explicit Stub(Shape* shape)
: BaseStub(),
shape_(shape)
{
MOZ_ASSERT(shape_);
}
Shape* shape() {
return shape_;
}
};
/*
* A ForOfPIC chain holds the following:
*
* Array.prototype (arrayProto_)
* To ensure that the incoming array has the standard proto.
*
* Array.prototype's shape (arrayProtoShape_)
* To ensure that Array.prototype has not been modified.
*
* ArrayIterator.prototype (arrayIteratorProto_)
* ArrayIterator.prototype's shape (arrayIteratorProtoShape_)
* To ensure that an ArrayIterator.prototype has not been modified.
*
* Array.prototype's slot number for @@iterator (arrayProtoIteratorSlot_)
* Array.prototype's canonical value for @@iterator (canonicalIteratorFunc_)
* To quickly retrieve and ensure that the iterator constructor
* stored in the slot has not changed.
*
* ArrayIterator.prototype's slot number for 'next' (arrayIteratorProtoNextSlot_)
* ArrayIterator.prototype's canonical value for 'next' (canonicalNextFunc_)
* To quickly retrieve and ensure that the 'next' method for ArrayIterator
* objects has not changed.
*/
class Chain : public BaseChain
{
private:
// Pointer to canonical Array.prototype and ArrayIterator.prototype
GCPtrNativeObject arrayProto_;
GCPtrNativeObject arrayIteratorProto_;
// Shape of matching Array.prototype object, and slot containing
// the @@iterator for it, and the canonical value.
GCPtrShape arrayProtoShape_;
uint32_t arrayProtoIteratorSlot_;
GCPtrValue canonicalIteratorFunc_;
// Shape of matching ArrayIteratorProto, and slot containing
// the 'next' property, and the canonical value.
GCPtrShape arrayIteratorProtoShape_;
uint32_t arrayIteratorProtoNextSlot_;
GCPtrValue canonicalNextFunc_;
// Initialization flag marking lazy initialization of above fields.
bool initialized_;
// Disabled flag is set when we don't want to try optimizing anymore
// because core objects were changed.
bool disabled_;
static const unsigned MAX_STUBS = 10;
public:
Chain()
: BaseChain(),
arrayProto_(nullptr),
arrayIteratorProto_(nullptr),
arrayProtoShape_(nullptr),
arrayProtoIteratorSlot_(-1),
canonicalIteratorFunc_(UndefinedValue()),
arrayIteratorProtoShape_(nullptr),
arrayIteratorProtoNextSlot_(-1),
initialized_(false),
disabled_(false)
{}
// Initialize the canonical iterator function.
bool initialize(JSContext* cx);
// Check if a given array object is optimized by this PIC.
Stub* isArrayOptimized(ArrayObject* obj);
// Try to optimize this chain for an object.
bool tryOptimizeArray(JSContext* cx, HandleArrayObject array, bool* optimized);
// Check if the global array-related objects have not been messed with
// in a way that would disable this PIC.
bool isArrayStateStillSane();
// Check if ArrayIterator.next is still optimizable.
inline bool isArrayNextStillSane() {
return (arrayIteratorProto_->lastProperty() == arrayIteratorProtoShape_) &&
(arrayIteratorProto_->getSlot(arrayIteratorProtoNextSlot_) == canonicalNextFunc_);
}
void mark(JSTracer* trc);
void sweep(FreeOp* fop);
private:
// Get a matching optimized stub for the given object.
Stub* getMatchingStub(JSObject* obj);
// Check if the given object is for-of optimizable with this PIC.
bool isOptimizableArray(JSObject* obj);
// Reset the PIC and all info associated with it.
void reset(JSContext* cx);
// Erase the stub chain.
void eraseChain();
};
// Class for object that holds ForOfPIC chain.
static const Class class_;
static NativeObject* createForOfPICObject(JSContext* cx, Handle<GlobalObject*> global);
static inline Chain* fromJSObject(NativeObject* obj) {
MOZ_ASSERT(js::GetObjectClass(obj) == &ForOfPIC::class_);
return (ForOfPIC::Chain*) obj->getPrivate();
}
static inline Chain* getOrCreate(JSContext* cx) {
NativeObject* obj = cx->global()->getForOfPICObject();
if (obj)
return fromJSObject(obj);
return create(cx);
}
static Chain* create(JSContext* cx);
};
} // namespace js
#endif /* vm_PIC_h */