Bug 1401577 - Optimize object flag accessors on native objects. r=anba

This commit is contained in:
Jan de Mooij 2017-09-20 18:56:08 +02:00
parent af29d63449
commit 810244a62c
10 changed files with 82 additions and 63 deletions

View File

@ -1680,7 +1680,7 @@ CanAttachDenseElementHole(JSObject* obj, bool ownProp)
// because we would have to lookup a property on the prototype instead.
do {
// The first two checks are also relevant to the receiver object.
if (obj->isIndexed())
if (obj->isNative() && obj->as<NativeObject>().isIndexed())
return false;
if (ClassCanHaveExtraProperties(obj->getClass()))
@ -3149,7 +3149,7 @@ CanAttachAddElement(JSObject* obj, bool isInit)
// or that such properties can't appear without a shape change.
do {
// The first two checks are also relevant to the receiver object.
if (obj->isIndexed())
if (obj->isNative() && obj->as<NativeObject>().isIndexed())
return false;
const Class* clasp = obj->getClass();

View File

@ -531,7 +531,8 @@ SetArrayElement(JSContext* cx, HandleObject obj, uint64_t index, HandleValue v)
static bool
DeleteArrayElement(JSContext* cx, HandleObject obj, uint64_t index, ObjectOpResult& result)
{
if (obj->is<ArrayObject>() && !obj->isIndexed() &&
if (obj->is<ArrayObject>() &&
!obj->as<NativeObject>().isIndexed() &&
!obj->as<NativeObject>().denseElementsAreFrozen())
{
ArrayObject* aobj = &obj->as<ArrayObject>();
@ -579,7 +580,8 @@ DeletePropertyOrThrow(JSContext* cx, HandleObject obj, uint64_t index)
static bool
DeletePropertiesOrThrow(JSContext* cx, HandleObject obj, uint64_t len, uint64_t finalLength)
{
if (obj->is<ArrayObject>() && !obj->isIndexed() &&
if (obj->is<ArrayObject>() &&
!obj->as<NativeObject>().isIndexed() &&
!obj->as<NativeObject>().denseElementsAreFrozen())
{
if (len <= UINT32_MAX) {
@ -988,10 +990,16 @@ array_addProperty(JSContext* cx, HandleObject obj, HandleId id, HandleValue v)
static inline bool
ObjectMayHaveExtraIndexedOwnProperties(JSObject* obj)
{
return (!obj->isNative() && !obj->is<UnboxedArrayObject>()) ||
obj->isIndexed() ||
obj->is<TypedArrayObject>() ||
ClassMayResolveId(*obj->runtimeFromAnyThread()->commonNames,
if (!obj->isNative())
return !obj->is<UnboxedArrayObject>();
if (obj->as<NativeObject>().isIndexed())
return true;
if (obj->is<TypedArrayObject>())
return true;
return ClassMayResolveId(*obj->runtimeFromAnyThread()->commonNames,
obj->getClass(), INT_TO_JSID(0), obj);
}
@ -3138,8 +3146,8 @@ GetIndexedPropertiesInRange(JSContext* cx, HandleObject obj, uint64_t begin, uin
}
// Append typed array elements.
if (pobj->is<TypedArrayObject>()) {
uint32_t len = pobj->as<TypedArrayObject>().length();
if (nativeObj->is<TypedArrayObject>()) {
uint32_t len = nativeObj->as<TypedArrayObject>().length();
for (uint32_t i = begin; i < len && i < end; i++) {
if (!indexes.append(i))
return false;
@ -3147,8 +3155,8 @@ GetIndexedPropertiesInRange(JSContext* cx, HandleObject obj, uint64_t begin, uin
}
// Append sparse elements.
if (pobj->isIndexed()) {
Shape::Range<NoGC> r(pobj->as<NativeObject>().lastProperty());
if (nativeObj->isIndexed()) {
Shape::Range<NoGC> r(nativeObj->lastProperty());
for (; !r.empty(); r.popFront()) {
Shape& shape = r.front();
jsid id = shape.propid();
@ -3278,7 +3286,7 @@ ArraySliceOrdinary(JSContext* cx, HandleObject obj, uint64_t begin, uint64_t end
}
}
if (obj->isNative() && obj->isIndexed() && count > 1000) {
if (obj->isNative() && obj->as<NativeObject>().isIndexed() && count > 1000) {
if (!SliceSparse(cx, obj, begin, end, narr))
return false;
} else {

View File

@ -1161,7 +1161,7 @@ GetScriptArrayObjectElements(JSContext* cx, HandleObject obj, MutableHandle<GCVe
{
MOZ_ASSERT(!obj->isSingleton());
MOZ_ASSERT(obj->is<ArrayObject>() || obj->is<UnboxedArrayObject>());
MOZ_ASSERT(!obj->isIndexed());
MOZ_ASSERT_IF(obj->isNative(), !obj->as<NativeObject>().isIndexed());
size_t length = GetAnyBoxedOrUnboxedArrayLength(obj);
if (!values.appendN(MagicValue(JS_ELEMENTS_HOLE), length))
@ -3586,7 +3586,6 @@ JSObject::dump(js::GenericPrinter& out) const
out.put("flags:");
if (obj->isDelegate()) out.put(" delegate");
if (!obj->is<ProxyObject>() && !obj->nonProxyIsExtensible()) out.put(" not_extensible");
if (obj->isIndexed()) out.put(" indexed");
if (obj->maybeHasInterestingSymbolProperty()) out.put(" maybe_has_interesting_symbol");
if (obj->isBoundFunction()) out.put(" bound_function");
if (obj->isQualifiedVarObj()) out.put(" varobj");
@ -3595,8 +3594,6 @@ JSObject::dump(js::GenericPrinter& out) const
if (obj->isIteratedSingleton()) out.put(" iterated_singleton");
if (obj->isNewGroupUnknown()) out.put(" new_type_unknown");
if (obj->hasUncacheableProto()) out.put(" has_uncacheable_proto");
if (obj->hadElementsAccess()) out.put(" had_elements_access");
if (obj->wasNewScriptCleared()) out.put(" new_script_cleared");
if (obj->hasStaticPrototype() && obj->staticPrototypeIsImmutable())
out.put(" immutable_prototype");
@ -3606,6 +3603,12 @@ JSObject::dump(js::GenericPrinter& out) const
out.put(" inDictionaryMode");
if (nobj->hasShapeTable())
out.put(" hasShapeTable");
if (nobj->hadElementsAccess())
out.put(" had_elements_access");
if (nobj->isIndexed())
out.put(" indexed");
if (nobj->wasNewScriptCleared())
out.put(" new_script_cleared");
}
out.putChar('\n');

View File

@ -235,21 +235,6 @@ class JSObject : public js::gc::Cell
return setFlags(cx, obj, js::BaseShape::UNCACHEABLE_PROTO, GENERATE_SHAPE);
}
/*
* Whether SETLELEM was used to access this object. See also the comment near
* PropertyTree::MAX_HEIGHT.
*/
inline bool hadElementsAccess() const;
static bool setHadElementsAccess(JSContext* cx, JS::HandleObject obj) {
return setFlags(cx, obj, js::BaseShape::HAD_ELEMENTS_ACCESS);
}
/*
* Whether there may be indexed properties on this object, excluding any in
* the object's elements.
*/
inline bool isIndexed() const;
/*
* Whether there may be "interesting symbol" properties on this object. An
* interesting symbol is a symbol for which symbol->isInterestingSymbol()
@ -413,12 +398,6 @@ class JSObject : public js::gc::Cell
inline bool isNewGroupUnknown() const;
static bool setNewGroupUnknown(JSContext* cx, const js::Class* clasp, JS::HandleObject obj);
// Mark an object as having its 'new' script information cleared.
inline bool wasNewScriptCleared() const;
static bool setNewScriptCleared(JSContext* cx, JS::HandleObject obj) {
return setFlags(cx, obj, js::BaseShape::NEW_SCRIPT_CLEARED);
}
/* Set a new prototype for an object with a singleton type. */
static bool splicePrototype(JSContext* cx, js::HandleObject obj, const js::Class* clasp,
js::Handle<js::TaggedProto> proto);

View File

@ -468,18 +468,6 @@ JSObject::hasUncacheableProto() const
return hasAllFlags(js::BaseShape::UNCACHEABLE_PROTO);
}
inline bool
JSObject::hadElementsAccess() const
{
return hasAllFlags(js::BaseShape::HAD_ELEMENTS_ACCESS);
}
inline bool
JSObject::isIndexed() const
{
return hasAllFlags(js::BaseShape::INDEXED);
}
MOZ_ALWAYS_INLINE bool
JSObject::maybeHasInterestingSymbolProperty() const
{
@ -494,7 +482,7 @@ JSObject::maybeHasInterestingSymbolProperty() const
return true;
}
return nobj->hasAllFlags(js::BaseShape::HAS_INTERESTING_SYMBOL);
return nobj->hasInterestingSymbol();
}
inline bool
@ -516,12 +504,6 @@ JSObject::isNewGroupUnknown() const
return hasAllFlags(js::BaseShape::NEW_GROUP_UNKNOWN);
}
inline bool
JSObject::wasNewScriptCleared() const
{
return hasAllFlags(js::BaseShape::NEW_SCRIPT_CLEARED);
}
namespace js {
static MOZ_ALWAYS_INLINE bool

View File

@ -505,7 +505,7 @@ JA(JSContext* cx, HandleObject obj, StringifyContext* scx)
"all its initially-dense elements were sparsified "
"and the object is indexed");
} else {
MOZ_ASSERT(obj->isIndexed());
MOZ_ASSERT(nativeObj->isIndexed());
}
}
#endif

View File

@ -1630,10 +1630,10 @@ SetObjectElementOperation(JSContext* cx, HandleObject obj, HandleId id, HandleVa
if (obj->isNative() &&
JSID_IS_ATOM(id) &&
!obj->as<NativeObject>().inDictionaryMode() &&
!obj->hadElementsAccess() &&
!obj->as<NativeObject>().hadElementsAccess() &&
obj->as<NativeObject>().slotSpan() > PropertyTree::MAX_HEIGHT_WITH_ELEMENTS_ACCESS / 3)
{
if (!JSObject::setHadElementsAccess(cx, obj))
if (!NativeObject::setHadElementsAccess(cx, obj.as<NativeObject>()))
return false;
}

View File

@ -720,6 +720,53 @@ class NativeObject : public ShapedObject
return slot - numFixedSlots();
}
/*
* The methods below shadow methods on JSObject and are more efficient for
* known-native objects.
*/
bool hasAllFlags(js::BaseShape::Flag flags) const {
MOZ_ASSERT(flags);
return shape_->hasAllObjectFlags(flags);
}
bool watched() const {
return hasAllFlags(js::BaseShape::WATCHED);
}
bool nonProxyIsExtensible() const {
return !hasAllFlags(js::BaseShape::NOT_EXTENSIBLE);
}
/*
* Whether there may be indexed properties on this object, excluding any in
* the object's elements.
*/
bool isIndexed() const {
return hasAllFlags(js::BaseShape::INDEXED);
}
static bool setHadElementsAccess(JSContext* cx, HandleNativeObject obj) {
return setFlags(cx, obj, js::BaseShape::HAD_ELEMENTS_ACCESS);
}
/*
* Whether SETLELEM was used to access this object. See also the comment near
* PropertyTree::MAX_HEIGHT.
*/
bool hadElementsAccess() const {
return hasAllFlags(js::BaseShape::HAD_ELEMENTS_ACCESS);
}
// Mark an object as having its 'new' script information cleared.
bool wasNewScriptCleared() const {
return hasAllFlags(js::BaseShape::NEW_SCRIPT_CLEARED);
}
static bool setNewScriptCleared(JSContext* cx, HandleNativeObject obj) {
return setFlags(cx, obj, js::BaseShape::NEW_SCRIPT_CLEARED);
}
bool hasInterestingSymbol() const {
return hasAllFlags(js::BaseShape::HAS_INTERESTING_SYMBOL);
}
/*
* Grow or shrink slots immediately before changing the slot span.
* The number of allocated slots is not stored explicitly, and changes to

View File

@ -320,7 +320,7 @@ JSObject::makeLazyGroup(JSContext* cx, HandleObject obj)
if (obj->isIteratedSingleton())
initialFlags |= OBJECT_FLAG_ITERATED;
if (obj->isIndexed())
if (obj->isNative() && obj->as<NativeObject>().isIndexed())
initialFlags |= OBJECT_FLAG_SPARSE_INDEXES;
if (obj->is<ArrayObject>() && obj->as<ArrayObject>().length() > INT32_MAX)
@ -497,7 +497,7 @@ ObjectGroup::defaultNewGroup(JSContext* cx, const Class* clasp,
// If we have previously cleared the 'new' script information for this
// function, don't try to construct another one.
if (associated && associated->wasNewScriptCleared())
if (associated && associated->as<JSFunction>().wasNewScriptCleared())
associated = nullptr;
} else {

View File

@ -3044,7 +3044,7 @@ ObjectGroup::clearNewScript(JSContext* cx, ObjectGroup* replacement /* = nullptr
// Mark the constructing function as having its 'new' script cleared, so we
// will not try to construct another one later.
RootedFunction fun(cx, newScript->function());
if (!JSObject::setNewScriptCleared(cx, fun))
if (!NativeObject::setNewScriptCleared(cx, fun))
cx->recoverFromOutOfMemory();
}