Diff
Modified: releases/WebKitGTK/webkit-2.28/JSTests/ChangeLog (255946 => 255947)
--- releases/WebKitGTK/webkit-2.28/JSTests/ChangeLog 2020-02-06 15:11:43 UTC (rev 255946)
+++ releases/WebKitGTK/webkit-2.28/JSTests/ChangeLog 2020-02-06 15:11:52 UTC (rev 255947)
@@ -1,3 +1,43 @@
+2020-02-05 Justin Michaud <[email protected]>
+
+ Deleting a property should not turn structures into uncacheable dictionaries
+ https://bugs.webkit.org/show_bug.cgi?id=206430
+
+ Reviewed by Yusuke Suzuki.
+
+ * microbenchmarks/delete-property-from-prototype-chain.js: Added.
+ (assert):
+ (noInline.assert.getZ):
+ (noInline.getZ.C):
+ (doTest):
+ (delete.C.prototype.z):
+ * microbenchmarks/delete-property-keeps-cacheable-structure.js: Added.
+ (assert):
+ (C):
+ (doTest):
+ * stress/cache-put-by-id-different-attributes.js:
+ (makePrototypeDict):
+ (set x):
+ * stress/cache-put-by-id-different-offset.js:
+ (makePrototypeDict):
+ (set x):
+ * stress/cache-put-by-id-poly-proto.js:
+ (makePrototypeDict):
+ (set _):
+ * stress/delete-property-check-structure-transition.js: Added.
+ (assert):
+ (assert_eq):
+ (assert_neq):
+ (sd):
+ (sid):
+ (testDeleteIsNotUncacheable):
+ (testCanMaterializeDeletes):
+ (testCanFlatten):
+ (testDeleteWithInlineCache.Object.prototype.globalProperty.42.makeFoo):
+ (testDeleteWithInlineCache.noInline.doTest):
+ (testDeleteWithInlineCache):
+ * stress/flatten-object-zero-unused-inline-properties.js:
+
2020-02-04 Alexey Shvayka <[email protected]>
Quantifiers after lookahead assertions should be syntax errors in Unicode patterns only
Added: releases/WebKitGTK/webkit-2.28/JSTests/microbenchmarks/delete-property-from-prototype-chain.js (0 => 255947)
--- releases/WebKitGTK/webkit-2.28/JSTests/microbenchmarks/delete-property-from-prototype-chain.js (rev 0)
+++ releases/WebKitGTK/webkit-2.28/JSTests/microbenchmarks/delete-property-from-prototype-chain.js 2020-02-06 15:11:52 UTC (rev 255947)
@@ -0,0 +1,43 @@
+//@ skip if $model == "Apple Watch Series 3" # added by mark-jsc-stress-test.py
+function assert(b) {
+ if (!b)
+ throw new Error;
+}
+noInline(assert)
+
+function getZ(o) {
+ return o.z
+}
+noInline(getZ)
+
+function C() {
+ this.x = 42;
+}
+
+let objs = [];
+for (let i = 0; i < 50; ++i) {
+ objs.push(new C);
+}
+
+function doTest(zVal) {
+ for (let i = 0; i < objs.length; ++i) {
+ let o = objs[i];
+ assert(o.x === 42);
+ assert(getZ(o) === zVal)
+ }
+}
+noInline(doTest);
+
+for (let i=0; i<10000; ++i) {
+ const X = { i }
+ C.prototype.z = X
+ doTest(X)
+}
+
+delete C.prototype.z
+
+for (let i=0; i<1000000; ++i) {
+ getZ({z: i})
+ doTest(undefined)
+}
+
Added: releases/WebKitGTK/webkit-2.28/JSTests/microbenchmarks/delete-property-keeps-cacheable-structure.js (0 => 255947)
--- releases/WebKitGTK/webkit-2.28/JSTests/microbenchmarks/delete-property-keeps-cacheable-structure.js (rev 0)
+++ releases/WebKitGTK/webkit-2.28/JSTests/microbenchmarks/delete-property-keeps-cacheable-structure.js 2020-02-06 15:11:52 UTC (rev 255947)
@@ -0,0 +1,28 @@
+//@ skip if $model == "Apple Watch Series 3" # added by mark-jsc-stress-test.py
+function assert(b) {
+ if (!b)
+ throw new Error;
+}
+
+function C() {
+ this.x = 42;
+ this.y = 1337;
+ delete this.y;
+}
+
+let objs = [];
+for (let i = 0; i < 50; ++i) {
+ objs.push(new C);
+}
+
+function doTest() {
+ for (let i = 0; i < objs.length; ++i) {
+ let o = objs[i];
+ assert(o.y === undefined);
+ assert(o.x === 42);
+ }
+}
+noInline(doTest);
+
+for (let i=0; i<10000000; ++i) doTest()
+
Modified: releases/WebKitGTK/webkit-2.28/JSTests/stress/cache-put-by-id-different-attributes.js (255946 => 255947)
--- releases/WebKitGTK/webkit-2.28/JSTests/stress/cache-put-by-id-different-attributes.js 2020-02-06 15:11:43 UTC (rev 255946)
+++ releases/WebKitGTK/webkit-2.28/JSTests/stress/cache-put-by-id-different-attributes.js 2020-02-06 15:11:52 UTC (rev 255947)
@@ -4,6 +4,16 @@
Foo.prototype.x = 0;
+function makePrototypeDict() {
+ for (let i=0; i<100; ++i) {
+ Foo.prototype.a = i
+ Foo.prototype.b = i
+ delete Foo.prototype.a
+ delete Foo.prototype.b
+ }
+}
+noInline(makePrototypeDict)
+
Object.defineProperty(Foo.prototype, 'y', {
set(x) {
if (Foo.prototype.x++ === 9) {
@@ -11,8 +21,10 @@
value: 13,
writable: true,
});
- if (typeof $vm !== 'undefined')
+ if (typeof $vm !== 'undefined') {
+ makePrototypeDict()
$vm.flattenDictionaryObject(Foo.prototype);
+ }
}
},
configurable: true,
Modified: releases/WebKitGTK/webkit-2.28/JSTests/stress/cache-put-by-id-different-offset.js (255946 => 255947)
--- releases/WebKitGTK/webkit-2.28/JSTests/stress/cache-put-by-id-different-offset.js 2020-02-06 15:11:43 UTC (rev 255946)
+++ releases/WebKitGTK/webkit-2.28/JSTests/stress/cache-put-by-id-different-offset.js 2020-02-06 15:11:52 UTC (rev 255947)
@@ -4,12 +4,24 @@
Foo.prototype.x = 0;
+function makePrototypeDict() {
+ for (let i=0; i<100; ++i) {
+ Foo.prototype.a = i
+ Foo.prototype.b = i
+ delete Foo.prototype.a
+ delete Foo.prototype.b
+ }
+}
+noInline(makePrototypeDict)
+
Object.defineProperty(Foo.prototype, 'y', {
set(x) {
if (Foo.prototype.x++ === 1) {
delete Foo.prototype.x;
- if (typeof $vm !== 'undefined')
+ if (typeof $vm !== 'undefined') {
+ makePrototypeDict()
$vm.flattenDictionaryObject(Foo.prototype);
+ }
}
}
});
Modified: releases/WebKitGTK/webkit-2.28/JSTests/stress/cache-put-by-id-poly-proto.js (255946 => 255947)
--- releases/WebKitGTK/webkit-2.28/JSTests/stress/cache-put-by-id-poly-proto.js 2020-02-06 15:11:43 UTC (rev 255946)
+++ releases/WebKitGTK/webkit-2.28/JSTests/stress/cache-put-by-id-poly-proto.js 2020-02-06 15:11:52 UTC (rev 255947)
@@ -4,6 +4,16 @@
let x = 0;
+function makePrototypeDict() {
+ for (let i=0; i<100; ++i) {
+ Foo.prototype.a = i
+ Foo.prototype.b = i
+ delete Foo.prototype.a
+ delete Foo.prototype.b
+ }
+}
+noInline(makePrototypeDict)
+
Object.defineProperty(Foo.prototype, 'y', {
set(_) {
if (x++ === 9) {
@@ -11,8 +21,10 @@
value: 13,
writable: true,
});
- if (typeof $vm !== 'undefined')
+ if (typeof $vm !== 'undefined') {
+ makePrototypeDict()
$vm.flattenDictionaryObject(Foo.prototype);
+ }
}
},
configurable: true,
Added: releases/WebKitGTK/webkit-2.28/JSTests/stress/delete-property-check-structure-transition.js (0 => 255947)
--- releases/WebKitGTK/webkit-2.28/JSTests/stress/delete-property-check-structure-transition.js (rev 0)
+++ releases/WebKitGTK/webkit-2.28/JSTests/stress/delete-property-check-structure-transition.js 2020-02-06 15:11:52 UTC (rev 255947)
@@ -0,0 +1,189 @@
+function assert(condition) {
+ if (!condition)
+ throw new Error("assertion failed");
+}
+
+function assert_eq(a, b) {
+ if (a !== b)
+ throw new Error("assertion failed: " + a + " === " + b);
+}
+
+function assert_neq(a, b) {
+ if (a === b)
+ throw new Error("assertion failed: " + a + " !== " + b);
+}
+
+function sd(obj) {
+ let data = ""
+ let result = []
+
+ if (!data)
+ return result
+
+ for (let i = 0; i < data.length/5; ++i) {
+ result.push({ id: data[i*5+0], offset: data[i*5+1], max: data[i*5+2], property: data[i*5+3], type: data[i*5+4] == 0 ? "added" : "deleted" })
+ }
+
+ return result
+}
+
+function sid(obj) {
+ let data = ""
+ return data[data.length-1].id
+}
+
+function testDeleteIsNotUncacheable(i) {
+ let foo = {}
+ foo["bar" + i] = 1
+ foo["baz" + i] = 2
+ assert(foo["bar" + i] === 1)
+ assert(foo["baz" + i] === 2)
+ let oldSid = sid(foo)
+
+ assert_eq($vm.getConcurrently(foo, "baz"+i), 1)
+ assert_eq($vm.getConcurrently(foo, "bar"+i), 1)
+ assert_eq($vm.getConcurrently(foo, "foo"+i), 0)
+
+ assert(delete foo["bar" + i])
+ assert_neq(oldSid, sid(foo))
+ assert(!(("bar" + i) in foo));
+ assert(foo["baz" + i] === 2)
+
+ assert_eq($vm.getConcurrently(foo, "baz"+i), 1)
+ assert_eq($vm.getConcurrently(foo, "bar"+i), 0)
+
+ assert_eq(Object.keys(foo).length, 1)
+
+ let data = ""
+ assert_eq(data[data.length-1].property, "bar" + i)
+
+ foo["bar" + i] = 1
+ assert_eq($vm.getConcurrently(foo, "baz"+i), 1)
+ assert_eq($vm.getConcurrently(foo, "bar"+i), 1)
+ assert(foo["bar" + i] === 1)
+}
+
+function testCanMaterializeDeletes(i) {
+ let foo = {}
+ foo["bar" + i] = 1
+ foo["baz" + i] = 2
+
+ assert(foo["bar" + i] === 1)
+ assert(foo["baz" + i] === 2)
+ assert(delete foo["bar" + i])
+ assert(!("bar" + i in foo))
+ assert(foo["baz" + i] === 2)
+ assert_eq(Object.keys(foo).length, 1)
+
+ let foo2 = {}
+ foo2["bar" + i] = 3
+ foo2["baz" + i] = 4
+ assert(delete foo2["bar" + i])
+
+ assert_eq(sid(foo2), sid(foo))
+ foo2["fun" + i] = 3
+ assert_neq(sid(foo2), sid(foo))
+
+ assert(foo2["fun" + i] === 3)
+ assert(foo2["baz" + i] === 4)
+ assert(!("bar" + i in foo2))
+ assert_eq(Object.keys(foo2).length, 2)
+ assert(foo["baz" + i] === 2)
+ assert(!("bar" + i in foo))
+ assert_eq(Object.keys(foo).length, 1)
+
+ let data = ""
+ assert_eq(data[data.length-1].property, "bar" + i)
+
+ data = ""
+ assert_eq(data[data.length-1].property, "fun" + i)
+ assert_eq(data[data.length-2].property, "bar" + i)
+}
+
+function testCanFlatten(i) {
+ let foo = {}
+ for (let j=0; j<500; ++j) {
+ const oldId = sid(foo)
+
+ foo["x" + 1000*j + i] = j
+ if (j > 0)
+ delete foo["x" + 1000*(j - 1) + i]
+
+ if (j > 100)
+ assert_eq(sid(foo), oldId)
+ }
+
+ for (let j=0; j<500; ++j) {
+ const val = foo["x" + 1000*j + i]
+ if (j == 499)
+ assert_eq(val, j)
+ else
+ assert_eq(val, undefined)
+ }
+
+ $vm.flattenDictionaryObject(foo)
+
+ for (let j=0; j<500; ++j) {
+ const val = foo["x" + 1000*j + i]
+ if (j == 499)
+ assert_eq(val, j)
+ else
+ assert_eq(val, undefined)
+ }
+}
+
+function testDeleteWithInlineCache() {
+ Object.prototype.globalProperty = 42
+
+ function makeFoo() {
+ let foo = {}
+ foo.baz = 1
+ assert(foo.globalProperty === 42)
+
+ return foo
+ }
+ noInline(makeFoo)
+
+ function doTest(xVal) {
+ for (let j=0; j<50; ++j) {
+ for (let z=0; z<10000; ++z) {
+ const foo = arr[j]
+
+ assert(foo.baz === 1)
+ assert_eq(Object.keys(foo).length, 1)
+ assert_eq(foo.globalProperty, xVal)
+ }
+ }
+ }
+ noInline(doTest)
+
+ arr = new Array(50)
+
+ for (let j=0; j<50; ++j) {
+ arr[j] = makeFoo()
+ if (j > 0)
+ assert_eq(sid(arr[j-1]), sid(arr[j]))
+ }
+
+ doTest(42)
+
+ Object.prototype.globalProperty = 43
+
+ doTest(43)
+
+ delete Object.prototype.globalProperty
+
+ doTest(undefined)
+}
+
+testDeleteWithInlineCache()
+
+for (let i = 0; i < 1000; ++i) {
+ testDeleteIsNotUncacheable(i)
+ testCanMaterializeDeletes(1000+i)
+}
+
+for (let i = 0; i < 100; ++i) {
+ testCanFlatten(2000+i)
+}
+
Modified: releases/WebKitGTK/webkit-2.28/JSTests/stress/flatten-object-zero-unused-inline-properties.js (255946 => 255947)
--- releases/WebKitGTK/webkit-2.28/JSTests/stress/flatten-object-zero-unused-inline-properties.js 2020-02-06 15:11:43 UTC (rev 255946)
+++ releases/WebKitGTK/webkit-2.28/JSTests/stress/flatten-object-zero-unused-inline-properties.js 2020-02-06 15:11:52 UTC (rev 255947)
@@ -1,4 +1,10 @@
let o = { foo: 1, bar: 2, baz: 3 };
+for (let i=0; i<100; ++i) {
+ o.a = i
+ o.b = i
+ delete o.a
+ delete o.b
+}
if ($vm.inlineCapacity(o) <= 3)
throw new Error("There should be inline capacity");
Modified: releases/WebKitGTK/webkit-2.28/Source/_javascript_Core/ChangeLog (255946 => 255947)
--- releases/WebKitGTK/webkit-2.28/Source/_javascript_Core/ChangeLog 2020-02-06 15:11:43 UTC (rev 255946)
+++ releases/WebKitGTK/webkit-2.28/Source/_javascript_Core/ChangeLog 2020-02-06 15:11:52 UTC (rev 255947)
@@ -1,3 +1,64 @@
+2020-02-05 Justin Michaud <[email protected]>
+
+ Deleting a property should not turn structures into uncacheable dictionaries
+ https://bugs.webkit.org/show_bug.cgi?id=206430
+
+ Reviewed by Yusuke Suzuki.
+
+ Right now, deleteProperty/removePropertyTransition causes a structure transition to uncacheable dictionary. Instead, we should allow it to transition to a new regular structure like adding a property does. This means that we have to:
+
+ 1) Break the assumption that structure transition offsets increase monotonically
+
+ We add a new flag to tell that a structure has deleted its property, and update materializePropertyTable to use it.
+
+ 2) Add a new transition map and transition kind for deletes
+
+ We cache the delete transition. We will not transition back to a previous structure if you add then immediately remove a property.
+
+ 3) Find some heuristic for when we should actually transition to uncacheable dictionary.
+
+ Since deleting properties is expected to be rare, we just walk the structure list and count its size on removal.
+
+ This patch also fixes a related bug in addProperty, where we did not use a GCSafeConcurrentJSLocker, and adds an option to trigger the bug. Finally, we add some helper methods to dollarVM to test.
+
+ This gives a 24x speedup on delete-property-keeps-cacheable-structure.js, and is neutral on delete-property-from-prototype-chain.js (which was already generating code using the inline cache).
+
+ * heap/HeapInlines.h:
+ (JSC::Heap::decrementDeferralDepthAndGCIfNeeded):
+ * runtime/JSObject.cpp:
+ (JSC::JSObject::deleteProperty):
+ * runtime/OptionsList.h:
+ * runtime/PropertyMapHashTable.h:
+ (JSC::PropertyTable::get):
+ (JSC::PropertyTable::add):
+ (JSC::PropertyTable::addDeletedOffset):
+ (JSC::PropertyTable::reinsert):
+ * runtime/Structure.cpp:
+ (JSC::StructureTransitionTable::contains const):
+ (JSC::StructureTransitionTable::get const):
+ (JSC::StructureTransitionTable::add):
+ (JSC::Structure::Structure):
+ (JSC::Structure::materializePropertyTable):
+ (JSC::Structure::addNewPropertyTransition):
+ (JSC::Structure::removePropertyTransition):
+ (JSC::Structure::removePropertyTransitionFromExistingStructure):
+ (JSC::Structure::removeNewPropertyTransition):
+ (JSC::Structure::toUncacheableDictionaryTransition):
+ (JSC::Structure::remove):
+ (JSC::Structure::visitChildren):
+ * runtime/Structure.h:
+ * runtime/StructureInlines.h:
+ (JSC::Structure::forEachPropertyConcurrently):
+ (JSC::Structure::add):
+ (JSC::Structure::remove):
+ (JSC::Structure::removePropertyWithoutTransition):
+ * runtime/StructureTransitionTable.h:
+ (JSC::StructureTransitionTable::Hash::hash):
+ * tools/JSDollarVM.cpp:
+ (JSC::JSDollarVMHelper::functionGetStructureTransitionList):
+ (JSC::functionGetConcurrently):
+ (JSC::JSDollarVM::finishCreation):
+
2020-02-05 Devin Rousso <[email protected]>
Web Inspector: Sources: add a special breakpoint for controlling whether `debugger` statements pause
Modified: releases/WebKitGTK/webkit-2.28/Source/_javascript_Core/heap/HeapInlines.h (255946 => 255947)
--- releases/WebKitGTK/webkit-2.28/Source/_javascript_Core/heap/HeapInlines.h 2020-02-06 15:11:43 UTC (rev 255946)
+++ releases/WebKitGTK/webkit-2.28/Source/_javascript_Core/heap/HeapInlines.h 2020-02-06 15:11:52 UTC (rev 255947)
@@ -188,7 +188,7 @@
ASSERT(!Thread::mayBeGCThread() || m_worldIsStopped);
m_deferralDepth--;
- if (UNLIKELY(m_didDeferGCWork)) {
+ if (UNLIKELY(m_didDeferGCWork) || Options::forceDidDeferGCWork()) {
decrementDeferralDepthAndGCIfNeededSlow();
// Here are the possible relationships between m_deferralDepth and m_didDeferGCWork.
Modified: releases/WebKitGTK/webkit-2.28/Source/_javascript_Core/runtime/JSObject.cpp (255946 => 255947)
--- releases/WebKitGTK/webkit-2.28/Source/_javascript_Core/runtime/JSObject.cpp 2020-02-06 15:11:43 UTC (rev 255946)
+++ releases/WebKitGTK/webkit-2.28/Source/_javascript_Core/runtime/JSObject.cpp 2020-02-06 15:11:52 UTC (rev 255947)
@@ -2001,13 +2001,23 @@
if (propertyIsPresent) {
if (attributes & PropertyAttribute::DontDelete && vm.deletePropertyMode() != VM::DeletePropertyMode::IgnoreConfigurable)
return false;
+ DeferredStructureTransitionWatchpointFire deferredWatchpointFire(vm, structure);
- PropertyOffset offset;
+ PropertyOffset offset = invalidOffset;
if (structure->isUncacheableDictionary())
- offset = structure->removePropertyWithoutTransition(vm, propertyName, [] (const ConcurrentJSLocker&, PropertyOffset) { });
- else
- thisObject->setStructure(vm, Structure::removePropertyTransition(vm, structure, propertyName, offset));
+ offset = structure->removePropertyWithoutTransition(vm, propertyName, [] (const GCSafeConcurrentJSLocker&, PropertyOffset, PropertyOffset) { });
+ else {
+ structure = Structure::removePropertyTransition(vm, structure, propertyName, offset, &deferredWatchpointFire);
+ if (thisObject->m_butterfly && !structure->outOfLineCapacity() && !structure->hasIndexingHeader(thisObject)) {
+ thisObject->nukeStructureAndSetButterfly(vm, thisObject->structureID(), nullptr);
+ offset = invalidOffset;
+ ASSERT(structure->maxOffset() == invalidOffset);
+ }
+ thisObject->setStructure(vm, structure);
+ }
+ ASSERT(!isValidOffset(structure->get(vm, propertyName, attributes)));
+
if (offset != invalidOffset)
thisObject->locationForOffset(offset)->clear();
}
Modified: releases/WebKitGTK/webkit-2.28/Source/_javascript_Core/runtime/OptionsList.h (255946 => 255947)
--- releases/WebKitGTK/webkit-2.28/Source/_javascript_Core/runtime/OptionsList.h 2020-02-06 15:11:43 UTC (rev 255946)
+++ releases/WebKitGTK/webkit-2.28/Source/_javascript_Core/runtime/OptionsList.h 2020-02-06 15:11:52 UTC (rev 255947)
@@ -347,6 +347,7 @@
v(Bool, useGC, true, Normal, nullptr) \
v(Bool, gcAtEnd, false, Normal, "If true, the jsc CLI will do a GC before exiting") \
v(Bool, forceGCSlowPaths, false, Normal, "If true, we will force all JIT fast allocations down their slow paths.") \
+ v(Bool, forceDidDeferGCWork, false, Normal, "If true, we will force all DeferGC destructions to perform a GC.") \
v(Unsigned, gcMaxHeapSize, 0, Normal, nullptr) \
v(Unsigned, forceRAMSize, 0, Normal, nullptr) \
v(Bool, recordGCPauseTimes, false, Normal, nullptr) \
Modified: releases/WebKitGTK/webkit-2.28/Source/_javascript_Core/runtime/PropertyMapHashTable.h (255946 => 255947)
--- releases/WebKitGTK/webkit-2.28/Source/_javascript_Core/runtime/PropertyMapHashTable.h 2020-02-06 15:11:43 UTC (rev 255946)
+++ releases/WebKitGTK/webkit-2.28/Source/_javascript_Core/runtime/PropertyMapHashTable.h 2020-02-06 15:11:52 UTC (rev 255947)
@@ -169,8 +169,7 @@
find_iterator find(const KeyType&);
ValueType* get(const KeyType&);
// Add a value to the table
- enum EffectOnPropertyOffset { PropertyOffsetMayChange, PropertyOffsetMustNotChange };
- std::pair<find_iterator, bool> add(const ValueType& entry, PropertyOffset&, EffectOnPropertyOffset);
+ std::pair<find_iterator, bool> WARN_UNUSED_RETURN add(const ValueType& entry);
// Remove a value from the table.
void remove(const find_iterator& iter);
void remove(const KeyType& key);
@@ -321,6 +320,7 @@
{
ASSERT(key);
ASSERT(key->isAtom() || key->isSymbol());
+ ASSERT(key != PROPERTY_MAP_DELETED_ENTRY_KEY);
if (!m_keyCount)
return nullptr;
@@ -335,8 +335,10 @@
unsigned entryIndex = m_index[hash & m_indexMask];
if (entryIndex == EmptyEntryIndex)
return nullptr;
- if (key == table()[entryIndex - 1].key)
+ if (key == table()[entryIndex - 1].key) {
+ ASSERT(!m_deletedOffsets || !m_deletedOffsets->contains(table()[entryIndex - 1].offset));
return &table()[entryIndex - 1];
+ }
#if DUMP_PROPERTYMAP_STATS
++propertyMapHashTableStats->numLookupProbing;
@@ -346,14 +348,14 @@
}
}
-inline std::pair<PropertyTable::find_iterator, bool> PropertyTable::add(const ValueType& entry, PropertyOffset& offset, EffectOnPropertyOffset offsetEffect)
+inline std::pair<PropertyTable::find_iterator, bool> WARN_UNUSED_RETURN PropertyTable::add(const ValueType& entry)
{
+ ASSERT(!m_deletedOffsets || !m_deletedOffsets->contains(entry.offset));
+
// Look for a value with a matching key already in the array.
find_iterator iter = find(entry.key);
- if (iter.first) {
- RELEASE_ASSERT(iter.first->offset <= offset);
+ if (iter.first)
return std::make_pair(iter, false);
- }
#if DUMP_PROPERTYMAP_STATS
++propertyMapHashTableStats->numAdds;
@@ -377,11 +379,6 @@
++m_keyCount;
- if (offsetEffect == PropertyOffsetMayChange)
- offset = std::max(offset, entry.offset);
- else
- RELEASE_ASSERT(offset >= entry.offset);
-
return std::make_pair(iter, true);
}
@@ -451,6 +448,7 @@
{
if (!m_deletedOffsets)
m_deletedOffsets = makeUnique<Vector<PropertyOffset>>();
+ ASSERT(!m_deletedOffsets->contains(offset));
m_deletedOffsets->append(offset);
}
Modified: releases/WebKitGTK/webkit-2.28/Source/_javascript_Core/runtime/Structure.cpp (255946 => 255947)
--- releases/WebKitGTK/webkit-2.28/Source/_javascript_Core/runtime/Structure.cpp 2020-02-06 15:11:43 UTC (rev 255946)
+++ releases/WebKitGTK/webkit-2.28/Source/_javascript_Core/runtime/Structure.cpp 2020-02-06 15:11:52 UTC (rev 255947)
@@ -87,22 +87,22 @@
m_data = bitwise_cast<intptr_t>(impl) | UsingSingleSlotFlag;
}
-bool StructureTransitionTable::contains(UniquedStringImpl* rep, unsigned attributes) const
+bool StructureTransitionTable::contains(UniquedStringImpl* rep, unsigned attributes, bool isAddition) const
{
if (isUsingSingleSlot()) {
Structure* transition = singleTransition();
- return transition && transition->m_transitionPropertyName == rep && transition->transitionPropertyAttributes() == attributes;
+ return transition && transition->m_transitionPropertyName == rep && transition->transitionPropertyAttributes() == attributes && transition->isPropertyDeletionTransition() == !isAddition;
}
- return map()->get(std::make_pair(rep, attributes));
+ return map()->get(std::make_tuple(rep, attributes, isAddition));
}
-inline Structure* StructureTransitionTable::get(UniquedStringImpl* rep, unsigned attributes) const
+inline Structure* StructureTransitionTable::get(UniquedStringImpl* rep, unsigned attributes, bool isAddition) const
{
if (isUsingSingleSlot()) {
Structure* transition = singleTransition();
- return (transition && transition->m_transitionPropertyName == rep && transition->transitionPropertyAttributes() == attributes) ? transition : 0;
+ return (transition && transition->m_transitionPropertyName == rep && transition->transitionPropertyAttributes() == attributes && transition->isPropertyDeletionTransition() == !isAddition) ? transition : 0;
}
- return map()->get(std::make_pair(rep, attributes));
+ return map()->get(std::make_tuple(rep, attributes, isAddition));
}
void StructureTransitionTable::add(VM& vm, Structure* structure)
@@ -127,7 +127,7 @@
// Newer versions of the STL have an std::make_pair function that takes rvalue references.
// When either of the parameters are bitfields, the C++ compiler will try to bind them as lvalues, which is invalid. To work around this, use unary "+" to make the parameter an rvalue.
// See https://bugs.webkit.org/show_bug.cgi?id=59261 for more details
- map()->set(std::make_pair(structure->m_transitionPropertyName.get(), +structure->transitionPropertyAttributes()), structure);
+ map()->set(std::make_tuple(structure->m_transitionPropertyName.get(), +structure->transitionPropertyAttributes(), !structure->isPropertyDeletionTransition()), structure);
}
void Structure::dumpStatistics()
@@ -200,7 +200,8 @@
setStaticPropertiesReified(false);
setTransitionWatchpointIsLikelyToBeFired(false);
setHasBeenDictionary(false);
- setIsAddingPropertyForTransition(false);
+ setProtectPropertyTableWhileTransitioning(false);
+ setIsPropertyDeletionTransition(false);
setTransitionOffset(vm, invalidOffset);
setMaxOffset(vm, invalidOffset);
@@ -236,7 +237,8 @@
setStaticPropertiesReified(false);
setTransitionWatchpointIsLikelyToBeFired(false);
setHasBeenDictionary(false);
- setIsAddingPropertyForTransition(false);
+ setProtectPropertyTableWhileTransitioning(false);
+ setIsPropertyDeletionTransition(false);
setTransitionOffset(vm, invalidOffset);
setMaxOffset(vm, invalidOffset);
@@ -272,7 +274,8 @@
setDidTransition(true);
setStaticPropertiesReified(previous->staticPropertiesReified());
setHasBeenDictionary(previous->hasBeenDictionary());
- setIsAddingPropertyForTransition(false);
+ setProtectPropertyTableWhileTransitioning(false);
+ setIsPropertyDeletionTransition(false);
setTransitionOffset(vm, invalidOffset);
setMaxOffset(vm, invalidOffset);
@@ -356,7 +359,7 @@
PropertyTable* Structure::materializePropertyTable(VM& vm, bool setPropertyTable)
{
ASSERT(structure(vm)->classInfo() == info());
- ASSERT(!isAddingPropertyForTransition());
+ ASSERT(!protectPropertyTableWhileTransitioning());
DeferGC deferGC(vm.heap);
@@ -384,10 +387,19 @@
structure = structures[i];
if (!structure->m_transitionPropertyName)
continue;
- ASSERT(i == structures.size() - 1 || structure->maxOffset() > structures[i + 1]->maxOffset());
+ if (structure->isPropertyDeletionTransition()) {
+ auto item = table->find(structure->m_transitionPropertyName.get());
+ ASSERT(item.first);
+ table->remove(item);
+ table->addDeletedOffset(structure->transitionOffset());
+ continue;
+ }
PropertyMapEntry entry(structure->m_transitionPropertyName.get(), structure->transitionOffset(), structure->transitionPropertyAttributes());
- auto maxOffset = this->maxOffset();
- table->add(entry, maxOffset, PropertyTable::PropertyOffsetMustNotChange);
+ auto nextOffset = table->nextOffset(structure->inlineCapacity());
+ ASSERT_UNUSED(nextOffset, nextOffset == structure->transitionOffset());
+ auto result = table->add(entry);
+ ASSERT_UNUSED(result, result.second);
+ ASSERT_UNUSED(result, result.first.first->offset == nextOffset);
}
checkOffsetConsistency(
@@ -410,7 +422,8 @@
ASSERT(!structure->isDictionary());
ASSERT(structure->isObject());
- if (Structure* existingTransition = structure->m_transitionTable.get(uid, attributes)) {
+ constexpr bool isAddition = true;
+ if (Structure* existingTransition = structure->m_transitionTable.get(uid, attributes, isAddition)) {
validateOffset(existingTransition->transitionOffset(), existingTransition->inlineCapacity());
offset = existingTransition->transitionOffset();
return existingTransition;
@@ -484,7 +497,6 @@
Structure* transition = toCacheableDictionaryTransition(vm, structure, deferred);
ASSERT(structure != transition);
offset = transition->add(vm, propertyName, attributes);
- transition->setTransitionOffset(vm, offset);
return transition;
}
@@ -500,10 +512,10 @@
// Holding the lock ensures that we either do this before the GC starts scanning the structure, in
// which case the GC will not blow the table away, or we do it after the GC already ran in which
// case all is well. If it wasn't for the lock, the GC would have TOCTOU: if could read
- // isAddingPropertyForTransition before we set it to true, and then blow the table away after.
+ // protectPropertyTableWhileTransitioning before we set it to true, and then blow the table away after.
{
ConcurrentJSLocker locker(transition->m_lock);
- transition->setIsAddingPropertyForTransition(true);
+ transition->setProtectPropertyTableWhileTransitioning(true);
}
transition->m_blob.setIndexingModeIncludingHistory(structure->indexingModeIncludingHistory() & ~CopyOnWrite);
@@ -518,12 +530,11 @@
// Now that everything is fine with the new structure's bookkeeping, the GC is free to blow the
// table away if it wants. We can now rebuild it fine.
WTF::storeStoreFence();
- transition->setIsAddingPropertyForTransition(false);
+ transition->setProtectPropertyTableWhileTransitioning(false);
checkOffset(transition->transitionOffset(), transition->inlineCapacity());
{
- ConcurrentJSLocker locker(structure->m_lock);
- DeferGC deferGC(vm.heap);
+ GCSafeConcurrentJSLocker locker(structure->m_lock, vm.heap);
structure->m_transitionTable.add(vm, transition);
}
transition->checkOffsetConsistency();
@@ -531,27 +542,85 @@
return transition;
}
-Structure* Structure::removePropertyTransition(VM& vm, Structure* structure, PropertyName propertyName, PropertyOffset& offset)
+Structure* Structure::removePropertyTransition(VM& vm, Structure* structure, PropertyName propertyName, PropertyOffset& offset, DeferredStructureTransitionWatchpointFire* deferred)
{
- // NOTE: There are some good reasons why this goes directly to uncacheable dictionary rather than
- // caching the removal. We can fix all of these things, but we must remember to do so, if we ever try
- // to optimize this case.
- //
- // - Cached transitions usually steal the property table, and assume that this is possible because they
- // can just rebuild the table by looking at past transitions. That code assumes that the table only
- // grew and never shrank. To support removals, we'd have to change the property table materialization
- // code to handle deletions. Also, we have logic to get the list of properties on a structure that
- // lacks a property table by just looking back through the set of transitions since the last
- // structure that had a pinned table. That logic would also have to be changed to handle cached
- // removals.
- //
+ Structure* newStructure = removePropertyTransitionFromExistingStructure(
+ vm, structure, propertyName, offset, deferred);
+ if (newStructure)
+ return newStructure;
+
+ return removeNewPropertyTransition(
+ vm, structure, propertyName, offset, deferred);
+}
+
+Structure* Structure::removePropertyTransitionFromExistingStructure(VM& vm, Structure* structure, PropertyName propertyName, PropertyOffset& offset, DeferredStructureTransitionWatchpointFire*)
+{
+ ASSERT(!isCompilationThread());
ASSERT(!structure->isUncacheableDictionary());
+ ASSERT(structure->isObject());
- Structure* transition = toUncacheableDictionaryTransition(vm, structure);
+ unsigned attributes;
+ structure->get(vm, propertyName, attributes);
- offset = transition->remove(propertyName);
+ constexpr bool isAddition = false;
+ if (Structure* existingTransition = structure->m_transitionTable.get(propertyName.uid(), attributes, isAddition)) {
+ validateOffset(existingTransition->transitionOffset(), existingTransition->inlineCapacity());
+ offset = existingTransition->transitionOffset();
+ return existingTransition;
+ }
+ return nullptr;
+}
+
+Structure* Structure::removeNewPropertyTransition(VM& vm, Structure* structure, PropertyName propertyName, PropertyOffset& offset, DeferredStructureTransitionWatchpointFire* deferred)
+{
+ ASSERT(!structure->isUncacheableDictionary());
+ ASSERT(structure->isObject());
+ ASSERT(!Structure::removePropertyTransitionFromExistingStructure(vm, structure, propertyName, offset, deferred));
+
+ int transitionCount = 0;
+ for (auto* s = structure; s && transitionCount <= s_maxTransitionLength; s = s->previousID())
+ ++transitionCount;
+
+ if (transitionCount > s_maxTransitionLength) {
+ ASSERT(!isCopyOnWrite(structure->indexingMode()));
+ Structure* transition = toUncacheableDictionaryTransition(vm, structure, deferred);
+ ASSERT(structure != transition);
+ offset = transition->remove(vm, propertyName);
+ return transition;
+ }
+
+ Structure* transition = create(vm, structure, deferred);
+ transition->m_cachedPrototypeChain.setMayBeNull(vm, transition, structure->m_cachedPrototypeChain.get());
+
+ // While we are deleting the property, we need to make sure the table is not cleared.
+ {
+ ConcurrentJSLocker locker(transition->m_lock);
+ transition->setProtectPropertyTableWhileTransitioning(true);
+ }
+
+ transition->m_blob.setIndexingModeIncludingHistory(structure->indexingModeIncludingHistory() & ~CopyOnWrite);
+ transition->m_transitionPropertyName = propertyName.uid();
+ transition->setPropertyTable(vm, structure->takePropertyTableOrCloneIfPinned(vm));
+ transition->setMaxOffset(vm, structure->maxOffset());
+ transition->setIsPropertyDeletionTransition(true);
+
+ offset = transition->remove(vm, propertyName);
+ ASSERT(offset != invalidOffset);
+ transition->setTransitionOffset(vm, offset);
+
+ // Now that everything is fine with the new structure's bookkeeping, the GC is free to blow the
+ // table away if it wants. We can now rebuild it fine.
+ WTF::storeStoreFence();
+ transition->setProtectPropertyTableWhileTransitioning(false);
+
+ checkOffset(transition->transitionOffset(), transition->inlineCapacity());
+ {
+ GCSafeConcurrentJSLocker locker(structure->m_lock, vm.heap);
+ structure->m_transitionTable.add(vm, transition);
+ }
transition->checkOffsetConsistency();
+ structure->checkOffsetConsistency();
return transition;
}
@@ -614,9 +683,9 @@
return toDictionaryTransition(vm, structure, CachedDictionaryKind, deferred);
}
-Structure* Structure::toUncacheableDictionaryTransition(VM& vm, Structure* structure)
+Structure* Structure::toUncacheableDictionaryTransition(VM& vm, Structure* structure, DeferredStructureTransitionWatchpointFire* deferred)
{
- return toDictionaryTransition(vm, structure, UncachedDictionaryKind);
+ return toDictionaryTransition(vm, structure, UncachedDictionaryKind, deferred);
}
Structure* Structure::sealTransition(VM& vm, Structure* structure)
@@ -655,7 +724,8 @@
IndexingType indexingModeIncludingHistory = newIndexingType(structure->indexingModeIncludingHistory(), transitionKind);
Structure* existingTransition;
- if (!structure->isDictionary() && (existingTransition = structure->m_transitionTable.get(0, attributes))) {
+ constexpr bool isAddition = true;
+ if (!structure->isDictionary() && (existingTransition = structure->m_transitionTable.get(0, attributes, isAddition))) {
ASSERT(existingTransition->transitionPropertyAttributes() == attributes);
ASSERT(existingTransition->indexingModeIncludingHistory() == indexingModeIncludingHistory);
return existingTransition;
@@ -977,9 +1047,11 @@
});
}
-PropertyOffset Structure::remove(PropertyName propertyName)
+PropertyOffset Structure::remove(VM& vm, PropertyName propertyName)
{
- return remove(propertyName, [] (const ConcurrentJSLocker&, PropertyOffset) { });
+ return remove<ShouldPin::No>(vm, propertyName, [this, &vm] (const GCSafeConcurrentJSLocker&, PropertyOffset, PropertyOffset newMaxOffset) {
+ setMaxOffset(vm, newMaxOffset);
+ });
}
void Structure::getPropertyNamesFromStructure(VM& vm, PropertyNameArray& propertyNames, EnumerationMode mode)
@@ -1059,7 +1131,7 @@
}
visitor.append(thisObject->m_previousOrRareData);
- if (thisObject->isPinnedPropertyTable() || thisObject->isAddingPropertyForTransition()) {
+ if (thisObject->isPinnedPropertyTable() || thisObject->protectPropertyTableWhileTransitioning()) {
// NOTE: This can interleave in pin(), in which case it may see a null property table.
// That's fine, because then the barrier will fire and we will scan this again.
visitor.append(thisObject->m_propertyTableUnsafe);
Modified: releases/WebKitGTK/webkit-2.28/Source/_javascript_Core/runtime/Structure.h (255946 => 255947)
--- releases/WebKitGTK/webkit-2.28/Source/_javascript_Core/runtime/Structure.h 2020-02-06 15:11:43 UTC (rev 255946)
+++ releases/WebKitGTK/webkit-2.28/Source/_javascript_Core/runtime/Structure.h 2020-02-06 15:11:52 UTC (rev 255947)
@@ -189,11 +189,13 @@
JS_EXPORT_PRIVATE static Structure* addNewPropertyTransition(VM&, Structure*, PropertyName, unsigned attributes, PropertyOffset&, PutPropertySlot::Context = PutPropertySlot::UnknownContext, DeferredStructureTransitionWatchpointFire* = nullptr);
static Structure* addPropertyTransitionToExistingStructureConcurrently(Structure*, UniquedStringImpl* uid, unsigned attributes, PropertyOffset&);
JS_EXPORT_PRIVATE static Structure* addPropertyTransitionToExistingStructure(Structure*, PropertyName, unsigned attributes, PropertyOffset&);
- static Structure* removePropertyTransition(VM&, Structure*, PropertyName, PropertyOffset&);
+ static Structure* removeNewPropertyTransition(VM&, Structure*, PropertyName, PropertyOffset&, DeferredStructureTransitionWatchpointFire* = nullptr);
+ static Structure* removePropertyTransition(VM&, Structure*, PropertyName, PropertyOffset&, DeferredStructureTransitionWatchpointFire* = nullptr);
+ static Structure* removePropertyTransitionFromExistingStructure(VM&, Structure*, PropertyName, PropertyOffset&, DeferredStructureTransitionWatchpointFire* = nullptr);
static Structure* changePrototypeTransition(VM&, Structure*, JSValue prototype, DeferredStructureTransitionWatchpointFire&);
JS_EXPORT_PRIVATE static Structure* attributeChangeTransition(VM&, Structure*, PropertyName, unsigned attributes);
JS_EXPORT_PRIVATE static Structure* toCacheableDictionaryTransition(VM&, Structure*, DeferredStructureTransitionWatchpointFire* = nullptr);
- static Structure* toUncacheableDictionaryTransition(VM&, Structure*);
+ static Structure* toUncacheableDictionaryTransition(VM&, Structure*, DeferredStructureTransitionWatchpointFire* = nullptr);
JS_EXPORT_PRIVATE static Structure* sealTransition(VM&, Structure*);
JS_EXPORT_PRIVATE static Structure* freezeTransition(VM&, Structure*);
static Structure* preventExtensionsTransition(VM&, Structure*);
@@ -450,10 +452,6 @@
{
return std::min<unsigned>(maxOffset() + 1, m_inlineCapacity);
}
- unsigned totalStorageSize() const
- {
- return numberOfSlotsForMaxOffset(maxOffset(), m_inlineCapacity);
- }
unsigned totalStorageCapacity() const
{
ASSERT(structure()->classInfo() == info());
@@ -701,8 +699,9 @@
DEFINE_BITFIELD(bool, didWatchInternalProperties, DidWatchInternalProperties, 1, 25);
DEFINE_BITFIELD(bool, transitionWatchpointIsLikelyToBeFired, TransitionWatchpointIsLikelyToBeFired, 1, 26);
DEFINE_BITFIELD(bool, hasBeenDictionary, HasBeenDictionary, 1, 27);
- DEFINE_BITFIELD(bool, isAddingPropertyForTransition, IsAddingPropertyForTransition, 1, 28);
+ DEFINE_BITFIELD(bool, protectPropertyTableWhileTransitioning, ProtectPropertyTableWhileTransitioning, 1, 28);
DEFINE_BITFIELD(bool, hasUnderscoreProtoPropertyExcludingOriginalProto, HasUnderscoreProtoPropertyExcludingOriginalProto, 1, 29);
+ DEFINE_BITFIELD(bool, isPropertyDeletionTransition, IsPropertyDeletionTransition, 1, 30);
private:
friend class LLIntOffsetsExtractor;
@@ -727,9 +726,9 @@
template<ShouldPin, typename Func>
PropertyOffset add(VM&, PropertyName, unsigned attributes, const Func&);
PropertyOffset add(VM&, PropertyName, unsigned attributes);
- template<typename Func>
- PropertyOffset remove(PropertyName, const Func&);
- PropertyOffset remove(PropertyName);
+ template<ShouldPin, typename Func>
+ PropertyOffset remove(VM&, PropertyName, const Func&);
+ PropertyOffset remove(VM&, PropertyName);
void checkConsistency();
@@ -841,6 +840,7 @@
TinyBloomFilter m_seenProperties;
friend class VMInspector;
+ friend class JSDollarVMHelper;
};
} // namespace JSC
Modified: releases/WebKitGTK/webkit-2.28/Source/_javascript_Core/runtime/StructureInlines.h (255946 => 255947)
--- releases/WebKitGTK/webkit-2.28/Source/_javascript_Core/runtime/StructureInlines.h 2020-02-06 15:11:43 UTC (rev 255946)
+++ releases/WebKitGTK/webkit-2.28/Source/_javascript_Core/runtime/StructureInlines.h 2020-02-06 15:11:52 UTC (rev 255947)
@@ -163,29 +163,41 @@
void Structure::forEachPropertyConcurrently(const Functor& functor)
{
Vector<Structure*, 8> structures;
- Structure* structure;
+ Structure* tableStructure;
PropertyTable* table;
- findStructuresAndMapForMaterialization(structures, structure, table);
+ findStructuresAndMapForMaterialization(structures, tableStructure, table);
+
+ HashSet<UniquedStringImpl*> seenProperties;
+
+ for (auto* structure : structures) {
+ if (!structure->m_transitionPropertyName || seenProperties.contains(structure->m_transitionPropertyName.get()))
+ continue;
+
+ seenProperties.add(structure->m_transitionPropertyName.get());
+
+ if (structure->isPropertyDeletionTransition())
+ continue;
+
+ if (!functor(PropertyMapEntry(structure->m_transitionPropertyName.get(), structure->transitionOffset(), structure->transitionPropertyAttributes()))) {
+ if (table)
+ tableStructure->m_lock.unlock();
+ return;
+ }
+ }
if (table) {
for (auto& entry : *table) {
+ if (seenProperties.contains(entry.key))
+ continue;
+
if (!functor(entry)) {
- structure->m_lock.unlock();
+ tableStructure->m_lock.unlock();
return;
}
}
- structure->m_lock.unlock();
+ tableStructure->m_lock.unlock();
}
-
- for (unsigned i = structures.size(); i--;) {
- structure = structures[i];
- if (!structure->m_transitionPropertyName)
- continue;
-
- if (!functor(PropertyMapEntry(structure->m_transitionPropertyName.get(), structure->transitionOffset(), structure->transitionPropertyAttributes())))
- return;
- }
}
template<typename Functor>
@@ -451,12 +463,13 @@
PropertyOffset newOffset = table->nextOffset(m_inlineCapacity);
m_propertyHash = m_propertyHash ^ rep->existingSymbolAwareHash();
+ m_seenProperties.add(bitwise_cast<uintptr_t>(rep));
- m_seenProperties.add(bitwise_cast<uintptr_t>(rep));
+ auto result = table->add(PropertyMapEntry(rep, newOffset, attributes));
+ ASSERT_UNUSED(result, result.second);
+ ASSERT_UNUSED(result, result.first.first->offset == newOffset);
+ auto newMaxOffset = std::max(newOffset, maxOffset());
- PropertyOffset newMaxOffset = maxOffset();
- table->add(PropertyMapEntry(rep, newOffset, attributes), newMaxOffset, PropertyTable::PropertyOffsetMayChange);
-
func(locker, newOffset, newMaxOffset);
ASSERT(maxOffset() == newMaxOffset);
@@ -465,25 +478,32 @@
return newOffset;
}
-template<typename Func>
-inline PropertyOffset Structure::remove(PropertyName propertyName, const Func& func)
+template<Structure::ShouldPin shouldPin, typename Func>
+inline PropertyOffset Structure::remove(VM& vm, PropertyName propertyName, const Func& func)
{
- ConcurrentJSLocker locker(m_lock);
-
+ PropertyTable* table = ensurePropertyTable(vm);
+ GCSafeConcurrentJSLocker locker(m_lock, vm.heap);
+
+ switch (shouldPin) {
+ case ShouldPin::Yes:
+ pin(locker, vm, table);
+ break;
+ case ShouldPin::No:
+ setPropertyTable(vm, table);
+ break;
+ }
+
+ ASSERT(JSC::isValidOffset(get(vm, propertyName)));
+
checkConsistency();
auto rep = propertyName.uid();
-
- // We ONLY remove from uncacheable dictionaries, which will have a pinned property table.
- // The only way for them not to have a table is if they are empty.
- PropertyTable* table = propertyTableOrNull();
- if (!table)
- return invalidOffset;
-
PropertyTable::find_iterator position = table->find(rep);
if (!position.first)
return invalidOffset;
+
+ setIsQuickPropertyAccessAllowedForEnumeration(false);
PropertyOffset offset = position.first->offset;
@@ -490,9 +510,14 @@
table->remove(position);
table->addDeletedOffset(offset);
+ PropertyOffset newMaxOffset = maxOffset();
+
+ func(locker, offset, newMaxOffset);
+
+ ASSERT(maxOffset() == newMaxOffset);
+ ASSERT(!JSC::isValidOffset(get(vm, propertyName)));
+
checkConsistency();
-
- func(locker, offset);
return offset;
}
@@ -503,13 +528,13 @@
}
template<typename Func>
-inline PropertyOffset Structure::removePropertyWithoutTransition(VM&, PropertyName propertyName, const Func& func)
+inline PropertyOffset Structure::removePropertyWithoutTransition(VM& vm, PropertyName propertyName, const Func& func)
{
ASSERT(isUncacheableDictionary());
ASSERT(isPinnedPropertyTable());
ASSERT(propertyTableOrNull());
- return remove(propertyName, func);
+ return remove<ShouldPin::Yes>(vm, propertyName, func);
}
ALWAYS_INLINE void Structure::setPrototypeWithoutTransition(VM& vm, JSValue prototype)
Modified: releases/WebKitGTK/webkit-2.28/Source/_javascript_Core/runtime/StructureTransitionTable.h (255946 => 255947)
--- releases/WebKitGTK/webkit-2.28/Source/_javascript_Core/runtime/StructureTransitionTable.h 2020-02-06 15:11:43 UTC (rev 255946)
+++ releases/WebKitGTK/webkit-2.28/Source/_javascript_Core/runtime/StructureTransitionTable.h 2020-02-06 15:11:52 UTC (rev 255947)
@@ -144,11 +144,11 @@
struct Hash {
- typedef std::pair<UniquedStringImpl*, unsigned> Key;
+ typedef std::tuple<UniquedStringImpl*, unsigned, bool> Key;
static unsigned hash(const Key& p)
{
- return PtrHash<UniquedStringImpl*>::hash(p.first) + p.second;
+ return PtrHash<UniquedStringImpl*>::hash(std::get<0>(p)) + std::get<1>(p);
}
static bool equal(const Key& a, const Key& b)
@@ -181,8 +181,8 @@
}
void add(VM&, Structure*);
- bool contains(UniquedStringImpl*, unsigned attributes) const;
- Structure* get(UniquedStringImpl*, unsigned attributes) const;
+ bool contains(UniquedStringImpl*, unsigned attributes, bool isAddition) const;
+ Structure* get(UniquedStringImpl*, unsigned attributes, bool isAddition) const;
private:
friend class SingleSlotTransitionWeakOwner;
Modified: releases/WebKitGTK/webkit-2.28/Source/_javascript_Core/tools/JSDollarVM.cpp (255946 => 255947)
--- releases/WebKitGTK/webkit-2.28/Source/_javascript_Core/tools/JSDollarVM.cpp 2020-02-06 15:11:43 UTC (rev 255946)
+++ releases/WebKitGTK/webkit-2.28/Source/_javascript_Core/tools/JSDollarVM.cpp 2020-02-06 15:11:52 UTC (rev 255947)
@@ -81,6 +81,8 @@
void updateVMStackLimits() { return m_vm.updateStackLimits(); };
+ static EncodedJSValue JSC_HOST_CALL functionGetStructureTransitionList(JSGlobalObject*, CallFrame*);
+
private:
VM& m_vm;
};
@@ -2769,6 +2771,63 @@
return JSValue::encode(jsString(vm, String::adopt(WTFMove(buffer))));
}
+EncodedJSValue JSC_HOST_CALL JSDollarVMHelper::functionGetStructureTransitionList(JSGlobalObject* globalObject, CallFrame* callFrame)
+{
+ DollarVMAssertScope assertScope;
+ VM& vm = globalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ JSObject* obj = callFrame->argument(0).toObject(globalObject);
+ RETURN_IF_EXCEPTION(scope, { });
+ if (!obj)
+ return JSValue::encode(jsNull());
+ Vector<Structure*, 8> structures;
+
+ for (auto* structure = obj->structure(); structure; structure = structure->previousID())
+ structures.append(structure);
+
+ JSArray* result = JSArray::tryCreate(vm, globalObject->arrayStructureForIndexingTypeDuringAllocation(ArrayWithContiguous), 0);
+ RETURN_IF_EXCEPTION(scope, { });
+
+ for (size_t i = 0; i < structures.size(); ++i) {
+ auto* structure = structures[structures.size() - i - 1];
+ result->push(globalObject, JSValue(structure->id()));
+ RETURN_IF_EXCEPTION(scope, { });
+ result->push(globalObject, JSValue(structure->transitionOffset()));
+ RETURN_IF_EXCEPTION(scope, { });
+ result->push(globalObject, JSValue(structure->maxOffset()));
+ RETURN_IF_EXCEPTION(scope, { });
+ if (structure->m_transitionPropertyName)
+ result->push(globalObject, jsString(vm, String(*structure->m_transitionPropertyName)));
+ else
+ result->push(globalObject, jsNull());
+ RETURN_IF_EXCEPTION(scope, { });
+ result->push(globalObject, JSValue(structure->isPropertyDeletionTransition()));
+ RETURN_IF_EXCEPTION(scope, { });
+ }
+
+ return JSValue::encode(result);
+}
+
+static EncodedJSValue JSC_HOST_CALL functionGetConcurrently(JSGlobalObject* globalObject, CallFrame* callFrame)
+{
+ DollarVMAssertScope assertScope;
+ VM& vm = globalObject->vm();
+ auto scope = DECLARE_THROW_SCOPE(vm);
+ JSObject* obj = callFrame->argument(0).toObject(globalObject);
+ RETURN_IF_EXCEPTION(scope, { });
+ if (!obj)
+ return JSValue::encode(jsNull());
+ String property = callFrame->argument(1).toWTFString(globalObject);
+ RETURN_IF_EXCEPTION(scope, { });
+ auto name = PropertyName(Identifier::fromString(vm, property));
+ auto offset = obj->structure()->getConcurrently(name.uid());
+ if (offset != invalidOffset)
+ ASSERT(JSValue::encode(obj->getDirect(offset)));
+ JSValue result = JSValue(offset != invalidOffset);
+ RETURN_IF_EXCEPTION(scope, { });
+ return JSValue::encode(result);
+}
+
void JSDollarVM::finishCreation(VM& vm)
{
DollarVMAssertScope assertScope;
@@ -2897,6 +2956,9 @@
addFunction(vm, "isWasmSupported", functionIsWasmSupported, 0);
addFunction(vm, "make16BitStringIfPossible", functionMake16BitStringIfPossible, 1);
+ addFunction(vm, "getStructureTransitionList", JSDollarVMHelper::functionGetStructureTransitionList, 1);
+ addFunction(vm, "getConcurrently", functionGetConcurrently, 2);
+
m_objectDoingSideEffectPutWithoutCorrectSlotStatusStructure.set(vm, this, ObjectDoingSideEffectPutWithoutCorrectSlotStatus::createStructure(vm, globalObject, jsNull()));
}