Diff
Modified: trunk/JSTests/ChangeLog (250539 => 250540)
--- trunk/JSTests/ChangeLog 2019-10-01 00:47:04 UTC (rev 250539)
+++ trunk/JSTests/ChangeLog 2019-10-01 00:50:46 UTC (rev 250540)
@@ -1,3 +1,50 @@
+2019-09-30 Saam Barati <sbar...@apple.com>
+
+ Inline caching is wrong for custom accessors and custom values
+ https://bugs.webkit.org/show_bug.cgi?id=201994
+ <rdar://problem/50850326>
+
+ Reviewed by Yusuke Suzuki.
+
+ * microbenchmarks/custom-accessor-materialized.js: Added.
+ (assert):
+ (test4.get const):
+ * microbenchmarks/custom-accessor-thin-air.js: Added.
+ (assert):
+ (test5.get const):
+ (test5.get proto):
+ * microbenchmarks/custom-accessor.js: Added.
+ (assert):
+ (test3.get const):
+ * microbenchmarks/custom-value-2.js: Added.
+ (assert):
+ (test1.getMultiline):
+ (test1):
+ * microbenchmarks/custom-value.js: Added.
+ (assert):
+ (test1.getMultiline):
+ (test1):
+ * stress/custom-accessor-delete-1.js: Added.
+ (assert):
+ (test3.get const):
+ * stress/custom-accessor-delete-2.js: Added.
+ (assert):
+ (test4.get const):
+ * stress/custom-accessor-delete-3.js: Added.
+ (assert):
+ (test5.get const):
+ (test5.get proto):
+ * stress/custom-value-delete-property-1.js: Added.
+ (assert):
+ (test1.getMultiline):
+ (test1):
+ * stress/custom-value-delete-property-2.js: Added.
+ (test2.foo):
+ (test2):
+ * stress/custom-value-delete-property-3.js: Added.
+ (test6.foo):
+ (test6):
+
2019-09-30 Yusuke Suzuki <ysuz...@apple.com>
[JSC] AI folds CompareEq wrongly when it sees proven Boolean and Number
Added: trunk/JSTests/microbenchmarks/custom-accessor-materialized.js (0 => 250540)
--- trunk/JSTests/microbenchmarks/custom-accessor-materialized.js (rev 0)
+++ trunk/JSTests/microbenchmarks/custom-accessor-materialized.js 2019-10-01 00:50:46 UTC (rev 250540)
@@ -0,0 +1,22 @@
+function assert(b) {
+ if (!b)
+ throw new Error;
+}
+
+function test4() {
+ function get(o) {
+ return o.testStaticAccessor;
+ }
+ noInline(get);
+
+ const proto = $vm.createStaticCustomAccessor();
+ const o = {__proto__: proto};
+ const d = Object.getOwnPropertyDescriptor(proto, "testStaticAccessor");
+ assert(!!d.get);
+ o.testField = 1337;
+
+ for (let i = 0; i < 500000; ++i) {
+ assert(get(o) === 1337);
+ }
+}
+test4();
Added: trunk/JSTests/microbenchmarks/custom-accessor-thin-air.js (0 => 250540)
--- trunk/JSTests/microbenchmarks/custom-accessor-thin-air.js (rev 0)
+++ trunk/JSTests/microbenchmarks/custom-accessor-thin-air.js 2019-10-01 00:50:46 UTC (rev 250540)
@@ -0,0 +1,24 @@
+function assert(b) {
+ if (!b)
+ throw new Error;
+}
+
+function test5() {
+ function get(o) {
+ return o.thinAirCustomGetter;
+ }
+ noInline(get);
+
+ const proto = $vm.createStaticCustomAccessor();
+ const o = {__proto__: proto};
+ o.testField = 1337;
+
+ for (let i = 0; i < 500000; ++i) {
+ assert(get(o) === 1337);
+ }
+ proto.xyz = 42;
+ for (let i = 0; i < 500000; ++i) {
+ assert(get(o) === 1337);
+ }
+}
+test5();
Added: trunk/JSTests/microbenchmarks/custom-accessor.js (0 => 250540)
--- trunk/JSTests/microbenchmarks/custom-accessor.js (rev 0)
+++ trunk/JSTests/microbenchmarks/custom-accessor.js 2019-10-01 00:50:46 UTC (rev 250540)
@@ -0,0 +1,20 @@
+function assert(b) {
+ if (!b)
+ throw new Error;
+}
+
+function test3() {
+ function get(o) {
+ return o.testStaticAccessor;
+ }
+ noInline(get);
+
+ const proto = $vm.createStaticCustomAccessor();
+ const o = {__proto__: proto};
+ o.testField = 1337;
+
+ for (let i = 0; i < 500000; ++i) {
+ assert(get(o) === 1337);
+ }
+}
+test3();
Added: trunk/JSTests/microbenchmarks/custom-value-2.js (0 => 250540)
--- trunk/JSTests/microbenchmarks/custom-value-2.js (rev 0)
+++ trunk/JSTests/microbenchmarks/custom-value-2.js 2019-10-01 00:50:46 UTC (rev 250540)
@@ -0,0 +1,26 @@
+function assert(b) {
+ if (!b)
+ throw new Error;
+}
+
+function test1() {
+ function getMultiline(o) {
+ return o.multiline;
+ }
+ noInline(getMultiline);
+
+ RegExp.foo = 42;
+
+ const o = {};
+ o.__proto__ = RegExp;
+ RegExp.multiline = false;
+
+ for (let i = 0; i < 5000000; ++i) {
+ assert(getMultiline(o) === false);
+ }
+ delete RegExp.foo;
+ for (let i = 0; i < 5000000; ++i) {
+ assert(getMultiline(o) === false);
+ }
+}
+test1();
Added: trunk/JSTests/microbenchmarks/custom-value.js (0 => 250540)
--- trunk/JSTests/microbenchmarks/custom-value.js (rev 0)
+++ trunk/JSTests/microbenchmarks/custom-value.js 2019-10-01 00:50:46 UTC (rev 250540)
@@ -0,0 +1,21 @@
+function assert(b) {
+ if (!b)
+ throw new Error;
+}
+
+function test1() {
+ function getMultiline(o) {
+ return o.multiline;
+ }
+ noInline(getMultiline);
+
+ const o = {};
+ o.__proto__ = RegExp;
+ RegExp.multiline = false;
+
+ for (let i = 0; i < 5000000; ++i) {
+ assert(getMultiline(o) === false);
+ }
+}
+
+test1();
Added: trunk/JSTests/stress/custom-accessor-delete-1.js (0 => 250540)
--- trunk/JSTests/stress/custom-accessor-delete-1.js (rev 0)
+++ trunk/JSTests/stress/custom-accessor-delete-1.js 2019-10-01 00:50:46 UTC (rev 250540)
@@ -0,0 +1,25 @@
+function assert(b) {
+ if (!b)
+ throw new Error;
+}
+
+function test3() {
+ function get(o) {
+ return o.testStaticAccessor;
+ }
+ noInline(get);
+
+ const proto = $vm.createStaticCustomAccessor();
+ const o = {__proto__: proto};
+ o.testField = 1337;
+
+ for (let i = 0; i < 500; ++i) {
+ assert(get(o) === 1337);
+ }
+
+ proto.xyz = 42;
+
+ assert(delete proto.testStaticAccessor);
+ assert(get(o) === undefined);
+}
+test3();
Added: trunk/JSTests/stress/custom-accessor-delete-2.js (0 => 250540)
--- trunk/JSTests/stress/custom-accessor-delete-2.js (rev 0)
+++ trunk/JSTests/stress/custom-accessor-delete-2.js 2019-10-01 00:50:46 UTC (rev 250540)
@@ -0,0 +1,27 @@
+function assert(b) {
+ if (!b)
+ throw new Error;
+}
+
+function test4() {
+ function get(o) {
+ return o.testStaticAccessor;
+ }
+ noInline(get);
+
+ const proto = $vm.createStaticCustomAccessor();
+ const o = {__proto__: proto};
+ const d = Object.getOwnPropertyDescriptor(proto, "testStaticAccessor");
+ assert(!!d.get);
+ o.testField = 1337;
+
+ for (let i = 0; i < 500; ++i) {
+ assert(get(o) === 1337);
+ }
+
+ proto.xyz = 42;
+
+ assert(delete proto.testStaticAccessor);
+ assert(get(o) === undefined);
+}
+test4();
Added: trunk/JSTests/stress/custom-accessor-delete-3.js (0 => 250540)
--- trunk/JSTests/stress/custom-accessor-delete-3.js (rev 0)
+++ trunk/JSTests/stress/custom-accessor-delete-3.js 2019-10-01 00:50:46 UTC (rev 250540)
@@ -0,0 +1,24 @@
+function assert(b) {
+ if (!b)
+ throw new Error;
+}
+
+function test5() {
+ function get(o) {
+ return o.thinAirCustomGetter;
+ }
+ noInline(get);
+
+ const proto = $vm.createStaticCustomAccessor();
+ const o = {__proto__: proto};
+ o.testField = 1337;
+
+ for (let i = 0; i < 500; ++i) {
+ assert(get(o) === 1337);
+ }
+ proto.xyz = 42;
+ for (let i = 0; i < 500; ++i) {
+ assert(get(o) === 1337);
+ }
+}
+test5();
Added: trunk/JSTests/stress/custom-value-delete-property-1.js (0 => 250540)
--- trunk/JSTests/stress/custom-value-delete-property-1.js (rev 0)
+++ trunk/JSTests/stress/custom-value-delete-property-1.js 2019-10-01 00:50:46 UTC (rev 250540)
@@ -0,0 +1,23 @@
+function assert(b) {
+ if (!b)
+ throw new Error;
+}
+
+function test1() {
+ function getMultiline(o) {
+ return o.multiline;
+ }
+ noInline(getMultiline);
+
+ const o = {};
+ o.__proto__ = RegExp;
+ RegExp.multiline = false;
+
+ for (let i = 0; i < 500; ++i) {
+ assert(getMultiline(o) === false);
+ }
+ delete RegExp.input;
+ delete RegExp.multiline;
+ assert(getMultiline(o) === undefined);
+}
+test1();
Added: trunk/JSTests/stress/custom-value-delete-property-2.js (0 => 250540)
--- trunk/JSTests/stress/custom-value-delete-property-2.js (rev 0)
+++ trunk/JSTests/stress/custom-value-delete-property-2.js 2019-10-01 00:50:46 UTC (rev 250540)
@@ -0,0 +1,14 @@
+function test2() {
+ function foo() {
+ const o = {};
+ for (let i = 0; i < 8; i++) {
+ let x = o.multiline;
+ o.__proto__ = RegExp;
+ }
+ delete RegExp.input;
+ }
+
+ foo();
+ foo();
+}
+test2();
Added: trunk/JSTests/stress/custom-value-delete-property-3.js (0 => 250540)
--- trunk/JSTests/stress/custom-value-delete-property-3.js (rev 0)
+++ trunk/JSTests/stress/custom-value-delete-property-3.js 2019-10-01 00:50:46 UTC (rev 250540)
@@ -0,0 +1,14 @@
+function test6() {
+ function foo() {
+ const o = {};
+ for (let i = 0; i < 8; i++) {
+ o.lastMatch;
+ o.__proto__ = RegExp;
+ }
+ delete RegExp.input;
+ }
+
+ foo();
+ foo();
+}
+test6();
Modified: trunk/LayoutTests/ChangeLog (250539 => 250540)
--- trunk/LayoutTests/ChangeLog 2019-10-01 00:47:04 UTC (rev 250539)
+++ trunk/LayoutTests/ChangeLog 2019-10-01 00:50:46 UTC (rev 250540)
@@ -1,3 +1,14 @@
+2019-09-30 Saam Barati <sbar...@apple.com>
+
+ Inline caching is wrong for custom accessors and custom values
+ https://bugs.webkit.org/show_bug.cgi?id=201994
+ <rdar://problem/50850326>
+
+ Reviewed by Yusuke Suzuki.
+
+ * js/dom/custom-accessor-redefine-expected.txt: Added.
+ * js/dom/custom-accessor-redefine.html: Added.
+
2019-09-30 Chris Dumez <cdu...@apple.com>
IDBTransaction / IDBObjectStore should not prevent a page from entering the back / forward cache
Added: trunk/LayoutTests/js/dom/custom-accessor-redefine-expected.txt (0 => 250540)
--- trunk/LayoutTests/js/dom/custom-accessor-redefine-expected.txt (rev 0)
+++ trunk/LayoutTests/js/dom/custom-accessor-redefine-expected.txt 2019-10-01 00:50:46 UTC (rev 250540)
@@ -0,0 +1,10 @@
+Check that custom accessor being redefined invalidates our inline caches.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS setter called as expected
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
Added: trunk/LayoutTests/js/dom/custom-accessor-redefine.html (0 => 250540)
--- trunk/LayoutTests/js/dom/custom-accessor-redefine.html (rev 0)
+++ trunk/LayoutTests/js/dom/custom-accessor-redefine.html 2019-10-01 00:50:46 UTC (rev 250540)
@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html>
+<head>
+<script src=""
+</head>
+<body>
+<script>
+description('Check that custom accessor being redefined invalidates our inline caches.');
+
+(function() {
+ "use strict";
+
+ let setterCalled = false;
+
+ function accessProperty() {
+ let oScript = document.createElement("script");
+ oScript.src = ""
+ }
+
+ // Force "code optimization" by calling the function several times
+ for (let i = 0; i < 1000; i++) {
+ accessProperty();
+ }
+
+ // Define a custom setter for HTMLScriptElement#src
+ const descriptor = Object.getOwnPropertyDescriptor(HTMLScriptElement.prototype, "src");
+ Object.defineProperty(HTMLScriptElement.prototype, "src", {
+ get: descriptor.get,
+ set: function() {
+
+ setterCalled = true;
+
+ descriptor.set.apply(this, arguments);
+ },
+ enumerable: descriptor.enumerable,
+ configurable: descriptor.configurable
+ });
+
+ accessProperty();
+
+ if (setterCalled)
+ testPassed("setter called as expected");
+ else
+ testFailed("Unexpected, setter not called.");
+})();
+</script>
+
+<script src=""
+</body>
+</html>
Modified: trunk/Source/_javascript_Core/ChangeLog (250539 => 250540)
--- trunk/Source/_javascript_Core/ChangeLog 2019-10-01 00:47:04 UTC (rev 250539)
+++ trunk/Source/_javascript_Core/ChangeLog 2019-10-01 00:50:46 UTC (rev 250540)
@@ -1,3 +1,98 @@
+2019-09-30 Saam Barati <sbar...@apple.com>
+
+ Inline caching is wrong for custom accessors and custom values
+ https://bugs.webkit.org/show_bug.cgi?id=201994
+ <rdar://problem/50850326>
+
+ Reviewed by Yusuke Suzuki.
+
+ There was an oversight in our inline caching code for custom accessors and
+ custom values. We used to assume that if an object O had a custom function for
+ property P, then O will forever respond to the same custom function for
+ property P.
+
+ This assumption was very wrong. These custom accessors/values might be
+ properties in JS which are configurable, so they can be rewritten to be
+ other properties. Our inline caching code would be wrong in the scenarios
+ where these property descriptors got redefined.
+
+ This patch makes it so that we now properly watchpoint for custom functions
+ being changed. If the custom accessor has been materialized, we place an
+ Equivalence watchpoint on the custom accessor. This patch also teaches
+ StructureStubInfo how to watchpoint on property value equivalence. Before,
+ we just watchpointed on structure transitions.
+
+ This patch also adds a new property condition kind for when the custom function
+ exists inside the static property table. This case is really easy to test for
+ because we just need to see if the structure still has static properties and
+ the static property table has the entry for a particular property. This
+ property condition kind just needs to watch for structure transitions because
+ an entry in the static property table can't be mutated.
+
+ This patch is neutral on the microbenchmarks I've added.
+
+ * bytecode/AccessCase.cpp:
+ (JSC::AccessCase::AccessCase):
+ (JSC::AccessCase::couldStillSucceed const):
+ (JSC::AccessCase::generateImpl):
+ * bytecode/AdaptiveInferredPropertyValueWatchpointBase.h:
+ * bytecode/ObjectPropertyCondition.cpp:
+ (JSC::ObjectPropertyCondition::structureEnsuresValidityAssumingImpurePropertyWatchpoint const):
+ * bytecode/ObjectPropertyCondition.h:
+ (JSC::ObjectPropertyCondition::customFunctionEquivalence):
+ * bytecode/ObjectPropertyConditionSet.cpp:
+ (JSC::ObjectPropertyConditionSet::hasOneSlotBaseCondition const):
+ (JSC::ObjectPropertyConditionSet::slotBaseCondition const):
+ (JSC::generateConditionsForPrototypePropertyHitCustom):
+ * bytecode/ObjectPropertyConditionSet.h:
+ * bytecode/PolyProtoAccessChain.cpp:
+ (JSC::PolyProtoAccessChain::create):
+ * bytecode/PolymorphicAccess.cpp:
+ (JSC::AccessGenerationState::installWatchpoint):
+ (JSC::PolymorphicAccess::commit):
+ (JSC::AccessGenerationState::addWatchpoint): Deleted.
+ * bytecode/PolymorphicAccess.h:
+ * bytecode/PropertyCondition.cpp:
+ (JSC::PropertyCondition::dumpInContext const):
+ (JSC::PropertyCondition::isStillValidAssumingImpurePropertyWatchpoint const):
+ (JSC::PropertyCondition::validityRequiresImpurePropertyWatchpoint const):
+ (JSC::PropertyCondition::isStillValid const):
+ (JSC::PropertyCondition::isWatchableWhenValid const):
+ (WTF::printInternal):
+ * bytecode/PropertyCondition.h:
+ (JSC::PropertyCondition::customFunctionEquivalence):
+ (JSC::PropertyCondition::hash const):
+ (JSC::PropertyCondition::operator== const):
+ * bytecode/StructureStubClearingWatchpoint.cpp:
+ (JSC::StructureTransitionStructureStubClearingWatchpoint::fireInternal):
+ (JSC::WatchpointsOnStructureStubInfo::addWatchpoint):
+ (JSC::WatchpointsOnStructureStubInfo::ensureReferenceAndInstallWatchpoint):
+ (JSC::WatchpointsOnStructureStubInfo::ensureReferenceAndAddWatchpoint):
+ (JSC::AdaptiveValueStructureStubClearingWatchpoint::handleFire):
+ (JSC::StructureStubClearingWatchpoint::fireInternal): Deleted.
+ * bytecode/StructureStubClearingWatchpoint.h:
+ * bytecode/Watchpoint.h:
+ * jit/Repatch.cpp:
+ (JSC::tryCacheGetByID):
+ (JSC::tryCachePutByID):
+ * runtime/ClassInfo.h:
+ * runtime/JSObject.cpp:
+ (JSC::JSObject::findPropertyHashEntry const):
+ * runtime/JSObject.h:
+ * runtime/ObjectPropertyChangeAdaptiveWatchpoint.h:
+ * runtime/Structure.cpp:
+ (JSC::Structure::findPropertyHashEntry const):
+ * runtime/Structure.h:
+ * tools/JSDollarVM.cpp:
+ (JSC::testStaticAccessorGetter):
+ (JSC::testStaticAccessorPutter):
+ (JSC::StaticCustomAccessor::StaticCustomAccessor):
+ (JSC::StaticCustomAccessor::createStructure):
+ (JSC::StaticCustomAccessor::create):
+ (JSC::StaticCustomAccessor::getOwnPropertySlot):
+ (JSC::functionCreateStaticCustomAccessor):
+ (JSC::JSDollarVM::finishCreation):
+
2019-09-30 Yusuke Suzuki <ysuz...@apple.com>
[JSC] AI folds CompareEq wrongly when it sees proven Boolean and Number
Modified: trunk/Source/_javascript_Core/bytecode/AccessCase.cpp (250539 => 250540)
--- trunk/Source/_javascript_Core/bytecode/AccessCase.cpp 2019-10-01 00:47:04 UTC (rev 250539)
+++ trunk/Source/_javascript_Core/bytecode/AccessCase.cpp 2019-10-01 00:50:46 UTC (rev 250540)
@@ -61,6 +61,7 @@
{
m_structure.setMayBeNull(vm, owner, structure);
m_conditionSet = conditionSet;
+ RELEASE_ASSERT(m_conditionSet.isValid());
}
std::unique_ptr<AccessCase> AccessCase::create(VM& vm, JSCell* owner, AccessType type, PropertyOffset offset, Structure* structure, const ObjectPropertyConditionSet& conditionSet, std::unique_ptr<PolyProtoAccessChain> prototypeAccessChain)
@@ -310,7 +311,16 @@
bool AccessCase::couldStillSucceed() const
{
- return m_conditionSet.structuresEnsureValidityAssumingImpurePropertyWatchpoint();
+ for (const ObjectPropertyCondition& condition : m_conditionSet) {
+ if (condition.condition().kind() == PropertyCondition::Equivalence) {
+ if (!condition.isWatchableAssumingImpurePropertyWatchpoint(PropertyCondition::WatchabilityEffort::EnsureWatchability))
+ return false;
+ } else {
+ if (!condition.structureEnsuresValidityAssumingImpurePropertyWatchpoint())
+ return false;
+ }
+ }
+ return true;
}
bool AccessCase::canReplace(const AccessCase& other) const
@@ -709,19 +719,18 @@
GPRReg thisGPR = state.thisGPR != InvalidGPRReg ? state.thisGPR : baseGPR;
GPRReg scratchGPR = state.scratchGPR;
- ASSERT(m_conditionSet.structuresEnsureValidityAssumingImpurePropertyWatchpoint());
-
for (const ObjectPropertyCondition& condition : m_conditionSet) {
RELEASE_ASSERT(!m_polyProtoAccessChain);
- Structure* structure = condition.object()->structure(vm);
-
- if (condition.isWatchableAssumingImpurePropertyWatchpoint()) {
- structure->addTransitionWatchpoint(state.addWatchpoint(condition));
+ if (condition.isWatchableAssumingImpurePropertyWatchpoint(PropertyCondition::WatchabilityEffort::EnsureWatchability)) {
+ state.installWatchpoint(condition);
continue;
}
- if (!condition.structureEnsuresValidityAssumingImpurePropertyWatchpoint(structure)) {
+ // For now, we only allow equivalence when it's watchable.
+ RELEASE_ASSERT(condition.condition().kind() != PropertyCondition::Equivalence);
+
+ if (!condition.structureEnsuresValidityAssumingImpurePropertyWatchpoint()) {
// The reason why this cannot happen is that we require that PolymorphicAccess calls
// AccessCase::generate() only after it has verified that
// AccessCase::couldStillSucceed() returned true.
@@ -731,6 +740,7 @@
}
// We will emit code that has a weak reference that isn't otherwise listed anywhere.
+ Structure* structure = condition.object()->structure(vm);
state.weakReferences.append(WriteBarrier<JSCell>(vm, codeBlock, structure));
jit.move(CCallHelpers::TrustedImmPtr(condition.object()), scratchGPR);
Modified: trunk/Source/_javascript_Core/bytecode/AdaptiveInferredPropertyValueWatchpointBase.h (250539 => 250540)
--- trunk/Source/_javascript_Core/bytecode/AdaptiveInferredPropertyValueWatchpointBase.h 2019-10-01 00:47:04 UTC (rev 250539)
+++ trunk/Source/_javascript_Core/bytecode/AdaptiveInferredPropertyValueWatchpointBase.h 2019-10-01 00:50:46 UTC (rev 250540)
@@ -32,6 +32,8 @@
namespace JSC {
+// FIXME: This isn't actually a Watchpoint. We should probably have a name which better reflects that:
+// https://bugs.webkit.org/show_bug.cgi?id=202381
class AdaptiveInferredPropertyValueWatchpointBase {
WTF_MAKE_NONCOPYABLE(AdaptiveInferredPropertyValueWatchpointBase);
WTF_MAKE_FAST_ALLOCATED;
Modified: trunk/Source/_javascript_Core/bytecode/ObjectPropertyCondition.cpp (250539 => 250540)
--- trunk/Source/_javascript_Core/bytecode/ObjectPropertyCondition.cpp 2019-10-01 00:47:04 UTC (rev 250539)
+++ trunk/Source/_javascript_Core/bytecode/ObjectPropertyCondition.cpp 2019-10-01 00:50:46 UTC (rev 250540)
@@ -46,18 +46,12 @@
dumpInContext(out, nullptr);
}
-bool ObjectPropertyCondition::structureEnsuresValidityAssumingImpurePropertyWatchpoint(
- Structure* structure) const
-{
- return m_condition.isStillValidAssumingImpurePropertyWatchpoint(structure);
-}
-
bool ObjectPropertyCondition::structureEnsuresValidityAssumingImpurePropertyWatchpoint() const
{
if (!*this)
return false;
- return structureEnsuresValidityAssumingImpurePropertyWatchpoint(m_object->structure());
+ return m_condition.isStillValidAssumingImpurePropertyWatchpoint(m_object->structure(), nullptr);
}
bool ObjectPropertyCondition::validityRequiresImpurePropertyWatchpoint(Structure* structure) const
Modified: trunk/Source/_javascript_Core/bytecode/ObjectPropertyCondition.h (250539 => 250540)
--- trunk/Source/_javascript_Core/bytecode/ObjectPropertyCondition.h 2019-10-01 00:47:04 UTC (rev 250539)
+++ trunk/Source/_javascript_Core/bytecode/ObjectPropertyCondition.h 2019-10-01 00:50:46 UTC (rev 250540)
@@ -121,6 +121,17 @@
vm.heap.writeBarrier(owner);
return equivalenceWithoutBarrier(object, uid, value);
}
+
+ static ObjectPropertyCondition customFunctionEquivalence(
+ VM& vm, JSCell* owner, JSObject* object, UniquedStringImpl* uid)
+ {
+ ObjectPropertyCondition result;
+ result.m_object = object;
+ result.m_condition = PropertyCondition::customFunctionEquivalence(uid);
+ if (owner)
+ vm.heap.writeBarrier(owner);
+ return result;
+ }
static ObjectPropertyCondition hasPrototypeWithoutBarrier(JSObject* object, JSObject* prototype)
{
@@ -193,7 +204,6 @@
// Checks if the object's structure claims that the property won't be intercepted. Validity
// does not require watchpoints on the object.
- bool structureEnsuresValidityAssumingImpurePropertyWatchpoint(Structure*) const;
bool structureEnsuresValidityAssumingImpurePropertyWatchpoint() const;
// Returns true if we need an impure property watchpoint to ensure validity even if
@@ -227,7 +237,7 @@
PropertyCondition::WatchabilityEffort = PropertyCondition::MakeNoChanges) const;
// This means that it's still valid and we could enforce validity by setting a transition
- // watchpoint on the structure.
+ // watchpoint on the structure, and a value change watchpoint if we're Equivalence.
bool isWatchable(
Structure*,
PropertyCondition::WatchabilityEffort = PropertyCondition::MakeNoChanges) const;
Modified: trunk/Source/_javascript_Core/bytecode/ObjectPropertyConditionSet.cpp (250539 => 250540)
--- trunk/Source/_javascript_Core/bytecode/ObjectPropertyConditionSet.cpp 2019-10-01 00:47:04 UTC (rev 250539)
+++ trunk/Source/_javascript_Core/bytecode/ObjectPropertyConditionSet.cpp 2019-10-01 00:50:46 UTC (rev 250540)
@@ -62,7 +62,22 @@
bool ObjectPropertyConditionSet::hasOneSlotBaseCondition() const
{
- return (numberOfConditionsWithKind(PropertyCondition::Presence) == 1) != (numberOfConditionsWithKind(PropertyCondition::Equivalence) == 1);
+ bool sawBase = false;
+ for (const ObjectPropertyCondition& condition : *this) {
+ switch (condition.kind()) {
+ case PropertyCondition::Presence:
+ case PropertyCondition::Equivalence:
+ case PropertyCondition::CustomFunctionEquivalence:
+ if (sawBase)
+ return false;
+ sawBase = true;
+ break;
+ default:
+ break;
+ }
+ }
+
+ return sawBase;
}
ObjectPropertyCondition ObjectPropertyConditionSet::slotBaseCondition() const
@@ -71,7 +86,8 @@
unsigned numFound = 0;
for (const ObjectPropertyCondition& condition : *this) {
if (condition.kind() == PropertyCondition::Presence
- || condition.kind() == PropertyCondition::Equivalence) {
+ || condition.kind() == PropertyCondition::Equivalence
+ || condition.kind() == PropertyCondition::CustomFunctionEquivalence) {
result = condition;
numFound++;
}
@@ -228,6 +244,13 @@
result = ObjectPropertyCondition::equivalence(vm, owner, object, uid, value);
break;
}
+ case PropertyCondition::CustomFunctionEquivalence: {
+ auto entry = object->findPropertyHashEntry(vm, uid);
+ if (!entry)
+ return ObjectPropertyCondition();
+ result = ObjectPropertyCondition::customFunctionEquivalence(vm, owner, object, uid);
+ break;
+ }
default:
RELEASE_ASSERT_NOT_REACHED();
return ObjectPropertyCondition();
@@ -378,15 +401,39 @@
ObjectPropertyConditionSet generateConditionsForPrototypePropertyHitCustom(
VM& vm, JSCell* owner, ExecState* exec, Structure* headStructure, JSObject* prototype,
- UniquedStringImpl* uid)
+ UniquedStringImpl* uid, unsigned attributes)
{
return generateConditions(
vm, exec->lexicalGlobalObject(), headStructure, prototype,
[&] (Vector<ObjectPropertyCondition>& conditions, JSObject* object) -> bool {
- if (object == prototype)
- return true;
- ObjectPropertyCondition result =
- generateCondition(vm, owner, object, uid, PropertyCondition::Absence);
+ auto kind = PropertyCondition::Absence;
+ if (object == prototype) {
+ Structure* structure = object->structure(vm);
+ PropertyOffset offset = structure->get(vm, uid);
+ if (isValidOffset(offset)) {
+ // When we reify custom accessors, we wrap them in a JSFunction that we shove
+ // inside a GetterSetter. So, once we've reified a custom accessor, we will
+ // no longer see it as a "custom" accessor/value. Hence, if our property access actually
+ // notices a custom, it must be a CustomGetterSetterType cell or something
+ // in the static property table. Custom values get reified into CustomGetterSetters.
+ JSValue value = object->getDirect(offset);
+ ASSERT_UNUSED(value, value.isCell() && value.asCell()->type() == CustomGetterSetterType);
+ kind = PropertyCondition::Equivalence;
+ } else if (structure->findPropertyHashEntry(uid))
+ kind = PropertyCondition::CustomFunctionEquivalence;
+ else if (attributes & PropertyAttribute::DontDelete) {
+ // This can't change, so we can blindly cache it.
+ return true;
+ } else {
+ // This means we materialized a custom out of thin air and it's not DontDelete (i.e, it can be
+ // redefined). This is curious. We don't actually need to crash here. We could blindly cache
+ // the function. Or we could blindly not cache it. However, we don't actually do this in WebKit
+ // right now, so it's reasonable to decide what to do later (or to warn people of forgetting DoneDelete.)
+ ASSERT_NOT_REACHED();
+ return false;
+ }
+ }
+ ObjectPropertyCondition result = generateCondition(vm, owner, object, uid, kind);
if (!result)
return false;
conditions.append(result);
Modified: trunk/Source/_javascript_Core/bytecode/ObjectPropertyConditionSet.h (250539 => 250540)
--- trunk/Source/_javascript_Core/bytecode/ObjectPropertyConditionSet.h 2019-10-01 00:47:04 UTC (rev 250539)
+++ trunk/Source/_javascript_Core/bytecode/ObjectPropertyConditionSet.h 2019-10-01 00:50:46 UTC (rev 250540)
@@ -176,7 +176,7 @@
UniquedStringImpl* uid);
ObjectPropertyConditionSet generateConditionsForPrototypePropertyHitCustom(
VM&, JSCell* owner, ExecState*, Structure* headStructure, JSObject* prototype,
- UniquedStringImpl* uid);
+ UniquedStringImpl* uid, unsigned attributes);
ObjectPropertyConditionSet generateConditionsForInstanceOf(
VM&, JSCell* owner, ExecState*, Structure* headStructure, JSObject* prototype, bool shouldHit);
Modified: trunk/Source/_javascript_Core/bytecode/PolyProtoAccessChain.cpp (250539 => 250540)
--- trunk/Source/_javascript_Core/bytecode/PolyProtoAccessChain.cpp 2019-10-01 00:47:04 UTC (rev 250539)
+++ trunk/Source/_javascript_Core/bytecode/PolyProtoAccessChain.cpp 2019-10-01 00:50:46 UTC (rev 250540)
@@ -51,12 +51,6 @@
for (unsigned iterationNumber = 0; true; ++iterationNumber) {
Structure* structure = current->structure(vm);
- if (!structure->propertyAccessesAreCacheable())
- return nullptr;
-
- if (structure->isProxy())
- return nullptr;
-
if (structure->isDictionary()) {
ASSERT(structure->isObject());
if (structure->hasBeenFlattenedBefore())
@@ -65,6 +59,12 @@
structure->flattenDictionaryStructure(vm, asObject(current));
}
+ if (!structure->propertyAccessesAreCacheable())
+ return nullptr;
+
+ if (structure->isProxy())
+ return nullptr;
+
// To save memory, we don't include the base in the chain. We let
// AccessCase provide the base to us as needed.
if (iterationNumber)
Modified: trunk/Source/_javascript_Core/bytecode/PolymorphicAccess.cpp (250539 => 250540)
--- trunk/Source/_javascript_Core/bytecode/PolymorphicAccess.cpp 2019-10-01 00:47:04 UTC (rev 250539)
+++ trunk/Source/_javascript_Core/bytecode/PolymorphicAccess.cpp 2019-10-01 00:50:46 UTC (rev 250540)
@@ -55,9 +55,9 @@
out.print(":", m_code);
}
-Watchpoint* AccessGenerationState::addWatchpoint(const ObjectPropertyCondition& condition)
+void AccessGenerationState::installWatchpoint(const ObjectPropertyCondition& condition)
{
- return WatchpointsOnStructureStubInfo::ensureReferenceAndAddWatchpoint(
+ WatchpointsOnStructureStubInfo::ensureReferenceAndInstallWatchpoint(
watchpoints, jit->codeBlock(), stubInfo, condition);
}
@@ -373,7 +373,7 @@
for (WatchpointSet* set : accessCase.commit(vm, ident)) {
Watchpoint* watchpoint =
WatchpointsOnStructureStubInfo::ensureReferenceAndAddWatchpoint(
- watchpoints, codeBlock, &stubInfo, ObjectPropertyCondition());
+ watchpoints, codeBlock, &stubInfo);
set->add(watchpoint);
}
Modified: trunk/Source/_javascript_Core/bytecode/PolymorphicAccess.h (250539 => 250540)
--- trunk/Source/_javascript_Core/bytecode/PolymorphicAccess.h 2019-10-01 00:47:04 UTC (rev 250539)
+++ trunk/Source/_javascript_Core/bytecode/PolymorphicAccess.h 2019-10-01 00:50:46 UTC (rev 250540)
@@ -212,7 +212,7 @@
std::unique_ptr<WatchpointsOnStructureStubInfo> watchpoints;
Vector<WriteBarrier<JSCell>> weakReferences;
- Watchpoint* addWatchpoint(const ObjectPropertyCondition& = ObjectPropertyCondition());
+ void installWatchpoint(const ObjectPropertyCondition&);
void restoreScratch();
void succeed();
Modified: trunk/Source/_javascript_Core/bytecode/PropertyCondition.cpp (250539 => 250540)
--- trunk/Source/_javascript_Core/bytecode/PropertyCondition.cpp 2019-10-01 00:47:04 UTC (rev 250539)
+++ trunk/Source/_javascript_Core/bytecode/PropertyCondition.cpp 2019-10-01 00:50:46 UTC (rev 250540)
@@ -54,6 +54,9 @@
case Equivalence:
out.print(m_header.type(), " of ", m_header.pointer(), " with ", inContext(requiredValue(), context));
return;
+ case CustomFunctionEquivalence:
+ out.print(m_header.type(), " of ", m_header.pointer());
+ return;
case HasPrototype:
out.print(m_header.type(), " with prototype ", inContext(JSValue(prototype()), context));
return;
@@ -86,6 +89,7 @@
case Absence:
case AbsenceOfSetEffect:
case Equivalence:
+ case CustomFunctionEquivalence:
if (!structure->propertyAccessesAreCacheable()) {
if (PropertyConditionInternal::verbose)
dataLog("Invalid because property accesses are not cacheable.\n");
@@ -248,7 +252,13 @@
}
return true;
- } }
+ }
+ case CustomFunctionEquivalence: {
+ if (structure->staticPropertiesReified())
+ return false;
+ return !!structure->findPropertyHashEntry(uid());
+ }
+ }
RELEASE_ASSERT_NOT_REACHED();
return false;
@@ -263,6 +273,7 @@
case Presence:
case Absence:
case Equivalence:
+ case CustomFunctionEquivalence:
return structure->needImpurePropertyWatchpoint();
case AbsenceOfSetEffect:
case HasPrototype:
@@ -288,6 +299,7 @@
break;
case Presence:
case Equivalence:
+ case CustomFunctionEquivalence:
if (structure->typeInfo().getOwnPropertySlotIsImpure())
return false;
break;
@@ -329,6 +341,21 @@
break;
}
+
+ case CustomFunctionEquivalence: {
+ // We just use the structure transition watchpoint for this. A structure S starts
+ // off with a property P in the static property hash table. If S transitions to
+ // S', either P remains in the static property table or not. If not, then we
+ // are no longer valid. So the above check of transitionWatchpointSetHasBeenInvalidated
+ // is sufficient.
+ //
+ // We could make this smarter in the future, since we sometimes reify static properties.
+ // We could make this adapt to looking at the object's storage for such reified custom
+ // functions, but we don't do that right now. We just allow this property condition to
+ // invalidate and create an Equivalence watchpoint for the materialized property sometime
+ // in the future.
+ break;
+ }
default:
break;
@@ -403,6 +430,9 @@
case JSC::PropertyCondition::Equivalence:
out.print("Equivalence");
return;
+ case JSC::PropertyCondition::CustomFunctionEquivalence:
+ out.print("CustomFunctionEquivalence");
+ return;
case JSC::PropertyCondition::HasPrototype:
out.print("HasPrototype");
return;
Modified: trunk/Source/_javascript_Core/bytecode/PropertyCondition.h (250539 => 250540)
--- trunk/Source/_javascript_Core/bytecode/PropertyCondition.h 2019-10-01 00:47:04 UTC (rev 250539)
+++ trunk/Source/_javascript_Core/bytecode/PropertyCondition.h 2019-10-01 00:50:46 UTC (rev 250540)
@@ -40,6 +40,7 @@
Absence,
AbsenceOfSetEffect,
Equivalence, // An adaptive watchpoint on this will be a pair of watchpoints, and when the structure transitions, we will set the replacement watchpoint on the new structure.
+ CustomFunctionEquivalence, // Custom value or accessor.
HasPrototype
};
@@ -122,6 +123,13 @@
vm.heap.writeBarrier(owner);
return equivalenceWithoutBarrier(uid, value);
}
+
+ static PropertyCondition customFunctionEquivalence(UniquedStringImpl* uid)
+ {
+ PropertyCondition result;
+ result.m_header = Header(uid, CustomFunctionEquivalence);
+ return result;
+ }
static PropertyCondition hasPrototypeWithoutBarrier(JSObject* prototype)
{
@@ -193,6 +201,8 @@
case Equivalence:
result ^= EncodedJSValueHash::hash(u.equivalence.value);
break;
+ case CustomFunctionEquivalence:
+ break;
}
return result;
}
@@ -213,6 +223,8 @@
return u.prototype.prototype == other.u.prototype.prototype;
case Equivalence:
return u.equivalence.value == other.u.equivalence.value;
+ case CustomFunctionEquivalence:
+ return true;
}
RELEASE_ASSERT_NOT_REACHED();
return false;
@@ -279,12 +291,12 @@
// This means that it's still valid and we could enforce validity by setting a transition
// watchpoint on the structure and possibly an impure property watchpoint.
bool isWatchableAssumingImpurePropertyWatchpoint(
- Structure*, JSObject* base = nullptr, WatchabilityEffort = MakeNoChanges) const;
+ Structure*, JSObject* base, WatchabilityEffort = MakeNoChanges) const;
// This means that it's still valid and we could enforce validity by setting a transition
// watchpoint on the structure.
bool isWatchable(
- Structure*, JSObject* base = nullptr, WatchabilityEffort = MakeNoChanges) const;
+ Structure*, JSObject*, WatchabilityEffort = MakeNoChanges) const;
bool watchingRequiresStructureTransitionWatchpoint() const
{
Modified: trunk/Source/_javascript_Core/bytecode/StructureStubClearingWatchpoint.cpp (250539 => 250540)
--- trunk/Source/_javascript_Core/bytecode/StructureStubClearingWatchpoint.cpp 2019-10-01 00:47:04 UTC (rev 250539)
+++ trunk/Source/_javascript_Core/bytecode/StructureStubClearingWatchpoint.cpp 2019-10-01 00:50:46 UTC (rev 250540)
@@ -34,7 +34,7 @@
namespace JSC {
-void StructureStubClearingWatchpoint::fireInternal(VM& vm, const FireDetail&)
+void StructureTransitionStructureStubClearingWatchpoint::fireInternal(VM& vm, const FireDetail&)
{
if (!m_holder->isValid())
return;
@@ -62,12 +62,15 @@
return m_codeBlock->isLive();
}
-StructureStubClearingWatchpoint* WatchpointsOnStructureStubInfo::addWatchpoint(const ObjectPropertyCondition& key)
+WatchpointsOnStructureStubInfo::Node& WatchpointsOnStructureStubInfo::addWatchpoint(const ObjectPropertyCondition& key)
{
- return m_watchpoints.add(key, *this);
+ if (!key || key.condition().kind() != PropertyCondition::Equivalence)
+ return *m_watchpoints.add(WTF::in_place<StructureTransitionStructureStubClearingWatchpoint>, key, *this);
+ ASSERT(key.condition().kind() == PropertyCondition::Equivalence);
+ return *m_watchpoints.add(WTF::in_place<AdaptiveValueStructureStubClearingWatchpoint>, key, *this);
}
-StructureStubClearingWatchpoint* WatchpointsOnStructureStubInfo::ensureReferenceAndAddWatchpoint(
+void WatchpointsOnStructureStubInfo::ensureReferenceAndInstallWatchpoint(
std::unique_ptr<WatchpointsOnStructureStubInfo>& holderRef, CodeBlock* codeBlock,
StructureStubInfo* stubInfo, const ObjectPropertyCondition& key)
{
@@ -78,9 +81,43 @@
ASSERT(holderRef->m_stubInfo == stubInfo);
}
- return holderRef->addWatchpoint(key);
+ ASSERT(!!key);
+ auto& watchpointVariant = holderRef->addWatchpoint(key);
+ if (key.kind() == PropertyCondition::Equivalence) {
+ auto& adaptiveWatchpoint = WTF::get<AdaptiveValueStructureStubClearingWatchpoint>(watchpointVariant);
+ adaptiveWatchpoint.install(codeBlock->vm());
+ } else {
+ auto* structureTransitionWatchpoint = &WTF::get<StructureTransitionStructureStubClearingWatchpoint>(watchpointVariant);
+ key.object()->structure()->addTransitionWatchpoint(structureTransitionWatchpoint);
+ }
}
+Watchpoint* WatchpointsOnStructureStubInfo::ensureReferenceAndAddWatchpoint(
+ std::unique_ptr<WatchpointsOnStructureStubInfo>& holderRef, CodeBlock* codeBlock,
+ StructureStubInfo* stubInfo)
+{
+ if (!holderRef)
+ holderRef = makeUnique<WatchpointsOnStructureStubInfo>(codeBlock, stubInfo);
+ else {
+ ASSERT(holderRef->m_codeBlock == codeBlock);
+ ASSERT(holderRef->m_stubInfo == stubInfo);
+ }
+
+ return &WTF::get<StructureTransitionStructureStubClearingWatchpoint>(holderRef->addWatchpoint(ObjectPropertyCondition()));
+}
+
+void AdaptiveValueStructureStubClearingWatchpoint::handleFire(VM&, const FireDetail&)
+{
+ if (!m_holder->isValid())
+ return;
+
+ // This will implicitly cause my own demise: stub reset removes all watchpoints.
+ // That works, because deleting a watchpoint removes it from the set's list, and
+ // the set's list traversal for firing is robust against the set changing.
+ ConcurrentJSLocker locker(m_holder->codeBlock()->m_lock);
+ m_holder->stubInfo()->reset(m_holder->codeBlock());
+}
+
} // namespace JSC
#endif // ENABLE(JIT)
Modified: trunk/Source/_javascript_Core/bytecode/StructureStubClearingWatchpoint.h (250539 => 250540)
--- trunk/Source/_javascript_Core/bytecode/StructureStubClearingWatchpoint.h 2019-10-01 00:47:04 UTC (rev 250539)
+++ trunk/Source/_javascript_Core/bytecode/StructureStubClearingWatchpoint.h 2019-10-01 00:50:46 UTC (rev 250540)
@@ -25,11 +25,11 @@
#pragma once
+#if ENABLE(JIT)
+
+#include "AdaptiveInferredPropertyValueWatchpointBase.h"
#include "ObjectPropertyCondition.h"
#include "Watchpoint.h"
-
-#if ENABLE(JIT)
-
#include <wtf/Bag.h>
#include <wtf/FastMalloc.h>
#include <wtf/Noncopyable.h>
@@ -40,12 +40,12 @@
class StructureStubInfo;
class WatchpointsOnStructureStubInfo;
-class StructureStubClearingWatchpoint final : public Watchpoint {
- WTF_MAKE_NONCOPYABLE(StructureStubClearingWatchpoint);
+class StructureTransitionStructureStubClearingWatchpoint final : public Watchpoint {
+ WTF_MAKE_NONCOPYABLE(StructureTransitionStructureStubClearingWatchpoint);
WTF_MAKE_FAST_ALLOCATED;
public:
- StructureStubClearingWatchpoint(const ObjectPropertyCondition& key, WatchpointsOnStructureStubInfo& holder)
- : Watchpoint(Watchpoint::Type::StructureStubClearing)
+ StructureTransitionStructureStubClearingWatchpoint(const ObjectPropertyCondition& key, WatchpointsOnStructureStubInfo& holder)
+ : Watchpoint(Watchpoint::Type::StructureTransitionStructureStubClearing)
, m_holder(&holder)
, m_key(key)
{
@@ -59,6 +59,26 @@
JSC_WATCHPOINT_FIELD(ObjectPropertyCondition, m_key);
};
+class AdaptiveValueStructureStubClearingWatchpoint final : public AdaptiveInferredPropertyValueWatchpointBase {
+ using Base = AdaptiveInferredPropertyValueWatchpointBase;
+ WTF_MAKE_NONCOPYABLE(AdaptiveValueStructureStubClearingWatchpoint);
+ WTF_MAKE_FAST_ALLOCATED;
+
+ void handleFire(VM&, const FireDetail&) override;
+
+public:
+ AdaptiveValueStructureStubClearingWatchpoint(const ObjectPropertyCondition& key, WatchpointsOnStructureStubInfo& holder)
+ : Base(key)
+ , m_holder(&holder)
+ {
+ RELEASE_ASSERT(key.condition().kind() == PropertyCondition::Equivalence);
+ }
+
+
+private:
+ PackedPtr<WatchpointsOnStructureStubInfo> m_holder;
+};
+
class WatchpointsOnStructureStubInfo {
WTF_MAKE_NONCOPYABLE(WatchpointsOnStructureStubInfo);
WTF_MAKE_FAST_ALLOCATED;
@@ -69,11 +89,16 @@
{
}
- StructureStubClearingWatchpoint* addWatchpoint(const ObjectPropertyCondition& key);
+ using Node = Variant<StructureTransitionStructureStubClearingWatchpoint, AdaptiveValueStructureStubClearingWatchpoint>;
+
+ Node& addWatchpoint(const ObjectPropertyCondition& key);
- static StructureStubClearingWatchpoint* ensureReferenceAndAddWatchpoint(
+ static void ensureReferenceAndInstallWatchpoint(
std::unique_ptr<WatchpointsOnStructureStubInfo>& holderRef,
CodeBlock*, StructureStubInfo*, const ObjectPropertyCondition& key);
+ static Watchpoint* ensureReferenceAndAddWatchpoint(
+ std::unique_ptr<WatchpointsOnStructureStubInfo>& holderRef,
+ CodeBlock*, StructureStubInfo*);
CodeBlock* codeBlock() const { return m_codeBlock; }
StructureStubInfo* stubInfo() const { return m_stubInfo; }
@@ -83,7 +108,9 @@
private:
CodeBlock* m_codeBlock;
StructureStubInfo* m_stubInfo;
- Bag<StructureStubClearingWatchpoint> m_watchpoints;
+ // FIXME: use less memory for the entries in this Bag:
+ // https://bugs.webkit.org/show_bug.cgi?id=202380
+ Bag<WTF::Variant<StructureTransitionStructureStubClearingWatchpoint, AdaptiveValueStructureStubClearingWatchpoint>> m_watchpoints;
};
} // namespace JSC
Modified: trunk/Source/_javascript_Core/bytecode/Watchpoint.h (250539 => 250540)
--- trunk/Source/_javascript_Core/bytecode/Watchpoint.h 2019-10-01 00:47:04 UTC (rev 250539)
+++ trunk/Source/_javascript_Core/bytecode/Watchpoint.h 2019-10-01 00:50:46 UTC (rev 250540)
@@ -115,7 +115,7 @@
#if ENABLE(JIT)
#define JSC_WATCHPOINT_TYPES_WITHOUT_DFG(macro) \
JSC_WATCHPOINT_TYPES_WITHOUT_JIT(macro) \
- macro(StructureStubClearing, StructureStubClearingWatchpoint)
+ macro(StructureTransitionStructureStubClearing, StructureTransitionStructureStubClearingWatchpoint)
#if ENABLE(DFG_JIT)
#define JSC_WATCHPOINT_TYPES(macro) \
Modified: trunk/Source/_javascript_Core/jit/Repatch.cpp (250539 => 250540)
--- trunk/Source/_javascript_Core/jit/Repatch.cpp 2019-10-01 00:47:04 UTC (rev 250539)
+++ trunk/Source/_javascript_Core/jit/Repatch.cpp 2019-10-01 00:50:46 UTC (rev 250540)
@@ -321,7 +321,7 @@
} else {
conditionSet = generateConditionsForPrototypePropertyHitCustom(
vm, codeBlock, exec, structure, slot.slotBase(),
- propertyName.impl());
+ propertyName.impl(), slot.attributes());
}
if (!conditionSet.isValid())
@@ -549,7 +549,7 @@
prototypeAccessChain = nullptr;
conditionSet =
generateConditionsForPrototypePropertyHitCustom(
- vm, codeBlock, exec, structure, slot.base(), ident.impl());
+ vm, codeBlock, exec, structure, slot.base(), ident.impl(), static_cast<unsigned>(PropertyAttribute::None));
if (!conditionSet.isValid())
return GiveUpOnCache;
}
Modified: trunk/Source/_javascript_Core/runtime/ClassInfo.h (250539 => 250540)
--- trunk/Source/_javascript_Core/runtime/ClassInfo.h 2019-10-01 00:47:04 UTC (rev 250539)
+++ trunk/Source/_javascript_Core/runtime/ClassInfo.h 2019-10-01 00:50:46 UTC (rev 250540)
@@ -189,12 +189,18 @@
sizeof(ClassName)
struct ClassInfo {
+ using CheckSubClassSnippetFunctionPtr = Ref<Snippet> (*)(void);
+
// A string denoting the class name. Example: "Window".
const char* className;
-
// Pointer to the class information of the base class.
// nullptrif there is none.
const ClassInfo* parentClass;
+ const HashTable* staticPropHashTable;
+ CheckSubClassSnippetFunctionPtr checkSubClassSnippet;
+ MethodTable methodTable;
+ TypedArrayType typedArrayStorageType;
+ unsigned staticClassSize;
static ptrdiff_t offsetOfParentClass()
{
@@ -213,16 +219,6 @@
JS_EXPORT_PRIVATE void dump(PrintStream&) const;
JS_EXPORT_PRIVATE bool hasStaticSetterOrReadonlyProperties() const;
-
- const HashTable* staticPropHashTable;
-
- using CheckSubClassSnippetFunctionPtr = Ref<Snippet> (*)(void);
- CheckSubClassSnippetFunctionPtr checkSubClassSnippet;
-
- MethodTable methodTable;
-
- TypedArrayType typedArrayStorageType;
- unsigned staticClassSize;
};
} // namespace JSC
Modified: trunk/Source/_javascript_Core/runtime/JSObject.cpp (250539 => 250540)
--- trunk/Source/_javascript_Core/runtime/JSObject.cpp 2019-10-01 00:47:04 UTC (rev 250539)
+++ trunk/Source/_javascript_Core/runtime/JSObject.cpp 2019-10-01 00:50:46 UTC (rev 250540)
@@ -2222,15 +2222,9 @@
return false;
}
-auto JSObject::findPropertyHashEntry(VM& vm, PropertyName propertyName) const -> Optional<PropertyHashEntry>
+Optional<Structure::PropertyHashEntry> JSObject::findPropertyHashEntry(VM& vm, PropertyName propertyName) const
{
- for (const ClassInfo* info = classInfo(vm); info; info = info->parentClass) {
- if (const HashTable* propHashTable = info->staticPropHashTable) {
- if (const HashTableValue* entry = propHashTable->entry(propertyName))
- return PropertyHashEntry { propHashTable, entry };
- }
- }
- return WTF::nullopt;
+ return structure(vm)->findPropertyHashEntry(propertyName);
}
bool JSObject::hasInstance(ExecState* exec, JSValue value, JSValue hasInstanceValue)
Modified: trunk/Source/_javascript_Core/runtime/JSObject.h (250539 => 250540)
--- trunk/Source/_javascript_Core/runtime/JSObject.h 2019-10-01 00:47:04 UTC (rev 250539)
+++ trunk/Source/_javascript_Core/runtime/JSObject.h 2019-10-01 00:50:46 UTC (rev 250540)
@@ -903,6 +903,8 @@
bool mayBePrototype() const;
void didBecomePrototype();
+ Optional<Structure::PropertyHashEntry> findPropertyHashEntry(VM&, PropertyName) const;
+
DECLARE_EXPORT_INFO;
protected:
@@ -1032,7 +1034,7 @@
// This is relevant to undecided, int32, double, and contiguous.
unsigned countElements();
-
+
private:
friend class LLIntOffsetsExtractor;
friend class VMInspector;
@@ -1060,11 +1062,6 @@
void fillCustomGetterPropertySlot(VM&, PropertySlot&, CustomGetterSetter*, unsigned, Structure*);
JS_EXPORT_PRIVATE bool getOwnStaticPropertySlot(VM&, PropertyName, PropertySlot&);
- struct PropertyHashEntry {
- const HashTable* table;
- const HashTableValue* value;
- };
- Optional<PropertyHashEntry> findPropertyHashEntry(VM&, PropertyName) const;
bool putByIndexBeyondVectorLength(ExecState*, unsigned propertyName, JSValue, bool shouldThrow);
bool putDirectIndexBeyondVectorLengthWithArrayStorage(ExecState*, unsigned propertyName, JSValue, unsigned attributes, PutDirectIndexMode, ArrayStorage*);
Modified: trunk/Source/_javascript_Core/runtime/ObjectPropertyChangeAdaptiveWatchpoint.h (250539 => 250540)
--- trunk/Source/_javascript_Core/runtime/ObjectPropertyChangeAdaptiveWatchpoint.h 2019-10-01 00:47:04 UTC (rev 250539)
+++ trunk/Source/_javascript_Core/runtime/ObjectPropertyChangeAdaptiveWatchpoint.h 2019-10-01 00:50:46 UTC (rev 250540)
@@ -29,16 +29,16 @@
namespace JSC {
-template<typename Watchpoint>
+template<typename WatchpointSet>
class ObjectPropertyChangeAdaptiveWatchpoint final : public AdaptiveInferredPropertyValueWatchpointBase {
public:
using Base = AdaptiveInferredPropertyValueWatchpointBase;
- ObjectPropertyChangeAdaptiveWatchpoint(JSCell* owner, const ObjectPropertyCondition& condition, Watchpoint& watchpoint)
+ ObjectPropertyChangeAdaptiveWatchpoint(JSCell* owner, const ObjectPropertyCondition& condition, WatchpointSet& watchpointSet)
: Base(condition)
, m_owner(owner)
- , m_watchpoint(watchpoint)
+ , m_watchpointSet(watchpointSet)
{
- RELEASE_ASSERT(watchpoint.stateOnJSThread() == IsWatched);
+ RELEASE_ASSERT(watchpointSet.stateOnJSThread() == IsWatched);
}
private:
@@ -49,11 +49,11 @@
void handleFire(VM& vm, const FireDetail&) override
{
- m_watchpoint.fireAll(vm, StringFireDetail("Object Property is changed."));
+ m_watchpointSet.fireAll(vm, StringFireDetail("Object Property is changed."));
}
JSCell* m_owner;
- Watchpoint& m_watchpoint;
+ WatchpointSet& m_watchpointSet;
};
} // namespace JSC
Modified: trunk/Source/_javascript_Core/runtime/Structure.cpp (250539 => 250540)
--- trunk/Source/_javascript_Core/runtime/Structure.cpp 2019-10-01 00:47:04 UTC (rev 250539)
+++ trunk/Source/_javascript_Core/runtime/Structure.cpp 2019-10-01 00:50:46 UTC (rev 250540)
@@ -1238,4 +1238,15 @@
return true;
}
+auto Structure::findPropertyHashEntry(PropertyName propertyName) const -> Optional<PropertyHashEntry>
+{
+ for (const ClassInfo* info = classInfo(); info; info = info->parentClass) {
+ if (const HashTable* propHashTable = info->staticPropHashTable) {
+ if (const HashTableValue* entry = propHashTable->entry(propertyName))
+ return PropertyHashEntry { propHashTable, entry };
+ }
+ }
+ return WTF::nullopt;
+}
+
} // namespace JSC
Modified: trunk/Source/_javascript_Core/runtime/Structure.h (250539 => 250540)
--- trunk/Source/_javascript_Core/runtime/Structure.h 2019-10-01 00:47:04 UTC (rev 250539)
+++ trunk/Source/_javascript_Core/runtime/Structure.h 2019-10-01 00:50:46 UTC (rev 250540)
@@ -61,6 +61,8 @@
class SlotVisitor;
class JSString;
struct DumpContext;
+struct HashTable;
+struct HashTableValue;
// The out-of-line property storage capacity to use when first allocating out-of-line
// storage. Note that all objects start out without having any out-of-line storage;
@@ -615,6 +617,12 @@
unsigned propertyHash() const { return m_propertyHash; }
static bool shouldConvertToPolyProto(const Structure* a, const Structure* b);
+
+ struct PropertyHashEntry {
+ const HashTable* table;
+ const HashTableValue* value;
+ };
+ Optional<PropertyHashEntry> findPropertyHashEntry(PropertyName) const;
DECLARE_EXPORT_INFO;
Modified: trunk/Source/_javascript_Core/tools/JSDollarVM.cpp (250539 => 250540)
--- trunk/Source/_javascript_Core/tools/JSDollarVM.cpp 2019-10-01 00:47:04 UTC (rev 250539)
+++ trunk/Source/_javascript_Core/tools/JSDollarVM.cpp 2019-10-01 00:50:46 UTC (rev 250540)
@@ -586,6 +586,79 @@
Vector<int> m_vector;
};
+static const struct CompactHashIndex staticCustomAccessorTableIndex[2] = {
+ { 0, -1 },
+ { -1, -1 },
+};
+
+static EncodedJSValue testStaticAccessorGetter(ExecState* exec, EncodedJSValue thisValue, PropertyName)
+{
+ DollarVMAssertScope assertScope;
+ VM& vm = exec->vm();
+
+ JSObject* thisObject = jsDynamicCast<JSObject*>(vm, JSValue::decode(thisValue));
+ RELEASE_ASSERT(thisObject);
+
+ if (JSValue result = thisObject->getDirect(vm, PropertyName(Identifier::fromString(vm, "testField"))))
+ return JSValue::encode(result);
+ return JSValue::encode(jsUndefined());
+}
+
+static bool testStaticAccessorPutter(ExecState* exec, EncodedJSValue thisValue, EncodedJSValue value)
+{
+ DollarVMAssertScope assertScope;
+ VM& vm = exec->vm();
+
+ JSObject* thisObject = jsDynamicCast<JSObject*>(vm, JSValue::decode(thisValue));
+ RELEASE_ASSERT(thisObject);
+
+ return thisObject->putDirect(vm, PropertyName(Identifier::fromString(vm, "testField")), JSValue::decode(value));
+}
+
+static const struct HashTableValue staticCustomAccessorTableValues[1] = {
+ { "testStaticAccessor", static_cast<unsigned>(PropertyAttribute::CustomAccessor), NoIntrinsic, { (intptr_t)static_cast<PropertySlot::GetValueFunc>(testStaticAccessorGetter), (intptr_t)static_cast<PutPropertySlot::PutValueFunc>(testStaticAccessorPutter) } },
+};
+
+static const struct HashTable staticCustomAccessorTable =
+ { 1, 1, true, nullptr, staticCustomAccessorTableValues, staticCustomAccessorTableIndex };
+
+class StaticCustomAccessor : public JSNonFinalObject {
+ using Base = JSNonFinalObject;
+public:
+ StaticCustomAccessor(VM& vm, Structure* structure)
+ : Base(vm, structure)
+ {
+ DollarVMAssertScope assertScope;
+ }
+
+ DECLARE_INFO;
+
+ static constexpr unsigned StructureFlags = Base::StructureFlags | HasStaticPropertyTable | OverridesGetOwnPropertySlot;
+
+ static Structure* createStructure(VM& vm, JSGlobalObject* globalObject, JSValue prototype)
+ {
+ DollarVMAssertScope assertScope;
+ return Structure::create(vm, globalObject, prototype, TypeInfo(ObjectType, StructureFlags), info());
+ }
+
+ static StaticCustomAccessor* create(VM& vm, Structure* structure)
+ {
+ DollarVMAssertScope assertScope;
+ StaticCustomAccessor* accessor = new (NotNull, allocateCell<StaticCustomAccessor>(vm.heap)) StaticCustomAccessor(vm, structure);
+ accessor->finishCreation(vm);
+ return accessor;
+ }
+
+ static bool getOwnPropertySlot(JSObject* thisObject, ExecState* exec, PropertyName propertyName, PropertySlot& slot)
+ {
+ if (String(propertyName.uid()) == "thinAirCustomGetter") {
+ slot.setCacheableCustom(thisObject, PropertyAttribute::DontDelete | PropertyAttribute::ReadOnly | PropertyAttribute::DontEnum | PropertyAttribute::CustomAccessor, testStaticAccessorGetter);
+ return true;
+ }
+ return JSNonFinalObject::getOwnPropertySlot(thisObject, exec, propertyName, slot);
+ }
+};
+
class DOMJITNode : public JSNonFinalObject {
public:
DOMJITNode(VM& vm, Structure* structure)
@@ -729,6 +802,7 @@
putDirectCustomAccessor(vm, Identifier::fromString(vm, "customGetter"), customGetterSetter, PropertyAttribute::ReadOnly | PropertyAttribute::CustomAccessor);
}
+
class DOMJITGetterComplex : public DOMJITNode {
public:
DOMJITGetterComplex(VM& vm, Structure* structure)
@@ -1199,6 +1273,8 @@
const ClassInfo DOMJITCheckSubClassObject::s_info = { "DOMJITCheckSubClassObject", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(DOMJITCheckSubClassObject) };
const ClassInfo JSTestCustomGetterSetter::s_info = { "JSTestCustomGetterSetter", &Base::s_info, nullptr, nullptr, CREATE_METHOD_TABLE(JSTestCustomGetterSetter) };
+const ClassInfo StaticCustomAccessor::s_info = { "StaticCustomAccessor", &Base::s_info, &staticCustomAccessorTable, nullptr, CREATE_METHOD_TABLE(StaticCustomAccessor) };
+
ElementHandleOwner* Element::handleOwner()
{
DollarVMAssertScope assertScope;
@@ -2015,6 +2091,16 @@
}
#endif
+static EncodedJSValue JSC_HOST_CALL functionCreateStaticCustomAccessor(ExecState* exec)
+{
+ DollarVMAssertScope assertScope;
+ VM& vm = exec->vm();
+ JSLockHolder lock(vm);
+ Structure* structure = StaticCustomAccessor::createStructure(vm, exec->lexicalGlobalObject(), jsNull());
+ auto* result = StaticCustomAccessor::create(vm, structure);
+ return JSValue::encode(result);
+}
+
static EncodedJSValue JSC_HOST_CALL functionSetImpureGetterDelegate(ExecState* exec)
{
DollarVMAssertScope assertScope;
@@ -2537,6 +2623,7 @@
#if ENABLE(WEBASSEMBLY)
addFunction(vm, "createWasmStreamingParser", functionCreateWasmStreamingParser, 0);
#endif
+ addFunction(vm, "createStaticCustomAccessor", functionCreateStaticCustomAccessor, 0);
addFunction(vm, "getPrivateProperty", functionGetPrivateProperty, 2);
addFunction(vm, "setImpureGetterDelegate", functionSetImpureGetterDelegate, 2);