DFG should have adaptive structure watchpoints
https://bugs.webkit.org/show_bug.cgi?id=146929

Reviewed by Geoffrey Garen.

Source/JavaScriptCore:

Before this change, if you wanted to efficiently validate whether an object has (or doesn't have) a
property, you'd check that the object still has the structure that you first saw the object have. We
optimized this a bit with transition watchpoints on the structure, which sometimes allowed us to
elide the structure check.

But this approach fails when that object frequently has new properties added to it. This would
change the structure and fire the transition watchpoint, so the code we emitted would be invalid and
we'd have to recompile either the IC or an entire code block.

This change introduces a new concept: an object property condition. This value describes some
condition involving a property on some object. There are four kinds: presence, absence,
absence-of-setter, and equivalence. For example, a presence condition says that we expect that the
object has some property at some offset with some attributes. This allows us to implement a new kind
of watchpoint, which knows about the object property condition that it's being used to enforce. If
the watchpoint fires because of a structure transition, the watchpoint may simply reinstall itself
on the new structure.

Object property conditions are used on the prototype chain of PutById transitions, GetById misses,
and prototype accesses. They are also used for any DFG accesses to object constants, including
global property accesses.

Mostly because of the effect on global property access, this is a 9% speed-up on Kraken. It's
neutral on most other things. It's a 68x speed-up on a microbenchmark that illustrates the prototype
chain situation. It's also a small speed-up on getter-richards.

* CMakeLists.txt:
* JavaScriptCore.vcxproj/JavaScriptCore.vcxproj:
* JavaScriptCore.xcodeproj/project.pbxproj:
* bytecode/CodeBlock.cpp:
(JSC::CodeBlock::printGetByIdCacheStatus):
(JSC::CodeBlock::printPutByIdCacheStatus):
* bytecode/CodeBlockJettisoningWatchpoint.cpp:
(JSC::CodeBlockJettisoningWatchpoint::fireInternal):
* bytecode/ComplexGetStatus.cpp:
(JSC::ComplexGetStatus::computeFor):
* bytecode/ComplexGetStatus.h:
(JSC::ComplexGetStatus::ComplexGetStatus):
(JSC::ComplexGetStatus::takesSlowPath):
(JSC::ComplexGetStatus::kind):
(JSC::ComplexGetStatus::offset):
(JSC::ComplexGetStatus::conditionSet):
(JSC::ComplexGetStatus::attributes): Deleted.
(JSC::ComplexGetStatus::specificValue): Deleted.
(JSC::ComplexGetStatus::chain): Deleted.
* bytecode/ConstantStructureCheck.cpp: Removed.
* bytecode/ConstantStructureCheck.h: Removed.
* bytecode/GetByIdStatus.cpp:
(JSC::GetByIdStatus::computeForStubInfo):
* bytecode/GetByIdVariant.cpp:
(JSC::GetByIdVariant::GetByIdVariant):
(JSC::GetByIdVariant::~GetByIdVariant):
(JSC::GetByIdVariant::operator=):
(JSC::GetByIdVariant::attemptToMerge):
(JSC::GetByIdVariant::dumpInContext):
(JSC::GetByIdVariant::baseStructure): Deleted.
* bytecode/GetByIdVariant.h:
(JSC::GetByIdVariant::operator!):
(JSC::GetByIdVariant::structureSet):
(JSC::GetByIdVariant::conditionSet):
(JSC::GetByIdVariant::offset):
(JSC::GetByIdVariant::callLinkStatus):
(JSC::GetByIdVariant::constantChecks): Deleted.
(JSC::GetByIdVariant::alternateBase): Deleted.
* bytecode/ObjectPropertyCondition.cpp: Added.
(JSC::ObjectPropertyCondition::dumpInContext):
(JSC::ObjectPropertyCondition::dump):
(JSC::ObjectPropertyCondition::structureEnsuresValidityAssumingImpurePropertyWatchpoint):
(JSC::ObjectPropertyCondition::validityRequiresImpurePropertyWatchpoint):
(JSC::ObjectPropertyCondition::isStillValid):
(JSC::ObjectPropertyCondition::structureEnsuresValidity):
(JSC::ObjectPropertyCondition::isWatchableAssumingImpurePropertyWatchpoint):
(JSC::ObjectPropertyCondition::isWatchable):
(JSC::ObjectPropertyCondition::isStillLive):
(JSC::ObjectPropertyCondition::validateReferences):
(JSC::ObjectPropertyCondition::attemptToMakeEquivalenceWithoutBarrier):
* bytecode/ObjectPropertyCondition.h: Added.
(JSC::ObjectPropertyCondition::ObjectPropertyCondition):
(JSC::ObjectPropertyCondition::presenceWithoutBarrier):
(JSC::ObjectPropertyCondition::presence):
(JSC::ObjectPropertyCondition::absenceWithoutBarrier):
(JSC::ObjectPropertyCondition::absence):
(JSC::ObjectPropertyCondition::absenceOfSetterWithoutBarrier):
(JSC::ObjectPropertyCondition::absenceOfSetter):
(JSC::ObjectPropertyCondition::equivalenceWithoutBarrier):
(JSC::ObjectPropertyCondition::equivalence):
(JSC::ObjectPropertyCondition::operator!):
(JSC::ObjectPropertyCondition::object):
(JSC::ObjectPropertyCondition::condition):
(JSC::ObjectPropertyCondition::kind):
(JSC::ObjectPropertyCondition::uid):
(JSC::ObjectPropertyCondition::hasOffset):
(JSC::ObjectPropertyCondition::offset):
(JSC::ObjectPropertyCondition::hasAttributes):
(JSC::ObjectPropertyCondition::attributes):
(JSC::ObjectPropertyCondition::hasPrototype):
(JSC::ObjectPropertyCondition::prototype):
(JSC::ObjectPropertyCondition::hasRequiredValue):
(JSC::ObjectPropertyCondition::requiredValue):
(JSC::ObjectPropertyCondition::hash):
(JSC::ObjectPropertyCondition::operator==):
(JSC::ObjectPropertyCondition::isHashTableDeletedValue):
(JSC::ObjectPropertyCondition::isCompatibleWith):
(JSC::ObjectPropertyCondition::watchingRequiresStructureTransitionWatchpoint):
(JSC::ObjectPropertyCondition::watchingRequiresReplacementWatchpoint):
(JSC::ObjectPropertyCondition::isValidValueForPresence):
(JSC::ObjectPropertyConditionHash::hash):
(JSC::ObjectPropertyConditionHash::equal):
* bytecode/ObjectPropertyConditionSet.cpp: Added.
(JSC::ObjectPropertyConditionSet::forObject):
(JSC::ObjectPropertyConditionSet::forConditionKind):
(JSC::ObjectPropertyConditionSet::numberOfConditionsWithKind):
(JSC::ObjectPropertyConditionSet::hasOneSlotBaseCondition):
(JSC::ObjectPropertyConditionSet::slotBaseCondition):
(JSC::ObjectPropertyConditionSet::mergedWith):
(JSC::ObjectPropertyConditionSet::structuresEnsureValidity):
(JSC::ObjectPropertyConditionSet::structuresEnsureValidityAssumingImpurePropertyWatchpoint):
(JSC::ObjectPropertyConditionSet::needImpurePropertyWatchpoint):
(JSC::ObjectPropertyConditionSet::areStillLive):
(JSC::ObjectPropertyConditionSet::dumpInContext):
(JSC::ObjectPropertyConditionSet::dump):
(JSC::generateConditionsForPropertyMiss):
(JSC::generateConditionsForPropertySetterMiss):
(JSC::generateConditionsForPrototypePropertyHit):
(JSC::generateConditionsForPrototypePropertyHitCustom):
(JSC::generateConditionsForPropertySetterMissConcurrently):
* bytecode/ObjectPropertyConditionSet.h: Added.
(JSC::ObjectPropertyConditionSet::ObjectPropertyConditionSet):
(JSC::ObjectPropertyConditionSet::invalid):
(JSC::ObjectPropertyConditionSet::nonEmpty):
(JSC::ObjectPropertyConditionSet::isValid):
(JSC::ObjectPropertyConditionSet::isEmpty):
(JSC::ObjectPropertyConditionSet::begin):
(JSC::ObjectPropertyConditionSet::end):
(JSC::ObjectPropertyConditionSet::releaseRawPointer):
(JSC::ObjectPropertyConditionSet::adoptRawPointer):
(JSC::ObjectPropertyConditionSet::fromRawPointer):
(JSC::ObjectPropertyConditionSet::Data::Data):
* bytecode/PolymorphicGetByIdList.cpp:
(JSC::GetByIdAccess::GetByIdAccess):
(JSC::GetByIdAccess::~GetByIdAccess):
(JSC::GetByIdAccess::visitWeak):
* bytecode/PolymorphicGetByIdList.h:
(JSC::GetByIdAccess::GetByIdAccess):
(JSC::GetByIdAccess::structure):
(JSC::GetByIdAccess::conditionSet):
(JSC::GetByIdAccess::stubRoutine):
(JSC::GetByIdAccess::chain): Deleted.
(JSC::GetByIdAccess::chainCount): Deleted.
* bytecode/PolymorphicPutByIdList.cpp:
(JSC::PutByIdAccess::fromStructureStubInfo):
(JSC::PutByIdAccess::visitWeak):
* bytecode/PolymorphicPutByIdList.h:
(JSC::PutByIdAccess::PutByIdAccess):
(JSC::PutByIdAccess::transition):
(JSC::PutByIdAccess::setter):
(JSC::PutByIdAccess::newStructure):
(JSC::PutByIdAccess::conditionSet):
(JSC::PutByIdAccess::stubRoutine):
(JSC::PutByIdAccess::chain): Deleted.
(JSC::PutByIdAccess::chainCount): Deleted.
* bytecode/PropertyCondition.cpp: Added.
(JSC::PropertyCondition::dumpInContext):
(JSC::PropertyCondition::dump):
(JSC::PropertyCondition::isStillValidAssumingImpurePropertyWatchpoint):
(JSC::PropertyCondition::validityRequiresImpurePropertyWatchpoint):
(JSC::PropertyCondition::isStillValid):
(JSC::PropertyCondition::isWatchableWhenValid):
(JSC::PropertyCondition::isWatchableAssumingImpurePropertyWatchpoint):
(JSC::PropertyCondition::isWatchable):
(JSC::PropertyCondition::isStillLive):
(JSC::PropertyCondition::validateReferences):
(JSC::PropertyCondition::isValidValueForAttributes):
(JSC::PropertyCondition::isValidValueForPresence):
(JSC::PropertyCondition::attemptToMakeEquivalenceWithoutBarrier):
(WTF::printInternal):
* bytecode/PropertyCondition.h: Added.
(JSC::PropertyCondition::PropertyCondition):
(JSC::PropertyCondition::presenceWithoutBarrier):
(JSC::PropertyCondition::presence):
(JSC::PropertyCondition::absenceWithoutBarrier):
(JSC::PropertyCondition::absence):
(JSC::PropertyCondition::absenceOfSetterWithoutBarrier):
(JSC::PropertyCondition::absenceOfSetter):
(JSC::PropertyCondition::equivalenceWithoutBarrier):
(JSC::PropertyCondition::equivalence):
(JSC::PropertyCondition::operator!):
(JSC::PropertyCondition::kind):
(JSC::PropertyCondition::uid):
(JSC::PropertyCondition::hasOffset):
(JSC::PropertyCondition::offset):
(JSC::PropertyCondition::hasAttributes):
(JSC::PropertyCondition::attributes):
(JSC::PropertyCondition::hasPrototype):
(JSC::PropertyCondition::prototype):
(JSC::PropertyCondition::hasRequiredValue):
(JSC::PropertyCondition::requiredValue):
(JSC::PropertyCondition::hash):
(JSC::PropertyCondition::operator==):
(JSC::PropertyCondition::isHashTableDeletedValue):
(JSC::PropertyCondition::isCompatibleWith):
(JSC::PropertyCondition::watchingRequiresStructureTransitionWatchpoint):
(JSC::PropertyCondition::watchingRequiresReplacementWatchpoint):
(JSC::PropertyConditionHash::hash):
(JSC::PropertyConditionHash::equal):
* bytecode/PutByIdStatus.cpp:
(JSC::PutByIdStatus::computeFromLLInt):
(JSC::PutByIdStatus::computeFor):
(JSC::PutByIdStatus::computeForStubInfo):
* bytecode/PutByIdVariant.cpp:
(JSC::PutByIdVariant::operator=):
(JSC::PutByIdVariant::transition):
(JSC::PutByIdVariant::setter):
(JSC::PutByIdVariant::makesCalls):
(JSC::PutByIdVariant::attemptToMerge):
(JSC::PutByIdVariant::attemptToMergeTransitionWithReplace):
(JSC::PutByIdVariant::dumpInContext):
(JSC::PutByIdVariant::baseStructure): Deleted.
* bytecode/PutByIdVariant.h:
(JSC::PutByIdVariant::PutByIdVariant):
(JSC::PutByIdVariant::kind):
(JSC::PutByIdVariant::structure):
(JSC::PutByIdVariant::structureSet):
(JSC::PutByIdVariant::oldStructure):
(JSC::PutByIdVariant::conditionSet):
(JSC::PutByIdVariant::offset):
(JSC::PutByIdVariant::callLinkStatus):
(JSC::PutByIdVariant::constantChecks): Deleted.
(JSC::PutByIdVariant::alternateBase): Deleted.
* bytecode/StructureStubClearingWatchpoint.cpp:
(JSC::StructureStubClearingWatchpoint::~StructureStubClearingWatchpoint):
(JSC::StructureStubClearingWatchpoint::push):
(JSC::StructureStubClearingWatchpoint::fireInternal):
(JSC::WatchpointsOnStructureStubInfo::~WatchpointsOnStructureStubInfo):
(JSC::WatchpointsOnStructureStubInfo::addWatchpoint):
(JSC::WatchpointsOnStructureStubInfo::ensureReferenceAndAddWatchpoint):
* bytecode/StructureStubClearingWatchpoint.h:
(JSC::StructureStubClearingWatchpoint::StructureStubClearingWatchpoint):
(JSC::WatchpointsOnStructureStubInfo::codeBlock):
(JSC::WatchpointsOnStructureStubInfo::stubInfo):
* bytecode/StructureStubInfo.cpp:
(JSC::StructureStubInfo::deref):
(JSC::StructureStubInfo::visitWeakReferences):
* bytecode/StructureStubInfo.h:
(JSC::StructureStubInfo::initPutByIdTransition):
(JSC::StructureStubInfo::initPutByIdReplace):
(JSC::StructureStubInfo::setSeen):
(JSC::StructureStubInfo::addWatchpoint):
* dfg/DFGAbstractInterpreterInlines.h:
(JSC::DFG::AbstractInterpreter<AbstractStateType>::executeEffects):
* dfg/DFGAdaptiveInferredPropertyValueWatchpoint.cpp: Added.
(JSC::DFG::AdaptiveInferredPropertyValueWatchpoint::AdaptiveInferredPropertyValueWatchpoint):
(JSC::DFG::AdaptiveInferredPropertyValueWatchpoint::install):
(JSC::DFG::AdaptiveInferredPropertyValueWatchpoint::fire):
(JSC::DFG::AdaptiveInferredPropertyValueWatchpoint::StructureWatchpoint::fireInternal):
(JSC::DFG::AdaptiveInferredPropertyValueWatchpoint::PropertyWatchpoint::fireInternal):
* dfg/DFGAdaptiveInferredPropertyValueWatchpoint.h: Added.
(JSC::DFG::AdaptiveInferredPropertyValueWatchpoint::key):
(JSC::DFG::AdaptiveInferredPropertyValueWatchpoint::StructureWatchpoint::StructureWatchpoint):
(JSC::DFG::AdaptiveInferredPropertyValueWatchpoint::PropertyWatchpoint::PropertyWatchpoint):
* dfg/DFGAdaptiveStructureWatchpoint.cpp: Added.
(JSC::DFG::AdaptiveStructureWatchpoint::AdaptiveStructureWatchpoint):
(JSC::DFG::AdaptiveStructureWatchpoint::install):
(JSC::DFG::AdaptiveStructureWatchpoint::fireInternal):
* dfg/DFGAdaptiveStructureWatchpoint.h: Added.
(JSC::DFG::AdaptiveStructureWatchpoint::key):
* dfg/DFGByteCodeParser.cpp:
(JSC::DFG::ByteCodeParser::cellConstantWithStructureCheck):
(JSC::DFG::ByteCodeParser::handleConstantInternalFunction):
(JSC::DFG::ByteCodeParser::handleGetByOffset):
(JSC::DFG::ByteCodeParser::handlePutByOffset):
(JSC::DFG::ByteCodeParser::check):
(JSC::DFG::ByteCodeParser::promoteToConstant):
(JSC::DFG::ByteCodeParser::planLoad):
(JSC::DFG::ByteCodeParser::load):
(JSC::DFG::ByteCodeParser::presenceLike):
(JSC::DFG::ByteCodeParser::checkPresenceLike):
(JSC::DFG::ByteCodeParser::store):
(JSC::DFG::ByteCodeParser::handleGetById):
(JSC::DFG::ByteCodeParser::handlePutById):
(JSC::DFG::ByteCodeParser::parseBlock):
(JSC::DFG::ByteCodeParser::emitChecks): Deleted.
* dfg/DFGCommonData.cpp:
(JSC::DFG::CommonData::validateReferences):
* dfg/DFGCommonData.h:
* dfg/DFGConstantFoldingPhase.cpp:
(JSC::DFG::ConstantFoldingPhase::foldConstants):
(JSC::DFG::ConstantFoldingPhase::emitGetByOffset):
(JSC::DFG::ConstantFoldingPhase::addBaseCheck):
(JSC::DFG::ConstantFoldingPhase::addStructureTransitionCheck):
(JSC::DFG::ConstantFoldingPhase::addChecks): Deleted.
* dfg/DFGDesiredWatchpoints.cpp:
(JSC::DFG::ArrayBufferViewWatchpointAdaptor::add):
(JSC::DFG::InferredValueAdaptor::add):
(JSC::DFG::AdaptiveStructureWatchpointAdaptor::add):
(JSC::DFG::DesiredWatchpoints::DesiredWatchpoints):
(JSC::DFG::DesiredWatchpoints::addLazily):
(JSC::DFG::DesiredWatchpoints::consider):
(JSC::DFG::DesiredWatchpoints::reallyAdd):
(JSC::DFG::DesiredWatchpoints::areStillValid):
(JSC::DFG::DesiredWatchpoints::dumpInContext):
* dfg/DFGDesiredWatchpoints.h:
(JSC::DFG::SetPointerAdaptor::add):
(JSC::DFG::SetPointerAdaptor::hasBeenInvalidated):
(JSC::DFG::SetPointerAdaptor::dumpInContext):
(JSC::DFG::InferredValueAdaptor::hasBeenInvalidated):
(JSC::DFG::InferredValueAdaptor::dumpInContext):
(JSC::DFG::ArrayBufferViewWatchpointAdaptor::hasBeenInvalidated):
(JSC::DFG::ArrayBufferViewWatchpointAdaptor::dumpInContext):
(JSC::DFG::AdaptiveStructureWatchpointAdaptor::hasBeenInvalidated):
(JSC::DFG::AdaptiveStructureWatchpointAdaptor::dumpInContext):
(JSC::DFG::GenericDesiredWatchpoints::reallyAdd):
(JSC::DFG::GenericDesiredWatchpoints::isWatched):
(JSC::DFG::GenericDesiredWatchpoints::dumpInContext):
(JSC::DFG::DesiredWatchpoints::isWatched):
(JSC::DFG::GenericSetAdaptor::add): Deleted.
(JSC::DFG::GenericSetAdaptor::hasBeenInvalidated): Deleted.
* dfg/DFGDesiredWeakReferences.cpp:
(JSC::DFG::DesiredWeakReferences::addLazily):
(JSC::DFG::DesiredWeakReferences::contains):
* dfg/DFGDesiredWeakReferences.h:
* dfg/DFGGraph.cpp:
(JSC::DFG::Graph::dump):
(JSC::DFG::Graph::clearFlagsOnAllNodes):
(JSC::DFG::Graph::watchCondition):
(JSC::DFG::Graph::isSafeToLoad):
(JSC::DFG::Graph::livenessFor):
(JSC::DFG::Graph::tryGetConstantProperty):
(JSC::DFG::Graph::visitChildren):
* dfg/DFGGraph.h:
(JSC::DFG::Graph::identifiers):
(JSC::DFG::Graph::watchpoints):
* dfg/DFGMultiGetByOffsetData.cpp: Added.
(JSC::DFG::GetByOffsetMethod::dumpInContext):
(JSC::DFG::GetByOffsetMethod::dump):
(JSC::DFG::MultiGetByOffsetCase::dumpInContext):
(JSC::DFG::MultiGetByOffsetCase::dump):
(WTF::printInternal):
* dfg/DFGMultiGetByOffsetData.h: Added.
(JSC::DFG::GetByOffsetMethod::GetByOffsetMethod):
(JSC::DFG::GetByOffsetMethod::constant):
(JSC::DFG::GetByOffsetMethod::load):
(JSC::DFG::GetByOffsetMethod::loadFromPrototype):
(JSC::DFG::GetByOffsetMethod::operator!):
(JSC::DFG::GetByOffsetMethod::kind):
(JSC::DFG::GetByOffsetMethod::prototype):
(JSC::DFG::GetByOffsetMethod::offset):
(JSC::DFG::MultiGetByOffsetCase::MultiGetByOffsetCase):
(JSC::DFG::MultiGetByOffsetCase::set):
(JSC::DFG::MultiGetByOffsetCase::method):
* dfg/DFGNode.h:
* dfg/DFGSafeToExecute.h:
(JSC::DFG::safeToExecute):
* dfg/DFGStructureRegistrationPhase.cpp:
(JSC::DFG::StructureRegistrationPhase::run):
* ftl/FTLLowerDFGToLLVM.cpp:
(JSC::FTL::DFG::LowerDFGToLLVM::compileMultiGetByOffset):
* jit/Repatch.cpp:
(JSC::repatchByIdSelfAccess):
(JSC::checkObjectPropertyCondition):
(JSC::checkObjectPropertyConditions):
(JSC::replaceWithJump):
(JSC::generateByIdStub):
(JSC::actionForCell):
(JSC::tryBuildGetByIDList):
(JSC::emitPutReplaceStub):
(JSC::emitPutTransitionStub):
(JSC::tryCachePutByID):
(JSC::tryBuildPutByIdList):
(JSC::tryRepatchIn):
(JSC::addStructureTransitionCheck): Deleted.
(JSC::emitPutTransitionStubAndGetOldStructure): Deleted.
* runtime/IntendedStructureChain.cpp: Removed.
* runtime/IntendedStructureChain.h: Removed.
* runtime/JSCJSValue.h:
* runtime/JSObject.cpp:
(JSC::throwTypeError):
(JSC::JSObject::convertToDictionary):
(JSC::JSObject::shiftButterflyAfterFlattening):
* runtime/JSObject.h:
(JSC::JSObject::flattenDictionaryObject):
(JSC::JSObject::convertToDictionary): Deleted.
* runtime/Operations.h:
(JSC::normalizePrototypeChain):
(JSC::normalizePrototypeChainForChainAccess): Deleted.
(JSC::isPrototypeChainNormalized): Deleted.
* runtime/PropertySlot.h:
(JSC::PropertySlot::PropertySlot):
(JSC::PropertySlot::slotBase):
* runtime/Structure.cpp:
(JSC::Structure::addPropertyTransition):
(JSC::Structure::attributeChangeTransition):
(JSC::Structure::toDictionaryTransition):
(JSC::Structure::toCacheableDictionaryTransition):
(JSC::Structure::toUncacheableDictionaryTransition):
(JSC::Structure::ensurePropertyReplacementWatchpointSet):
(JSC::Structure::startWatchingPropertyForReplacements):
(JSC::Structure::didCachePropertyReplacement):
(JSC::Structure::dump):
* runtime/Structure.h:
* runtime/VM.h:
* tests/stress/fold-multi-get-by-offset-to-get-by-offset-without-folding-the-structure-check-new.js: Added.
(foo):
(bar):
(baz):
* tests/stress/multi-get-by-offset-self-or-proto.js: Added.
(foo):
* tests/stress/replacement-watchpoint-dictionary.js: Added.
(foo):
* tests/stress/replacement-watchpoint.js: Added.
(foo):
* tests/stress/undefined-access-dictionary-then-proto-change.js: Added.
(foo):
* tests/stress/undefined-access-then-proto-change.js: Added.
(foo):

LayoutTests:

* js/regress/global-object-access-with-mutating-structure-expected.txt: Added.
* js/regress/global-object-access-with-mutating-structure.html: Added.
* js/regress/prototype-access-with-mutating-prototype-expected.txt: Added.
* js/regress/prototype-access-with-mutating-prototype.html: Added.
* js/regress/script-tests/global-object-access-with-mutating-structure.js: Added.
(foo):
* js/regress/script-tests/prototype-access-with-mutating-prototype.js: Added.
(foo):
* js/regress/script-tests/undefined-property-access.js:
(foo):
(bar):
(baz):



git-svn-id: http://svn.webkit.org/repository/webkit/trunk@187780 268f45cc-cd09-0410-ab3c-d52691b4dbfc
diff --git a/Source/JavaScriptCore/jit/Repatch.cpp b/Source/JavaScriptCore/jit/Repatch.cpp
index fe0d061..762f391 100644
--- a/Source/JavaScriptCore/jit/Repatch.cpp
+++ b/Source/JavaScriptCore/jit/Repatch.cpp
@@ -105,7 +105,7 @@
     const Identifier& propertyName, PropertyOffset offset, const FunctionPtr &slowPathFunction,
     bool compact)
 {
-    if (structure->typeInfo().newImpurePropertyFiresWatchpoints())
+    if (structure->needImpurePropertyWatchpoint())
         vm.registerWatchpointForImpureProperty(propertyName, stubInfo.addWatchpoint(codeBlock));
     
     RepatchBuffer repatchBuffer(codeBlock);
@@ -132,47 +132,33 @@
 #endif
 }
 
-static void addStructureTransitionCheck(
-    JSCell* object, Structure* structure, CodeBlock* codeBlock, StructureStubInfo& stubInfo,
+static void checkObjectPropertyCondition(
+    const ObjectPropertyCondition& condition, CodeBlock* codeBlock, StructureStubInfo& stubInfo,
     MacroAssembler& jit, MacroAssembler::JumpList& failureCases, GPRReg scratchGPR)
 {
-    if (object->structure() == structure && structure->transitionWatchpointSetIsStillValid()) {
-        structure->addTransitionWatchpoint(stubInfo.addWatchpoint(codeBlock));
-        if (!ASSERT_DISABLED) {
-            // If we execute this code, the object must have the structure we expect. Assert
-            // this in debug modes.
-            jit.move(MacroAssembler::TrustedImmPtr(object), scratchGPR);
-            MacroAssembler::Jump ok = branchStructure(
-                jit,
-                MacroAssembler::Equal,
-                MacroAssembler::Address(scratchGPR, JSCell::structureIDOffset()),
-                structure);
-            jit.abortWithReason(RepatchIneffectiveWatchpoint);
-            ok.link(&jit);
-        }
+    if (condition.isWatchableAssumingImpurePropertyWatchpoint()) {
+        condition.object()->structure()->addTransitionWatchpoint(
+            stubInfo.addWatchpoint(codeBlock, condition));
         return;
     }
-    
-    jit.move(MacroAssembler::TrustedImmPtr(object), scratchGPR);
+
+    Structure* structure = condition.object()->structure();
+    RELEASE_ASSERT(condition.structureEnsuresValidityAssumingImpurePropertyWatchpoint(structure));
+    jit.move(MacroAssembler::TrustedImmPtr(condition.object()), scratchGPR);
     failureCases.append(
-        branchStructure(jit,
-            MacroAssembler::NotEqual,
-            MacroAssembler::Address(scratchGPR, JSCell::structureIDOffset()),
-            structure));
+        branchStructure(
+            jit, MacroAssembler::NotEqual,
+            MacroAssembler::Address(scratchGPR, JSCell::structureIDOffset()), structure));
 }
 
-static void addStructureTransitionCheck(
-    JSValue prototype, CodeBlock* codeBlock, StructureStubInfo& stubInfo,
+static void checkObjectPropertyConditions(
+    const ObjectPropertyConditionSet& set, CodeBlock* codeBlock, StructureStubInfo& stubInfo,
     MacroAssembler& jit, MacroAssembler::JumpList& failureCases, GPRReg scratchGPR)
 {
-    if (prototype.isNull())
-        return;
-    
-    ASSERT(prototype.isCell());
-    
-    addStructureTransitionCheck(
-        prototype.asCell(), prototype.asCell()->structure(), codeBlock, stubInfo, jit,
-        failureCases, scratchGPR);
+    for (const ObjectPropertyCondition& condition : set) {
+        checkObjectPropertyCondition(
+            condition, codeBlock, stubInfo, jit, failureCases, scratchGPR);
+    }
 }
 
 static void replaceWithJump(RepatchBuffer& repatchBuffer, StructureStubInfo& stubInfo, const MacroAssemblerCodePtr target)
@@ -296,11 +282,13 @@
 
 static bool generateByIdStub(
     ExecState* exec, ByIdStubKind kind, const Identifier& propertyName,
-    FunctionPtr custom, StructureStubInfo& stubInfo, StructureChain* chain, size_t count,
-    PropertyOffset offset, Structure* structure, bool loadTargetFromProxy, WatchpointSet* watchpointSet,
-    CodeLocationLabel successLabel, CodeLocationLabel slowCaseLabel, RefPtr<JITStubRoutine>& stubRoutine)
+    FunctionPtr custom, StructureStubInfo& stubInfo, const ObjectPropertyConditionSet& conditionSet,
+    JSObject* alternateBase, PropertyOffset offset, Structure* structure, bool loadTargetFromProxy,
+    WatchpointSet* watchpointSet, CodeLocationLabel successLabel, CodeLocationLabel slowCaseLabel,
+    RefPtr<JITStubRoutine>& stubRoutine)
 {
-
+    ASSERT(conditionSet.structuresEnsureValidityAssumingImpurePropertyWatchpoint());
+    
     VM* vm = &exec->vm();
     GPRReg baseGPR = static_cast<GPRReg>(stubInfo.patch.baseGPR);
     JSValueRegs valueRegs = JSValueRegs(
@@ -346,37 +334,31 @@
     }
 
     CodeBlock* codeBlock = exec->codeBlock();
-    if (structure->typeInfo().newImpurePropertyFiresWatchpoints())
+    if (structure->needImpurePropertyWatchpoint() || conditionSet.needImpurePropertyWatchpoint())
         vm->registerWatchpointForImpureProperty(propertyName, stubInfo.addWatchpoint(codeBlock));
 
     if (watchpointSet)
         watchpointSet->add(stubInfo.addWatchpoint(codeBlock));
 
-    Structure* currStructure = structure; 
-    JSObject* protoObject = 0;
-    if (chain) {
-        WriteBarrier<Structure>* it = chain->head();
-        for (unsigned i = 0; i < count; ++i, ++it) {
-            protoObject = asObject(currStructure->prototypeForLookup(exec));
-            Structure* protoStructure = protoObject->structure();
-            if (protoStructure->typeInfo().newImpurePropertyFiresWatchpoints())
-                vm->registerWatchpointForImpureProperty(propertyName, stubInfo.addWatchpoint(codeBlock));
-            addStructureTransitionCheck(
-                protoObject, protoStructure, codeBlock, stubInfo, stubJit,
-                failureCases, scratchGPR);
-            currStructure = it->get();
-        }
-        ASSERT(!protoObject || protoObject->structure() == currStructure);
+    checkObjectPropertyConditions(
+        conditionSet, codeBlock, stubInfo, stubJit, failureCases, scratchGPR);
+
+    if (isValidOffset(offset)) {
+        Structure* currStructure;
+        if (conditionSet.isEmpty())
+            currStructure = structure;
+        else
+            currStructure = conditionSet.slotBaseCondition().object()->structure();
+        currStructure->startWatchingPropertyForReplacements(*vm, offset);
     }
     
-    currStructure->startWatchingPropertyForReplacements(*vm, offset);
     GPRReg baseForAccessGPR = InvalidGPRReg;
     if (kind != GetUndefined) {
-        if (chain) {
+        if (!conditionSet.isEmpty()) {
             // We could have clobbered scratchGPR earlier, so we have to reload from baseGPR to get the target.
             if (loadTargetFromProxy)
                 stubJit.loadPtr(MacroAssembler::Address(baseGPR, JSProxy::targetOffset()), baseForGetGPR);
-            stubJit.move(MacroAssembler::TrustedImmPtr(protoObject), scratchGPR);
+            stubJit.move(MacroAssembler::TrustedImmPtr(alternateBase), scratchGPR);
             baseForAccessGPR = scratchGPR;
         } else {
             // For proxy objects, we need to do all the Structure checks before moving the baseGPR into
@@ -625,9 +607,8 @@
         asObject(cell)->flattenDictionaryObject(vm);
         return RetryCacheLater;
     }
-    ASSERT(!structure->isUncacheableDictionary());
     
-    if (typeInfo.hasImpureGetOwnPropertySlot() && !typeInfo.newImpurePropertyFiresWatchpoints())
+    if (!structure->propertyAccessesAreCacheable())
         return GiveUpOnCache;
 
     return AttemptToCache;
@@ -825,21 +806,21 @@
     }
 
     PropertyOffset offset = slot.isUnset() ? invalidOffset : slot.cachedOffset();
-    StructureChain* prototypeChain = 0;
-    size_t count = 0;
     
+    ObjectPropertyConditionSet conditionSet;
     if (slot.isUnset() || slot.slotBase() != baseValue) {
         if (typeInfo.prohibitsPropertyCaching() || structure->isDictionary())
             return GiveUpOnCache;
 
         if (slot.isUnset())
-            count = normalizePrototypeChain(exec, structure);
+            conditionSet = generateConditionsForPropertyMiss(*vm, codeBlock->ownerExecutable(), exec, structure, ident.impl());
         else
-            count = normalizePrototypeChainForChainAccess(
-                exec, structure, slot.slotBase(), ident, offset);
-        if (count == InvalidPrototypeChain)
+            conditionSet = generateConditionsForPrototypePropertyHit(*vm, codeBlock->ownerExecutable(), exec, structure, slot.slotBase(), ident.impl());
+
+        if (!conditionSet.isValid())
             return GiveUpOnCache;
-        prototypeChain = structure->prototypeChain(exec);
+
+        offset = slot.isUnset() ? invalidOffset : conditionSet.slotBaseCondition().offset();
     }
     
     PolymorphicGetByIdList* list = PolymorphicGetByIdList::from(stubInfo);
@@ -850,7 +831,7 @@
     
     RefPtr<JITStubRoutine> stubRoutine;
     bool result = generateByIdStub(
-        exec, kindFor(slot), ident, customFor(slot), stubInfo, prototypeChain, count, offset, 
+        exec, kindFor(slot), ident, customFor(slot), stubInfo, conditionSet, slot.slotBase(), offset, 
         structure, loadTargetFromProxy, slot.watchpointSet(), 
         stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone),
         CodeLocationLabel(list->currentSlowPathTarget(stubInfo)), stubRoutine);
@@ -869,7 +850,7 @@
     
     list->addAccess(GetByIdAccess(
         *vm, codeBlock->ownerExecutable(), accessType, stubRoutine, structure,
-        prototypeChain, count));
+        conditionSet));
     
     patchJumpToGetByIdStub(codeBlock, stubInfo, stubRoutine.get());
     
@@ -991,26 +972,28 @@
     return true;
 }
 
-static Structure* emitPutTransitionStubAndGetOldStructure(ExecState* exec, VM* vm, Structure*& structure, const Identifier& ident, 
-    const PutPropertySlot& slot, StructureStubInfo& stubInfo, PutKind putKind)
+static bool emitPutTransitionStub(
+    ExecState* exec, VM* vm, Structure*& structure, const Identifier& ident, 
+    const PutPropertySlot& slot, StructureStubInfo& stubInfo, PutKind putKind,
+    Structure*& oldStructure, ObjectPropertyConditionSet& conditionSet)
 {
     PropertyName pname(ident);
-    Structure* oldStructure = structure;
+    oldStructure = structure;
     if (!oldStructure->isObject() || oldStructure->isDictionary() || parseIndex(pname))
-        return nullptr;
+        return false;
 
     PropertyOffset propertyOffset;
     structure = Structure::addPropertyTransitionToExistingStructureConcurrently(oldStructure, ident.impl(), 0, propertyOffset);
 
     if (!structure || !structure->isObject() || structure->isDictionary() || !structure->propertyAccessesAreCacheable())
-        return nullptr;
+        return false;
 
     // Skip optimizing the case where we need a realloc, if we don't have
     // enough registers to make it happen.
     if (GPRInfo::numberOfRegisters < 6
         && oldStructure->outOfLineCapacity() != structure->outOfLineCapacity()
         && oldStructure->outOfLineCapacity()) {
-        return nullptr;
+        return false;
     }
 
     // Skip optimizing the case where we need realloc, and the structure has
@@ -1018,14 +1001,14 @@
     // FIXME: We shouldn't skip this! Implement it!
     // https://bugs.webkit.org/show_bug.cgi?id=130914
     if (oldStructure->couldHaveIndexingHeader())
-        return nullptr;
+        return false;
 
-    if (normalizePrototypeChain(exec, structure) == InvalidPrototypeChain)
-        return nullptr;
-
-    StructureChain* prototypeChain = structure->prototypeChain(exec);
-
-    // emitPutTransitionStub
+    if (putKind == NotDirect) {
+        conditionSet = generateConditionsForPropertySetterMiss(
+            *vm, exec->codeBlock()->ownerExecutable(), exec, structure, ident.impl());
+        if (!conditionSet.isValid())
+            return false;
+    }
 
     CodeLocationLabel failureLabel = stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase);
     RefPtr<JITStubRoutine>& stubRoutine = stubInfo.stubRoutine;
@@ -1081,17 +1064,8 @@
         MacroAssembler::Address(baseGPR, JSCell::structureIDOffset()), 
         oldStructure));
     
-    addStructureTransitionCheck(
-        oldStructure->storedPrototype(), exec->codeBlock(), stubInfo, stubJit, failureCases,
-        scratchGPR1);
-            
-    if (putKind == NotDirect) {
-        for (WriteBarrier<Structure>* it = prototypeChain->head(); *it; ++it) {
-            addStructureTransitionCheck(
-                (*it)->storedPrototype(), exec->codeBlock(), stubInfo, stubJit, failureCases,
-                scratchGPR1);
-        }
-    }
+    checkObjectPropertyConditions(
+        conditionSet, exec->codeBlock(), stubInfo, stubJit, failureCases, scratchGPR1);
 
     MacroAssembler::JumpList slowPath;
     
@@ -1226,7 +1200,7 @@
     
     LinkBuffer patchBuffer(*vm, stubJit, exec->codeBlock(), JITCompilationCanFail);
     if (patchBuffer.didFailToAllocate())
-        return nullptr;
+        return false;
     
     patchBuffer.link(success, stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone));
     if (allocator.didReuseRegisters())
@@ -1254,8 +1228,8 @@
             exec->codeBlock()->ownerExecutable(),
             structure->outOfLineCapacity() != oldStructure->outOfLineCapacity(),
             structure);
-
-    return oldStructure;
+    
+    return true;
 }
 
 static InlineCacheAction tryCachePutByID(ExecState* exec, JSValue baseValue, Structure* structure, const Identifier& ident, const PutPropertySlot& slot, StructureStubInfo& stubInfo, PutKind putKind)
@@ -1279,12 +1253,11 @@
     if (slot.base() == baseValue && slot.isCacheablePut()) {
         if (slot.type() == PutPropertySlot::NewProperty) {
 
-            Structure* oldStructure = emitPutTransitionStubAndGetOldStructure(exec, vm, structure, ident, slot, stubInfo, putKind);
-            if (!oldStructure)
+            Structure* oldStructure;
+            ObjectPropertyConditionSet conditionSet;
+            if (!emitPutTransitionStub(exec, vm, structure, ident, slot, stubInfo, putKind, oldStructure, conditionSet))
                 return GiveUpOnCache;
             
-            StructureChain* prototypeChain = structure->prototypeChain(exec);
-            
             RepatchBuffer repatchBuffer(codeBlock);
             repatchBuffer.relink(
                 stubInfo.callReturnLocation.jumpAtOffset(
@@ -1292,7 +1265,7 @@
                 CodeLocationLabel(stubInfo.stubRoutine->code().code()));
             repatchCall(repatchBuffer, stubInfo.callReturnLocation, appropriateListBuildingPutByIdFunction(slot, putKind));
             
-            stubInfo.initPutByIdTransition(*vm, codeBlock->ownerExecutable(), oldStructure, structure, prototypeChain, putKind == Direct);
+            stubInfo.initPutByIdTransition(*vm, codeBlock->ownerExecutable(), oldStructure, structure, conditionSet, putKind == Direct);
             
             return RetryCacheLater;
         }
@@ -1310,20 +1283,31 @@
         && stubInfo.patch.spillMode == DontSpill) {
         RefPtr<JITStubRoutine> stubRoutine;
 
-        StructureChain* prototypeChain = 0;
-        PropertyOffset offset = slot.cachedOffset();
-        size_t count = 0;
-        if (baseValue != slot.base()) {
-            count = normalizePrototypeChainForChainAccess(exec, structure, slot.base(), ident, offset);
-            if (count == InvalidPrototypeChain)
+        ObjectPropertyConditionSet conditionSet;
+        PropertyOffset offset;
+        if (slot.base() != baseValue) {
+            if (slot.isCacheableCustom()) {
+                conditionSet =
+                    generateConditionsForPrototypePropertyHitCustom(
+                        *vm, codeBlock->ownerExecutable(), exec, structure, slot.base(),
+                        ident.impl());
+            } else {
+                conditionSet =
+                    generateConditionsForPrototypePropertyHit(
+                        *vm, codeBlock->ownerExecutable(), exec, structure, slot.base(),
+                        ident.impl());
+            }
+            if (!conditionSet.isValid())
                 return GiveUpOnCache;
-            prototypeChain = structure->prototypeChain(exec);
-        }
+            offset = slot.isCacheableCustom() ? invalidOffset : conditionSet.slotBaseCondition().offset();
+        } else
+            offset = slot.cachedOffset();
+
         PolymorphicPutByIdList* list;
         list = PolymorphicPutByIdList::from(putKind, stubInfo);
 
         bool result = generateByIdStub(
-            exec, kindFor(slot), ident, customFor(slot), stubInfo, prototypeChain, count,
+            exec, kindFor(slot), ident, customFor(slot), stubInfo, conditionSet, slot.base(),
             offset, structure, false, nullptr,
             stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone),
             stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToSlowCase),
@@ -1334,7 +1318,7 @@
         list->addAccess(PutByIdAccess::setter(
             *vm, codeBlock->ownerExecutable(),
             slot.isCacheableSetter() ? PutByIdAccess::Setter : PutByIdAccess::CustomSetter,
-            structure, prototypeChain, count, slot.customSetter(), stubRoutine));
+            structure, conditionSet, slot.customSetter(), stubRoutine));
 
         RepatchBuffer repatchBuffer(codeBlock);
         repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.deltaCallToJump), CodeLocationLabel(stubRoutine->code().code()));
@@ -1378,17 +1362,16 @@
             if (list->isFull())
                 return GiveUpOnCache; // Will get here due to recursion.
 
-            Structure* oldStructure = emitPutTransitionStubAndGetOldStructure(exec, vm, structure, propertyName, slot, stubInfo, putKind);
-
-            if (!oldStructure) 
+            Structure* oldStructure;
+            ObjectPropertyConditionSet conditionSet;
+            if (!emitPutTransitionStub(exec, vm, structure, propertyName, slot, stubInfo, putKind, oldStructure, conditionSet))
                 return GiveUpOnCache;
 
-            StructureChain* prototypeChain = structure->prototypeChain(exec);
             stubRoutine = stubInfo.stubRoutine;
             list->addAccess(
                 PutByIdAccess::transition(
                     *vm, codeBlock->ownerExecutable(),
-                    oldStructure, structure, prototypeChain,
+                    oldStructure, structure, conditionSet,
                     stubRoutine));
 
         } else {
@@ -1421,21 +1404,32 @@
     if ((slot.isCacheableCustom() || slot.isCacheableSetter())
         && stubInfo.patch.spillMode == DontSpill) {
         RefPtr<JITStubRoutine> stubRoutine;
-        StructureChain* prototypeChain = 0;
-        PropertyOffset offset = slot.cachedOffset();
-        size_t count = 0;
-        if (baseValue != slot.base()) {
-            count = normalizePrototypeChainForChainAccess(exec, structure, slot.base(), propertyName, offset);
-            if (count == InvalidPrototypeChain)
-                return GiveUpOnCache;
-            prototypeChain = structure->prototypeChain(exec);
-        }
         
+        ObjectPropertyConditionSet conditionSet;
+        PropertyOffset offset;
+        if (slot.base() != baseValue) {
+            if (slot.isCacheableCustom()) {
+                conditionSet =
+                    generateConditionsForPrototypePropertyHitCustom(
+                        *vm, codeBlock->ownerExecutable(), exec, structure, slot.base(),
+                        propertyName.impl());
+            } else {
+                conditionSet =
+                    generateConditionsForPrototypePropertyHit(
+                        *vm, codeBlock->ownerExecutable(), exec, structure, slot.base(),
+                        propertyName.impl());
+            }
+            if (!conditionSet.isValid())
+                return GiveUpOnCache;
+            offset = slot.isCacheableCustom() ? invalidOffset : conditionSet.slotBaseCondition().offset();
+        } else
+            offset = slot.cachedOffset();
+
         PolymorphicPutByIdList* list;
         list = PolymorphicPutByIdList::from(putKind, stubInfo);
 
         bool result = generateByIdStub(
-            exec, kindFor(slot), propertyName, customFor(slot), stubInfo, prototypeChain, count,
+            exec, kindFor(slot), propertyName, customFor(slot), stubInfo, conditionSet, slot.base(),
             offset, structure, false, nullptr,
             stubInfo.callReturnLocation.labelAtOffset(stubInfo.patch.deltaCallToDone),
             CodeLocationLabel(list->currentSlowPathTarget()),
@@ -1446,7 +1440,7 @@
         list->addAccess(PutByIdAccess::setter(
             *vm, codeBlock->ownerExecutable(),
             slot.isCacheableSetter() ? PutByIdAccess::Setter : PutByIdAccess::CustomSetter,
-            structure, prototypeChain, count, slot.customSetter(), stubRoutine));
+            structure, conditionSet, slot.customSetter(), stubRoutine));
 
         RepatchBuffer repatchBuffer(codeBlock);
         repatchBuffer.relink(stubInfo.callReturnLocation.jumpAtOffset(stubInfo.patch.deltaCallToJump), CodeLocationLabel(stubRoutine->code().code()));
@@ -1485,11 +1479,17 @@
     VM* vm = &exec->vm();
     Structure* structure = base->structure(*vm);
     
-    PropertyOffset offsetIgnored;
-    JSValue foundSlotBase = wasFound ? slot.slotBase() : JSValue();
-    size_t count = !foundSlotBase || foundSlotBase != base ? 
-        normalizePrototypeChainForChainAccess(exec, structure, foundSlotBase, ident, offsetIgnored) : 0;
-    if (count == InvalidPrototypeChain)
+    ObjectPropertyConditionSet conditionSet;
+    if (wasFound) {
+        if (slot.slotBase() != base) {
+            conditionSet = generateConditionsForPrototypePropertyHit(
+                *vm, codeBlock->ownerExecutable(), exec, structure, slot.slotBase(), ident.impl());
+        }
+    } else {
+        conditionSet = generateConditionsForPropertyMiss(
+            *vm, codeBlock->ownerExecutable(), exec, structure, ident.impl());
+    }
+    if (!conditionSet.isValid())
         return GiveUpOnCache;
     
     PolymorphicAccessStructureList* polymorphicStructureList;
@@ -1514,7 +1514,6 @@
             return GiveUpOnCache;
     }
     
-    StructureChain* chain = structure->prototypeChain(exec);
     RefPtr<JITStubRoutine> stubRoutine;
     
     {
@@ -1545,18 +1544,8 @@
         if (slot.watchpointSet())
             slot.watchpointSet()->add(stubInfo.addWatchpoint(codeBlock));
 
-        Structure* currStructure = structure;
-        WriteBarrier<Structure>* it = chain->head();
-        for (unsigned i = 0; i < count; ++i, ++it) {
-            JSObject* prototype = asObject(currStructure->prototypeForLookup(exec));
-            Structure* protoStructure = prototype->structure();
-            addStructureTransitionCheck(
-                prototype, protoStructure, exec->codeBlock(), stubInfo, stubJit,
-                failureCases, scratchGPR);
-            if (protoStructure->typeInfo().newImpurePropertyFiresWatchpoints())
-                vm->registerWatchpointForImpureProperty(ident, stubInfo.addWatchpoint(codeBlock));
-            currStructure = it->get();
-        }
+        checkObjectPropertyConditions(
+            conditionSet, exec->codeBlock(), stubInfo, stubJit, failureCases, scratchGPR);
         
 #if USE(JSVALUE64)
         stubJit.move(MacroAssembler::TrustedImm64(JSValue::encode(jsBoolean(wasFound))), resultGPR);