зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1462540 - Initialize NativeIterator objects (and any associated property name strings and HeapReceiverGuards) all within a single constructor call, without using PodZero. r=jandem
--HG-- extra : rebase_source : 5b1634954f659b7c0b35fde2826b8f91e2d2db43
This commit is contained in:
Родитель
0b60c0f525
Коммит
561722cb1c
|
@ -14,6 +14,8 @@
|
|||
#include "mozilla/PodOperations.h"
|
||||
#include "mozilla/Unused.h"
|
||||
|
||||
#include <new>
|
||||
|
||||
#include "jstypes.h"
|
||||
#include "jsutil.h"
|
||||
|
||||
|
@ -46,7 +48,6 @@ using mozilla::DebugOnly;
|
|||
using mozilla::Maybe;
|
||||
using mozilla::PodCopy;
|
||||
using mozilla::PodEqual;
|
||||
using mozilla::PodZero;
|
||||
|
||||
typedef Rooted<PropertyIteratorObject*> RootedPropertyIteratorObject;
|
||||
|
||||
|
@ -534,7 +535,17 @@ js::GetPropertyKeys(JSContext* cx, HandleObject obj, unsigned flags, AutoIdVecto
|
|||
props);
|
||||
}
|
||||
|
||||
static inline PropertyIteratorObject*
|
||||
static inline void
|
||||
RegisterEnumerator(JSContext* cx, NativeIterator* ni)
|
||||
{
|
||||
/* Register non-escaping native enumerators (for-in) with the current context. */
|
||||
ni->link(cx->compartment()->enumerators);
|
||||
|
||||
MOZ_ASSERT(!(ni->flags & JSITER_ACTIVE));
|
||||
ni->flags |= JSITER_ACTIVE;
|
||||
}
|
||||
|
||||
static PropertyIteratorObject*
|
||||
NewPropertyIteratorObject(JSContext* cx)
|
||||
{
|
||||
RootedObjectGroup group(cx, ObjectGroup::defaultNewGroup(cx, &PropertyIteratorObject::class_,
|
||||
|
@ -563,129 +574,139 @@ NewPropertyIteratorObject(JSContext* cx)
|
|||
return res;
|
||||
}
|
||||
|
||||
NativeIterator*
|
||||
NativeIterator::allocateIterator(JSContext* cx, uint32_t numGuards, uint32_t plength)
|
||||
static PropertyIteratorObject*
|
||||
CreatePropertyIterator(JSContext* cx, Handle<JSObject*> objBeingIterated,
|
||||
const AutoIdVector& props, uint32_t numGuards, uint32_t guardKey)
|
||||
{
|
||||
Rooted<PropertyIteratorObject*> propIter(cx, NewPropertyIteratorObject(cx));
|
||||
if (!propIter)
|
||||
return nullptr;
|
||||
|
||||
static_assert(sizeof(ReceiverGuard) == 2 * sizeof(GCPtrFlatString),
|
||||
"NativeIterators are allocated in space for 1) themselves, "
|
||||
"2) the properties a NativeIterator iterates (as "
|
||||
"GCPtrFlatStrings), and 3) |numGuards| ReceiverGuard "
|
||||
"GCPtrFlatStrings), and 3) |numGuards| HeapReceiverGuard "
|
||||
"objects; the additional-length calculation below assumes "
|
||||
"this size-relationship when determining the extra space to "
|
||||
"allocate");
|
||||
|
||||
size_t extraLength = plength + numGuards * 2;
|
||||
NativeIterator* ni =
|
||||
cx->zone()->pod_malloc_with_extra<NativeIterator, GCPtrFlatString>(extraLength);
|
||||
if (!ni) {
|
||||
size_t extraCount = props.length() + numGuards * 2;
|
||||
void* mem =
|
||||
cx->zone()->pod_malloc_with_extra<NativeIterator, GCPtrFlatString>(extraCount);
|
||||
if (!mem) {
|
||||
ReportOutOfMemory(cx);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Zero out the NativeIterator first.
|
||||
PodZero(ni);
|
||||
// This also registers |ni| with |propIter|.
|
||||
bool hadError = false;
|
||||
NativeIterator* ni =
|
||||
new (mem) NativeIterator(cx, propIter, objBeingIterated, props, numGuards, guardKey,
|
||||
&hadError);
|
||||
if (hadError)
|
||||
return nullptr;
|
||||
|
||||
// Zero out the remaining space for GCPtrFlatStrings for properties and
|
||||
// ReceiverGuards for guards.
|
||||
GCPtrFlatString* extra = ni->begin();
|
||||
PodZero(extra, extraLength);
|
||||
RegisterEnumerator(cx, ni);
|
||||
return propIter;
|
||||
}
|
||||
|
||||
ni->props_cursor = extra;
|
||||
ni->props_end = extra + plength;
|
||||
return ni;
|
||||
/**
|
||||
* Initialize a sentinel NativeIterator whose purpose is only to act as the
|
||||
* start/end of the circular linked list of NativeIterators in
|
||||
* JSCompartment::enumerators.
|
||||
*/
|
||||
NativeIterator::NativeIterator()
|
||||
{
|
||||
// Do our best to enforce that nothing in |this| except the two fields set
|
||||
// below is ever observed.
|
||||
JS_POISON(static_cast<void*>(this), 0xCC, sizeof(*this), MemCheckKind::MakeUndefined);
|
||||
|
||||
// These are the only two fields in sentinel NativeIterators that are
|
||||
// examined, in JSCompartment::sweepNativeIterators. Everything else is
|
||||
// only examined *if* it's a NativeIterator being traced by a
|
||||
// PropertyIteratorObject that owns it, and nothing owns this iterator.
|
||||
prev_ = next_ = this;
|
||||
}
|
||||
|
||||
NativeIterator*
|
||||
NativeIterator::allocateSentinel(JSContext* maybecx)
|
||||
{
|
||||
NativeIterator* ni = js_pod_malloc<NativeIterator>();
|
||||
NativeIterator* ni = js_new<NativeIterator>();
|
||||
if (!ni) {
|
||||
if (maybecx)
|
||||
ReportOutOfMemory(maybecx);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
PodZero(ni);
|
||||
|
||||
ni->next_ = ni;
|
||||
ni->prev_ = ni;
|
||||
return ni;
|
||||
}
|
||||
|
||||
inline void
|
||||
NativeIterator::init(JSObject* obj, JSObject* iterObj, uint32_t numGuards, uint32_t key)
|
||||
/**
|
||||
* Initialize a fresh NativeIterator.
|
||||
*
|
||||
* This definition is a bit tricky: some parts of initializing are fallible, so
|
||||
* as we initialize, we must carefully keep this in GC-safe state (see
|
||||
* NativeIterator::trace).
|
||||
*/
|
||||
NativeIterator::NativeIterator(JSContext* cx, Handle<PropertyIteratorObject*> propIter,
|
||||
Handle<JSObject*> objBeingIterated, const AutoIdVector& props,
|
||||
uint32_t numGuards, uint32_t guardKey, bool* hadError)
|
||||
: obj(objBeingIterated),
|
||||
iterObj_(propIter),
|
||||
// NativeIterator initially acts as if it contains no properties.
|
||||
props_cursor(begin()),
|
||||
props_end(props_cursor),
|
||||
// ...and no HeapReceiverGuards.
|
||||
guard_length(0),
|
||||
guard_key(guardKey),
|
||||
flags(0)
|
||||
{
|
||||
this->obj.init(obj);
|
||||
this->iterObj_ = iterObj;
|
||||
this->flags = 0;
|
||||
this->guard_length = numGuards;
|
||||
this->guard_key = key;
|
||||
}
|
||||
MOZ_ASSERT(!*hadError);
|
||||
|
||||
bool
|
||||
NativeIterator::initProperties(JSContext* cx, Handle<PropertyIteratorObject*> obj,
|
||||
const AutoIdVector& props)
|
||||
{
|
||||
// The obj parameter is just so that we can ensure that this object will get
|
||||
// traced if we GC.
|
||||
MOZ_ASSERT(this == obj->getNativeIterator());
|
||||
// NOTE: This must be done first thing: PropertyIteratorObject::finalize
|
||||
// can only free |this| (and not leak it) if this has happened.
|
||||
propIter->setNativeIterator(this);
|
||||
|
||||
size_t plength = props.length();
|
||||
MOZ_ASSERT(plength == size_t(end() - begin()));
|
||||
|
||||
GCPtrFlatString* propNames = begin();
|
||||
for (size_t i = 0; i < plength; i++) {
|
||||
for (size_t i = 0, len = props.length(); i < len; i++) {
|
||||
JSFlatString* str = IdToString(cx, props[i]);
|
||||
if (!str)
|
||||
return false;
|
||||
propNames[i].init(str);
|
||||
if (!str) {
|
||||
*hadError = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Placement-new the next property string at the end of the currently
|
||||
// computed property strings.
|
||||
GCPtrFlatString* loc = props_end;
|
||||
|
||||
// Increase the overall property string count before initializing the
|
||||
// property string, so this construction isn't on a location not known
|
||||
// to the GC yet.
|
||||
props_end++;
|
||||
|
||||
new (loc) GCPtrFlatString(str);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline void
|
||||
RegisterEnumerator(JSContext* cx, NativeIterator* ni)
|
||||
{
|
||||
/* Register non-escaping native enumerators (for-in) with the current context. */
|
||||
ni->link(cx->compartment()->enumerators);
|
||||
|
||||
MOZ_ASSERT(!(ni->flags & JSITER_ACTIVE));
|
||||
ni->flags |= JSITER_ACTIVE;
|
||||
}
|
||||
|
||||
static inline PropertyIteratorObject*
|
||||
VectorToKeyIterator(JSContext* cx, HandleObject obj, AutoIdVector& keys, uint32_t numGuards)
|
||||
{
|
||||
if (obj->isSingleton() && !JSObject::setIteratedSingleton(cx, obj))
|
||||
return nullptr;
|
||||
MarkObjectGroupFlags(cx, obj, OBJECT_FLAG_ITERATED);
|
||||
|
||||
Rooted<PropertyIteratorObject*> iterobj(cx, NewPropertyIteratorObject(cx));
|
||||
if (!iterobj)
|
||||
return nullptr;
|
||||
|
||||
NativeIterator* ni = NativeIterator::allocateIterator(cx, numGuards, keys.length());
|
||||
if (!ni)
|
||||
return nullptr;
|
||||
|
||||
iterobj->setNativeIterator(ni);
|
||||
ni->init(obj, iterobj, numGuards, 0);
|
||||
if (!ni->initProperties(cx, iterobj, keys))
|
||||
return nullptr;
|
||||
|
||||
if (numGuards) {
|
||||
// Fill in the guard array from scratch. Also recompute the guard key
|
||||
// as we might have reshaped the object (see for instance the
|
||||
// setIteratedSingleton call above) or GC might have moved shapes and
|
||||
// groups in memory.
|
||||
JSObject* pobj = obj;
|
||||
size_t ind = 0;
|
||||
if (numGuards > 0) {
|
||||
// Construct guards into the guard array. Also recompute the guard key,
|
||||
// which incorporates Shape* and ObjectGroup* addresses that could have
|
||||
// changed during a GC triggered in (among other places) |IdToString|
|
||||
//. above.
|
||||
JSObject* pobj = objBeingIterated;
|
||||
uint32_t key = 0;
|
||||
HeapReceiverGuard* guards = ni->guardArray();
|
||||
HeapReceiverGuard* guards = guardArray();
|
||||
do {
|
||||
ReceiverGuard guard(pobj);
|
||||
guards[ind++].init(guard);
|
||||
|
||||
// Placement-new the next HeapReceiverGuard at the end of the
|
||||
// currently initialized HeapReceiverGuards.
|
||||
uint32_t index = guard_length;
|
||||
|
||||
// Increase the overall guard-count before initializing the
|
||||
// HeapReceiverGuard, so this construction isn't on a location not
|
||||
// known to the GC.
|
||||
guard_length++;
|
||||
|
||||
new (&guards[index]) HeapReceiverGuard(guard);
|
||||
|
||||
key = mozilla::AddToHash(key, guard.hash());
|
||||
|
||||
// The one caller of this method that passes |numGuards > 0|, does
|
||||
|
@ -693,12 +714,22 @@ VectorToKeyIterator(JSContext* cx, HandleObject obj, AutoIdVector& keys, uint32_
|
|||
// necessarily have static prototypes).
|
||||
pobj = pobj->staticPrototype();
|
||||
} while (pobj);
|
||||
ni->guard_key = key;
|
||||
MOZ_ASSERT(ind == numGuards);
|
||||
|
||||
guard_key = key;
|
||||
MOZ_ASSERT(guard_length == numGuards);
|
||||
}
|
||||
|
||||
RegisterEnumerator(cx, ni);
|
||||
return iterobj;
|
||||
MOZ_ASSERT(!*hadError);
|
||||
}
|
||||
|
||||
static inline PropertyIteratorObject*
|
||||
VectorToKeyIterator(JSContext* cx, HandleObject obj, AutoIdVector& props, uint32_t numGuards)
|
||||
{
|
||||
if (obj->isSingleton() && !JSObject::setIteratedSingleton(cx, obj))
|
||||
return nullptr;
|
||||
MarkObjectGroupFlags(cx, obj, OBJECT_FLAG_ITERATED);
|
||||
|
||||
return CreatePropertyIterator(cx, obj, props, numGuards, 0);
|
||||
}
|
||||
|
||||
|
||||
|
@ -712,22 +743,8 @@ js::EnumeratedIdVectorToIterator(JSContext* cx, HandleObject obj, AutoIdVector&
|
|||
JSObject*
|
||||
js::NewEmptyPropertyIterator(JSContext* cx)
|
||||
{
|
||||
Rooted<PropertyIteratorObject*> iterobj(cx, NewPropertyIteratorObject(cx));
|
||||
if (!iterobj)
|
||||
return nullptr;
|
||||
|
||||
AutoIdVector keys(cx); // Empty
|
||||
NativeIterator* ni = NativeIterator::allocateIterator(cx, 0, keys.length());
|
||||
if (!ni)
|
||||
return nullptr;
|
||||
|
||||
iterobj->setNativeIterator(ni);
|
||||
ni->init(nullptr, iterobj, 0, 0);
|
||||
if (!ni->initProperties(cx, iterobj, keys))
|
||||
return nullptr;
|
||||
|
||||
RegisterEnumerator(cx, ni);
|
||||
return iterobj;
|
||||
AutoIdVector props(cx); // Empty
|
||||
return CreatePropertyIterator(cx, nullptr, props, 0, 0);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
|
|
|
@ -31,29 +31,51 @@ class PropertyIteratorObject;
|
|||
|
||||
struct NativeIterator
|
||||
{
|
||||
GCPtrObject obj; // Object being iterated.
|
||||
JSObject* iterObj_; // Internal iterator object.
|
||||
GCPtrFlatString* props_cursor;
|
||||
GCPtrFlatString* props_end; // also the start of HeapReceiverGuards
|
||||
uint32_t guard_length;
|
||||
uint32_t guard_key;
|
||||
uint32_t flags;
|
||||
// Object being iterated.
|
||||
GCPtrObject obj = {};
|
||||
|
||||
// Internal iterator object.
|
||||
JSObject* iterObj_ = nullptr;
|
||||
|
||||
// The next property, pointing into an array of strings directly after this
|
||||
// NativeIterator as part of the overall allocation containing |*this|.
|
||||
GCPtrFlatString* props_cursor; // initialized by constructor
|
||||
|
||||
// The limit/end of properties to iterate. (This is also, after casting,
|
||||
// the start of an array of HeapReceiverGuards included in the overall
|
||||
// allocation that stores |*this| and the iterated strings.)
|
||||
GCPtrFlatString* props_end; // initialized by constructor
|
||||
|
||||
uint32_t guard_length = 0;
|
||||
uint32_t guard_key = 0;
|
||||
uint32_t flags = 0;
|
||||
|
||||
private:
|
||||
/* While in compartment->enumerators, these form a doubly linked list. */
|
||||
NativeIterator* next_;
|
||||
NativeIterator* prev_;
|
||||
NativeIterator* next_ = nullptr;
|
||||
NativeIterator* prev_ = nullptr;
|
||||
|
||||
// No further fields appear after here *in NativeIterator*, but this class
|
||||
// is always allocated with space tacked on immediately after |this| (see
|
||||
// below) to store 1) a dynamically known number of iterated property names
|
||||
// and 2) a dynamically known number of HeapReceiverGuards. The limit of
|
||||
// all such property names, and the start of HeapReceiverGuards, is
|
||||
// |props_end|. The next property name to iterate is |props_cursor| and
|
||||
// equals |props_end| when this iterator is exhausted but not ready yet for
|
||||
// possible reuse.
|
||||
// is always allocated with space tacked on immediately after |this| to
|
||||
// store iterated property names up to |props_end| and |guard_length|
|
||||
// HeapReceiverGuards after that.
|
||||
|
||||
public:
|
||||
/**
|
||||
* Initialize a NativeIterator properly allocated for |props.length()|
|
||||
* properties and |numGuards| guards.
|
||||
*
|
||||
* Despite being a constructor, THIS FUNCTION CAN REPORT ERRORS. Users
|
||||
* MUST set |*hadError = false| on entry and consider |*hadError| on return
|
||||
* to mean this function failed.
|
||||
*/
|
||||
NativeIterator(JSContext* cx, Handle<PropertyIteratorObject*> propIter,
|
||||
Handle<JSObject*> objBeingIterated, const AutoIdVector& props,
|
||||
uint32_t numGuards, uint32_t guardKey, bool* hadError);
|
||||
|
||||
/** Initialize a |JSCompartment::enumerators| sentinel. */
|
||||
NativeIterator();
|
||||
|
||||
GCPtrFlatString* begin() const {
|
||||
static_assert(alignof(NativeIterator) >= alignof(GCPtrFlatString),
|
||||
"GCPtrFlatStrings for properties must be able to appear "
|
||||
|
@ -123,16 +145,8 @@ struct NativeIterator
|
|||
}
|
||||
|
||||
static NativeIterator* allocateSentinel(JSContext* maybecx);
|
||||
static NativeIterator* allocateIterator(JSContext* cx, uint32_t slength, uint32_t plength);
|
||||
void init(JSObject* obj, JSObject* iterObj, uint32_t slength, uint32_t key);
|
||||
bool initProperties(JSContext* cx, Handle<PropertyIteratorObject*> obj,
|
||||
const js::AutoIdVector& props);
|
||||
|
||||
void trace(JSTracer* trc);
|
||||
|
||||
static void destroy(NativeIterator* iter) {
|
||||
js_free(iter);
|
||||
}
|
||||
};
|
||||
|
||||
class PropertyIteratorObject : public NativeObject
|
||||
|
|
Загрузка…
Ссылка в новой задаче