зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1283334 - Part 1: Do not sparsify dense arrays when freezing - Interpreter. r=jandem
--HG-- extra : rebase_source : e069a08f84c15d454f9c4041b33a195dc3e6fcd0 extra : histedit_source : fe61eb2e8147a4799dbac41d29968ba5865e773b
This commit is contained in:
Родитель
37e0260ff1
Коммит
f90cd661a0
|
@ -2941,6 +2941,15 @@ JS_FreezeObject(JSContext* cx, HandleObject obj)
|
|||
return FreezeObject(cx, obj);
|
||||
}
|
||||
|
||||
static bool
|
||||
DeepFreezeSlot(JSContext* cx, const Value& v)
|
||||
{
|
||||
if (v.isPrimitive())
|
||||
return true;
|
||||
RootedObject obj(cx, &v.toObject());
|
||||
return JS_DeepFreezeObject(cx, obj);
|
||||
}
|
||||
|
||||
JS_PUBLIC_API(bool)
|
||||
JS_DeepFreezeObject(JSContext* cx, HandleObject obj)
|
||||
{
|
||||
|
@ -2960,12 +2969,13 @@ JS_DeepFreezeObject(JSContext* cx, HandleObject obj)
|
|||
|
||||
/* Walk slots in obj and if any value is a non-null object, seal it. */
|
||||
if (obj->isNative()) {
|
||||
for (uint32_t i = 0, n = obj->as<NativeObject>().slotSpan(); i < n; ++i) {
|
||||
const Value& v = obj->as<NativeObject>().getSlot(i);
|
||||
if (v.isPrimitive())
|
||||
continue;
|
||||
RootedObject obj(cx, &v.toObject());
|
||||
if (!JS_DeepFreezeObject(cx, obj))
|
||||
NativeObject* nobj = &obj->as<NativeObject>();
|
||||
for (uint32_t i = 0, n = nobj->slotSpan(); i < n; ++i) {
|
||||
if (!DeepFreezeSlot(cx, nobj->getSlot(i)))
|
||||
return false;
|
||||
}
|
||||
for (uint32_t i = 0, n = nobj->getDenseInitializedLength(); i < n; ++i) {
|
||||
if (!DeepFreezeSlot(cx, nobj->getDenseElement(i)))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -476,7 +476,7 @@ js::SetIntegrityLevel(JSContext* cx, HandleObject obj, IntegrityLevel level)
|
|||
assertSameCompartment(cx, obj);
|
||||
|
||||
// Steps 3-5. (Steps 1-2 are redundant assertions.)
|
||||
if (!PreventExtensions(cx, obj))
|
||||
if (!PreventExtensions(cx, obj, level))
|
||||
return false;
|
||||
|
||||
// Steps 6-7.
|
||||
|
@ -484,10 +484,6 @@ js::SetIntegrityLevel(JSContext* cx, HandleObject obj, IntegrityLevel level)
|
|||
if (!GetPropertyKeys(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY | JSITER_SYMBOLS, &keys))
|
||||
return false;
|
||||
|
||||
// PreventExtensions must sparsify dense objects, so we can assign to holes
|
||||
// without checks.
|
||||
MOZ_ASSERT_IF(obj->isNative(), obj->as<NativeObject>().getDenseCapacity() == 0);
|
||||
|
||||
// Steps 8-9, loosely interpreted.
|
||||
if (obj->isNative() && !obj->as<NativeObject>().inDictionaryMode() &&
|
||||
!obj->is<TypedArrayObject>())
|
||||
|
@ -2640,7 +2636,7 @@ js::SetPrototype(JSContext* cx, HandleObject obj, HandleObject proto)
|
|||
}
|
||||
|
||||
bool
|
||||
js::PreventExtensions(JSContext* cx, HandleObject obj, ObjectOpResult& result)
|
||||
js::PreventExtensions(JSContext* cx, HandleObject obj, ObjectOpResult& result, IntegrityLevel level)
|
||||
{
|
||||
if (obj->is<ProxyObject>())
|
||||
return js::Proxy::preventExtensions(cx, obj, result);
|
||||
|
@ -2656,13 +2652,19 @@ js::PreventExtensions(JSContext* cx, HandleObject obj, ObjectOpResult& result)
|
|||
if (!js::GetPropertyKeys(cx, obj, JSITER_HIDDEN | JSITER_OWNONLY, &props))
|
||||
return false;
|
||||
|
||||
// Convert all dense elements to sparse properties. This will shrink the
|
||||
// initialized length and capacity of the object to zero and ensure that no
|
||||
// new dense elements can be added without calling growElements(), which
|
||||
// checks isExtensible().
|
||||
// Actually prevent extension. If the object is being frozen, do it by
|
||||
// setting the frozen flag on both the object and the object group.
|
||||
// Otherwise, fallback to sparsifying the object, which makes sure no
|
||||
// element can be added without a call to isExtensible, at the cost of
|
||||
// performance.
|
||||
if (obj->isNative()) {
|
||||
if (!NativeObject::sparsifyDenseElements(cx, obj.as<NativeObject>()))
|
||||
if (level == IntegrityLevel::Frozen) {
|
||||
MarkObjectGroupFlags(cx, obj, OBJECT_FLAG_FROZEN);
|
||||
if (!ObjectElements::FreezeElements(cx, obj.as<NativeObject>()))
|
||||
return false;
|
||||
} else if (!NativeObject::sparsifyDenseElements(cx, obj.as<NativeObject>())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!obj->setFlags(cx, BaseShape::NOT_EXTENSIBLE, JSObject::GENERATE_SHAPE))
|
||||
|
@ -2671,10 +2673,10 @@ js::PreventExtensions(JSContext* cx, HandleObject obj, ObjectOpResult& result)
|
|||
}
|
||||
|
||||
bool
|
||||
js::PreventExtensions(JSContext* cx, HandleObject obj)
|
||||
js::PreventExtensions(JSContext* cx, HandleObject obj, IntegrityLevel level)
|
||||
{
|
||||
ObjectOpResult result;
|
||||
return PreventExtensions(cx, obj, result) && result.checkStrict(cx, obj);
|
||||
return PreventExtensions(cx, obj, result, level) && result.checkStrict(cx, obj);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
@ -75,8 +75,13 @@ extern const Class MathClass;
|
|||
class GlobalObject;
|
||||
class NewObjectCache;
|
||||
|
||||
enum class IntegrityLevel {
|
||||
Sealed,
|
||||
Frozen
|
||||
};
|
||||
|
||||
// Forward declarations, required for later friend declarations.
|
||||
bool PreventExtensions(JSContext* cx, JS::HandleObject obj, JS::ObjectOpResult& result);
|
||||
bool PreventExtensions(JSContext* cx, JS::HandleObject obj, JS::ObjectOpResult& result, IntegrityLevel level = IntegrityLevel::Sealed);
|
||||
bool SetImmutablePrototype(js::ExclusiveContext* cx, JS::HandleObject obj, bool* succeeded);
|
||||
|
||||
} /* namespace js */
|
||||
|
@ -106,7 +111,7 @@ class JSObject : public js::gc::Cell
|
|||
friend class js::NewObjectCache;
|
||||
friend class js::Nursery;
|
||||
friend class js::gc::RelocationOverlay;
|
||||
friend bool js::PreventExtensions(JSContext* cx, JS::HandleObject obj, JS::ObjectOpResult& result);
|
||||
friend bool js::PreventExtensions(JSContext* cx, JS::HandleObject obj, JS::ObjectOpResult& result, IntegrityLevel level);
|
||||
friend bool js::SetImmutablePrototype(js::ExclusiveContext* cx, JS::HandleObject obj,
|
||||
bool* succeeded);
|
||||
|
||||
|
@ -748,13 +753,16 @@ IsExtensible(ExclusiveContext* cx, HandleObject obj, bool* extensible);
|
|||
* ES6 [[PreventExtensions]]. Attempt to change the [[Extensible]] bit on |obj|
|
||||
* to false. Indicate success or failure through the |result| outparam, or
|
||||
* actual error through the return value.
|
||||
*
|
||||
* The `level` argument is SM-specific. `obj` should have an integrity level of
|
||||
* at least `level`.
|
||||
*/
|
||||
extern bool
|
||||
PreventExtensions(JSContext* cx, HandleObject obj, ObjectOpResult& result);
|
||||
PreventExtensions(JSContext* cx, HandleObject obj, ObjectOpResult& result, IntegrityLevel level);
|
||||
|
||||
/* Convenience function. As above, but throw on failure. */
|
||||
extern bool
|
||||
PreventExtensions(JSContext* cx, HandleObject obj);
|
||||
PreventExtensions(JSContext* cx, HandleObject obj, IntegrityLevel level = IntegrityLevel::Sealed);
|
||||
|
||||
/*
|
||||
* ES6 [[GetOwnProperty]]. Get a description of one of obj's own properties.
|
||||
|
@ -1339,11 +1347,6 @@ Throw(JSContext* cx, jsid id, unsigned errorNumber);
|
|||
extern bool
|
||||
Throw(JSContext* cx, JSObject* obj, unsigned errorNumber);
|
||||
|
||||
enum class IntegrityLevel {
|
||||
Sealed,
|
||||
Frozen
|
||||
};
|
||||
|
||||
/*
|
||||
* ES6 rev 29 (6 Dec 2014) 7.3.13. Mark obj as non-extensible, and adjust each
|
||||
* of obj's own properties' attributes appropriately: each property becomes
|
||||
|
|
|
@ -101,6 +101,24 @@ ObjectElements::MakeElementsCopyOnWrite(ExclusiveContext* cx, NativeObject* obj)
|
|||
return true;
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
ObjectElements::FreezeElements(ExclusiveContext* cx, HandleNativeObject obj)
|
||||
{
|
||||
if (!obj->maybeCopyElementsForWrite(cx))
|
||||
return false;
|
||||
|
||||
if (obj->hasEmptyElements())
|
||||
return true;
|
||||
|
||||
ObjectElements* header = obj->getElementsHeader();
|
||||
|
||||
// Note: this method doesn't update type information to indicate that the
|
||||
// elements might be frozen. Handling this is left to the caller.
|
||||
header->freeze();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void
|
||||
js::NativeObject::checkShapeConsistency()
|
||||
|
@ -2305,7 +2323,9 @@ SetExistingProperty(JSContext* cx, HandleNativeObject obj, HandleId id, HandleVa
|
|||
{
|
||||
// Step 5 for dense elements.
|
||||
if (IsImplicitDenseOrTypedArrayElement(shape)) {
|
||||
// Step 5.a is a no-op: all dense elements are writable.
|
||||
// Step 5.a.
|
||||
if (obj->getElementsHeader()->isFrozen())
|
||||
return result.fail(JSMSG_READ_ONLY);
|
||||
|
||||
// Pure optimization for the common case:
|
||||
if (receiver.isObject() && pobj == &receiver.toObject())
|
||||
|
|
|
@ -182,6 +182,9 @@ class ObjectElements
|
|||
// memory. This is a static property of the TypedArray, set when it
|
||||
// is created and never changed.
|
||||
SHARED_MEMORY = 0x8,
|
||||
|
||||
// These elements are set to integrity level "frozen".
|
||||
FROZEN = 0x10,
|
||||
};
|
||||
|
||||
private:
|
||||
|
@ -286,6 +289,15 @@ class ObjectElements
|
|||
|
||||
static bool ConvertElementsToDoubles(JSContext* cx, uintptr_t elements);
|
||||
static bool MakeElementsCopyOnWrite(ExclusiveContext* cx, NativeObject* obj);
|
||||
static bool FreezeElements(ExclusiveContext* cx, HandleNativeObject obj);
|
||||
|
||||
bool isFrozen() const {
|
||||
return flags & FROZEN;
|
||||
}
|
||||
void freeze() {
|
||||
MOZ_ASSERT(!isFrozen());
|
||||
flags |= FROZEN;
|
||||
}
|
||||
|
||||
// This is enough slots to store an object of this class. See the static
|
||||
// assertion below.
|
||||
|
|
|
@ -175,6 +175,8 @@ GetShapeAttributes(JSObject* obj, Shape* shape)
|
|||
if (IsImplicitDenseOrTypedArrayElement(shape)) {
|
||||
if (obj->is<TypedArrayObject>())
|
||||
return JSPROP_ENUMERATE | JSPROP_PERMANENT;
|
||||
if (obj->as<NativeObject>().getElementsHeader()->isFrozen())
|
||||
return JSPROP_ENUMERATE | JSPROP_PERMANENT | JSPROP_READONLY;
|
||||
return JSPROP_ENUMERATE;
|
||||
}
|
||||
|
||||
|
|
|
@ -145,7 +145,8 @@ enum : uint32_t {
|
|||
/* Whether any objects have been iterated over. */
|
||||
OBJECT_FLAG_ITERATED = 0x00080000,
|
||||
|
||||
/* 0x00100000 is not used. */
|
||||
/* Whether any object this represents may be frozen. */
|
||||
OBJECT_FLAG_FROZEN = 0x00100000,
|
||||
|
||||
/*
|
||||
* For the function on a run-once script, whether the function has actually
|
||||
|
|
Загрузка…
Ссылка в новой задаче