Source code

Revision control

Copy as Markdown

Other Tools

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
* vim: set ts=8 sts=2 et sw=2 tw=80:
* 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 "vm/GlobalObject.h"
#include "vm/NativeObject.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:
using CatStub = typename Category::Stub;
using CatChain = typename Category::Chain;
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:
using CatStub = typename Category::Stub;
using CatChain = typename Category::Chain;
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(JSObject* obj, CatStub* stub);
unsigned numStubs() const {
unsigned count = 0;
for (CatStub* stub = stubs_; stub; stub = stub->next()) {
count++;
}
return count;
}
};
// Class for object that holds ForOfPIC chain.
class ForOfPICObject : public NativeObject {
public:
enum { ChainSlot, SlotCount };
static const JSClass class_;
};
/*
* 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;
using BaseStub = PICStub<ForOfPIC>;
using BaseChain = PICChain<ForOfPIC>;
/*
* 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.
const HeapPtr<Shape*> shape_;
public:
explicit Stub(Shape* shape) : shape_(shape) { MOZ_ASSERT(shape_); }
Shape* shape() { return shape_; }
void trace(JSTracer* trc);
};
/*
* 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
* ArrayIterator.prototype's shape
* (arrayIteratorProto_, arrayIteratorProtoShape_)
* To ensure that an ArrayIterator.prototype has not been modified.
*
* Array.prototype's slot number for @@iterator
* Array.prototype's canonical value for @@iterator
* (arrayProtoIteratorSlot_, canonicalIteratorFunc_)
* To quickly retrieve and ensure that the iterator constructor
* stored in the slot has not changed.
*
* ArrayIterator.prototype's slot number for 'next'
* ArrayIterator.prototype's canonical value for 'next'
* (arrayIteratorProtoNextSlot_, canonicalNextFunc_)
* To quickly retrieve and ensure that the 'next' method for
* ArrayIterator objects has not changed.
*/
class Chain : public BaseChain {
private:
// Pointer to owning JSObject for memory accounting purposes.
const GCPtr<JSObject*> picObject_;
// Pointer to canonical Array.prototype, ArrayIterator.prototype,
// Iterator.prototype, and Object.prototype
GCPtr<NativeObject*> arrayProto_;
GCPtr<NativeObject*> arrayIteratorProto_;
GCPtr<NativeObject*> iteratorProto_;
GCPtr<NativeObject*> objectProto_;
// Shape of matching Array.prototype object, and slot containing
// the @@iterator for it, and the canonical value.
GCPtr<Shape*> arrayProtoShape_;
uint32_t arrayProtoIteratorSlot_;
GCPtr<Value> canonicalIteratorFunc_;
// Shape of matching ArrayIteratorProto, and slot containing
// the 'next' property, and the canonical value.
GCPtr<Shape*> arrayIteratorProtoShape_;
uint32_t arrayIteratorProtoNextSlot_;
GCPtr<Value> canonicalNextFunc_;
// Shape of matching Iterator.prototype object.
GCPtr<Shape*> iteratorProtoShape_;
// Shape of matching Object.prototype object.
GCPtr<Shape*> objectProtoShape_;
// 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:
explicit Chain(JSObject* picObject)
: picObject_(picObject),
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);
// Try to optimize this chain for a newly allocated array.
bool tryOptimizeArray(JSContext* cx, bool* optimized);
// Try to optimize this chain for an object.
bool tryOptimizeArray(JSContext* cx, Handle<ArrayObject*> array,
bool* optimized);
// Check if %ArrayIteratorPrototype% still uses the default "next" method.
bool tryOptimizeArrayIteratorNext(JSContext* cx, bool* optimized);
void trace(JSTracer* trc);
void finalize(JS::GCContext* gcx, JSObject* obj);
void freeAllStubs(JS::GCContext* gcx);
private:
// 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 and ArrayIterator.return are still
// optimizable.
inline bool isArrayIteratorStateStillSane() {
// Ensure the prototype chain is intact, which will ensure that "return"
// has not been defined.
if (arrayIteratorProto_->shape() != arrayIteratorProtoShape_) {
return false;
}
if (iteratorProto_->shape() != iteratorProtoShape_) {
return false;
}
if (objectProto_->shape() != objectProtoShape_) {
return false;
}
return arrayIteratorProto_->getSlot(arrayIteratorProtoNextSlot_) ==
canonicalNextFunc_;
}
// Check if a matching optimized stub for the given object exists.
bool hasMatchingStub(ArrayObject* obj);
// Reset the PIC and all info associated with it.
void reset(JSContext* cx);
// Erase the stub chain.
void eraseChain(JSContext* cx);
};
static NativeObject* createForOfPICObject(JSContext* cx,
Handle<GlobalObject*> global);
static inline Chain* fromJSObject(NativeObject* obj) {
MOZ_ASSERT(obj->is<ForOfPICObject>());
return obj->maybePtrFromReservedSlot<Chain>(ForOfPICObject::ChainSlot);
}
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 */