зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1715512 part 11 - Add fast path for sealing/freezing properties. r=jonco
This corresponds to the fast path we currently have in SetIntegrityLevel, but makes it more of a core operation. The old Shape-based implementation doesn't support dictionary objects, but this patch adds code for dictionary maps because it's easy to implement and is much faster than the generic seal/freeze code. Differential Revision: https://phabricator.services.mozilla.com/D117311
This commit is contained in:
Родитель
26c07b486d
Коммит
e10ed74d26
|
@ -0,0 +1,26 @@
|
|||
let sym = Symbol();
|
||||
|
||||
let o = {x: 1, y: 2, z: 3, a: 4, b: 5, 12345678: 6, [sym]: 7};
|
||||
for (let i = 0; i < 100; i++) {
|
||||
o["foo" + i] = 1;
|
||||
}
|
||||
delete o.x;
|
||||
|
||||
Object.seal(o);
|
||||
assertEq(Object.getOwnPropertyNames(o).length, 105);
|
||||
assertEq(Object.getOwnPropertySymbols(o).length, 1);
|
||||
|
||||
assertEq(Object.isSealed(o), true);
|
||||
assertEq(Object.isFrozen(o), false);
|
||||
|
||||
let desc = Object.getOwnPropertyDescriptor(o, "y");
|
||||
assertEq(desc.writable, true);
|
||||
assertEq(desc.configurable, false);
|
||||
|
||||
Object.freeze(o);
|
||||
assertEq(Object.isSealed(o), true);
|
||||
assertEq(Object.isFrozen(o), true);
|
||||
|
||||
desc = Object.getOwnPropertyDescriptor(o, "y");
|
||||
assertEq(desc.writable, false);
|
||||
assertEq(desc.configurable, false);
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
#include "gc/Allocator.h"
|
||||
#include "gc/HashUtil.h"
|
||||
#include "js/GCVector.h"
|
||||
#include "vm/JSObject.h"
|
||||
|
||||
#include "vm/ObjectFlags-inl.h"
|
||||
|
@ -432,6 +433,83 @@ bool SharedPropMap::addPropertyInternal(JSContext* cx,
|
|||
return true;
|
||||
}
|
||||
|
||||
static PropertyFlags ComputeFlagsForSealOrFreeze(PropertyKey key,
|
||||
PropertyFlags flags,
|
||||
IntegrityLevel level) {
|
||||
// Private fields are not visible to SetIntegrityLevel.
|
||||
if (key.isSymbol() && key.toSymbol()->isPrivateName()) {
|
||||
return flags;
|
||||
}
|
||||
|
||||
// Make all properties non-configurable; if freezing, make data properties
|
||||
// read-only.
|
||||
flags.clearFlag(PropertyFlag::Configurable);
|
||||
if (level == IntegrityLevel::Frozen && flags.isDataDescriptor()) {
|
||||
flags.clearFlag(PropertyFlag::Writable);
|
||||
}
|
||||
|
||||
return flags;
|
||||
}
|
||||
|
||||
// static
|
||||
bool SharedPropMap::freezeOrSealProperties(JSContext* cx, IntegrityLevel level,
|
||||
const JSClass* clasp,
|
||||
MutableHandle<SharedPropMap*> map,
|
||||
uint32_t mapLength,
|
||||
ObjectFlags* objectFlags) {
|
||||
// Add all maps to a Vector so we can iterate over them in reverse order
|
||||
// (property definition order).
|
||||
JS::RootedVector<SharedPropMap*> maps(cx);
|
||||
{
|
||||
SharedPropMap* curMap = map;
|
||||
while (true) {
|
||||
if (!maps.append(curMap)) {
|
||||
return false;
|
||||
}
|
||||
if (!curMap->hasPrevious()) {
|
||||
break;
|
||||
}
|
||||
curMap = curMap->asNormal()->previous();
|
||||
}
|
||||
}
|
||||
|
||||
// Build a new SharedPropMap by adding each property with the changed
|
||||
// attributes.
|
||||
Rooted<SharedPropMap*> newMap(cx);
|
||||
uint32_t newMapLength = 0;
|
||||
|
||||
Rooted<PropertyKey> key(cx);
|
||||
Rooted<SharedPropMap*> curMap(cx);
|
||||
|
||||
for (size_t i = maps.length(); i > 0; i--) {
|
||||
curMap = maps[i - 1];
|
||||
uint32_t len = (i == 1) ? mapLength : PropMap::Capacity;
|
||||
|
||||
for (uint32_t j = 0; j < len; j++) {
|
||||
key = curMap->getKey(j);
|
||||
PropertyInfo prop = curMap->getPropertyInfo(j);
|
||||
PropertyFlags flags =
|
||||
ComputeFlagsForSealOrFreeze(key, prop.flags(), level);
|
||||
|
||||
if (prop.isCustomDataProperty()) {
|
||||
if (!addCustomDataProperty(cx, clasp, &newMap, &newMapLength, key,
|
||||
flags, objectFlags)) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!addPropertyWithKnownSlot(cx, clasp, &newMap, &newMapLength, key,
|
||||
flags, prop.slot(), objectFlags)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
map.set(newMap);
|
||||
MOZ_ASSERT(newMapLength == mapLength);
|
||||
return true;
|
||||
}
|
||||
|
||||
void LinkedPropMap::handOffTableTo(LinkedPropMap* next) {
|
||||
MOZ_ASSERT(hasTable());
|
||||
MOZ_ASSERT(!next->hasTable());
|
||||
|
@ -518,6 +596,27 @@ void DictionaryPropMap::changeProperty(JSContext* cx, const JSClass* clasp,
|
|||
linkedData_.propInfos[index] = PropertyInfo(flags, slot);
|
||||
}
|
||||
|
||||
void DictionaryPropMap::freezeOrSealProperties(JSContext* cx,
|
||||
IntegrityLevel level,
|
||||
const JSClass* clasp,
|
||||
uint32_t mapLength,
|
||||
ObjectFlags* objectFlags) {
|
||||
DictionaryPropMap* curMap = this;
|
||||
do {
|
||||
for (uint32_t i = 0; i < mapLength; i++) {
|
||||
if (!curMap->hasKey(i)) {
|
||||
continue;
|
||||
}
|
||||
PropertyKey key = curMap->getKey(i);
|
||||
PropertyFlags flags = curMap->getPropertyInfo(i).flags();
|
||||
flags = ComputeFlagsForSealOrFreeze(key, flags, level);
|
||||
curMap->changePropertyFlags(cx, clasp, i, flags, objectFlags);
|
||||
}
|
||||
curMap = curMap->previous();
|
||||
mapLength = PropMap::Capacity;
|
||||
} while (curMap);
|
||||
}
|
||||
|
||||
// static
|
||||
void DictionaryPropMap::skipTrailingHoles(MutableHandle<DictionaryPropMap*> map,
|
||||
uint32_t* mapLength) {
|
||||
|
|
|
@ -205,6 +205,8 @@
|
|||
|
||||
namespace js {
|
||||
|
||||
enum class IntegrityLevel;
|
||||
|
||||
class DictionaryPropMap;
|
||||
class SharedPropMap;
|
||||
class LinkedPropMap;
|
||||
|
@ -647,6 +649,14 @@ class SharedPropMap : public PropMap {
|
|||
PropertyFlags flags,
|
||||
ObjectFlags* objectFlags);
|
||||
|
||||
// Freeze or seal all properties by creating a new shared map. Returns the new
|
||||
// map and object flags.
|
||||
static bool freezeOrSealProperties(JSContext* cx, IntegrityLevel level,
|
||||
const JSClass* clasp,
|
||||
MutableHandle<SharedPropMap*> map,
|
||||
uint32_t mapLength,
|
||||
ObjectFlags* objectFlags);
|
||||
|
||||
// Create a new dictionary map as copy of this map.
|
||||
static DictionaryPropMap* toDictionaryMap(JSContext* cx,
|
||||
Handle<SharedPropMap*> map,
|
||||
|
@ -930,6 +940,12 @@ class DictionaryPropMap final : public PropMap {
|
|||
MutableHandle<DictionaryPropMap*> map,
|
||||
uint32_t* mapLength, NativeObject* obj);
|
||||
|
||||
// Freeze or seal all properties in this map. Returns the new object flags.
|
||||
// The caller is responsible for generating a new shape for the object.
|
||||
void freezeOrSealProperties(JSContext* cx, IntegrityLevel level,
|
||||
const JSClass* clasp, uint32_t mapLength,
|
||||
ObjectFlags* objectFlags);
|
||||
|
||||
// Change a property's slot number and/or flags and return the new object
|
||||
// flags. The caller is responsible for generating a new shape.
|
||||
void changeProperty(JSContext* cx, const JSClass* clasp, uint32_t index,
|
||||
|
|
Загрузка…
Ссылка в новой задаче