DFG should have adaptive structure watchpoints
authorfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 3 Aug 2015 23:13:56 +0000 (23:13 +0000)
committerfpizlo@apple.com <fpizlo@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 3 Aug 2015 23:13:56 +0000 (23:13 +0000)
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: https://svn.webkit.org/repository/webkit/trunk@187780 268f45cc-cd09-0410-ab3c-d52691b4dbfc

74 files changed:
LayoutTests/ChangeLog
LayoutTests/js/regress/global-object-access-with-mutating-structure-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/global-object-access-with-mutating-structure.html [new file with mode: 0644]
LayoutTests/js/regress/prototype-access-with-mutating-prototype-expected.txt [new file with mode: 0644]
LayoutTests/js/regress/prototype-access-with-mutating-prototype.html [new file with mode: 0644]
LayoutTests/js/regress/script-tests/global-object-access-with-mutating-structure.js [new file with mode: 0644]
LayoutTests/js/regress/script-tests/prototype-access-with-mutating-prototype.js [new file with mode: 0644]
LayoutTests/js/regress/script-tests/undefined-property-access.js
Source/JavaScriptCore/CMakeLists.txt
Source/JavaScriptCore/ChangeLog
Source/JavaScriptCore/JavaScriptCore.vcxproj/JavaScriptCore.vcxproj
Source/JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
Source/JavaScriptCore/bytecode/CodeBlock.cpp
Source/JavaScriptCore/bytecode/CodeBlockJettisoningWatchpoint.cpp
Source/JavaScriptCore/bytecode/ComplexGetStatus.cpp
Source/JavaScriptCore/bytecode/ComplexGetStatus.h
Source/JavaScriptCore/bytecode/GetByIdStatus.cpp
Source/JavaScriptCore/bytecode/GetByIdVariant.cpp
Source/JavaScriptCore/bytecode/GetByIdVariant.h
Source/JavaScriptCore/bytecode/ObjectPropertyCondition.cpp [new file with mode: 0644]
Source/JavaScriptCore/bytecode/ObjectPropertyCondition.h [new file with mode: 0644]
Source/JavaScriptCore/bytecode/ObjectPropertyConditionSet.cpp [new file with mode: 0644]
Source/JavaScriptCore/bytecode/ObjectPropertyConditionSet.h [new file with mode: 0644]
Source/JavaScriptCore/bytecode/PolymorphicGetByIdList.cpp
Source/JavaScriptCore/bytecode/PolymorphicGetByIdList.h
Source/JavaScriptCore/bytecode/PolymorphicPutByIdList.cpp
Source/JavaScriptCore/bytecode/PolymorphicPutByIdList.h
Source/JavaScriptCore/bytecode/PropertyCondition.cpp [new file with mode: 0644]
Source/JavaScriptCore/bytecode/PropertyCondition.h [new file with mode: 0644]
Source/JavaScriptCore/bytecode/PutByIdStatus.cpp
Source/JavaScriptCore/bytecode/PutByIdVariant.cpp
Source/JavaScriptCore/bytecode/PutByIdVariant.h
Source/JavaScriptCore/bytecode/StructureStubClearingWatchpoint.cpp
Source/JavaScriptCore/bytecode/StructureStubClearingWatchpoint.h
Source/JavaScriptCore/bytecode/StructureStubInfo.cpp
Source/JavaScriptCore/bytecode/StructureStubInfo.h
Source/JavaScriptCore/dfg/DFGAbstractInterpreterInlines.h
Source/JavaScriptCore/dfg/DFGAdaptiveInferredPropertyValueWatchpoint.cpp [new file with mode: 0644]
Source/JavaScriptCore/dfg/DFGAdaptiveInferredPropertyValueWatchpoint.h [moved from Source/JavaScriptCore/bytecode/ConstantStructureCheck.h with 50% similarity]
Source/JavaScriptCore/dfg/DFGAdaptiveStructureWatchpoint.cpp [moved from Source/JavaScriptCore/bytecode/ConstantStructureCheck.cpp with 50% similarity]
Source/JavaScriptCore/dfg/DFGAdaptiveStructureWatchpoint.h [new file with mode: 0644]
Source/JavaScriptCore/dfg/DFGByteCodeParser.cpp
Source/JavaScriptCore/dfg/DFGCommonData.cpp
Source/JavaScriptCore/dfg/DFGCommonData.h
Source/JavaScriptCore/dfg/DFGConstantFoldingPhase.cpp
Source/JavaScriptCore/dfg/DFGDesiredWatchpoints.cpp
Source/JavaScriptCore/dfg/DFGDesiredWatchpoints.h
Source/JavaScriptCore/dfg/DFGDesiredWeakReferences.cpp
Source/JavaScriptCore/dfg/DFGDesiredWeakReferences.h
Source/JavaScriptCore/dfg/DFGGraph.cpp
Source/JavaScriptCore/dfg/DFGGraph.h
Source/JavaScriptCore/dfg/DFGMultiGetByOffsetData.cpp [new file with mode: 0644]
Source/JavaScriptCore/dfg/DFGMultiGetByOffsetData.h [new file with mode: 0644]
Source/JavaScriptCore/dfg/DFGNode.h
Source/JavaScriptCore/dfg/DFGSafeToExecute.h
Source/JavaScriptCore/dfg/DFGStructureRegistrationPhase.cpp
Source/JavaScriptCore/ftl/FTLLowerDFGToLLVM.cpp
Source/JavaScriptCore/jit/Repatch.cpp
Source/JavaScriptCore/runtime/IntendedStructureChain.cpp [deleted file]
Source/JavaScriptCore/runtime/IntendedStructureChain.h [deleted file]
Source/JavaScriptCore/runtime/JSCJSValue.h
Source/JavaScriptCore/runtime/JSObject.cpp
Source/JavaScriptCore/runtime/JSObject.h
Source/JavaScriptCore/runtime/Operations.h
Source/JavaScriptCore/runtime/PropertySlot.h
Source/JavaScriptCore/runtime/Structure.cpp
Source/JavaScriptCore/runtime/Structure.h
Source/JavaScriptCore/runtime/VM.h
Source/JavaScriptCore/tests/stress/fold-multi-get-by-offset-to-get-by-offset-without-folding-the-structure-check-new.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/multi-get-by-offset-self-or-proto.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/replacement-watchpoint-dictionary.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/replacement-watchpoint.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/undefined-access-dictionary-then-proto-change.js [new file with mode: 0644]
Source/JavaScriptCore/tests/stress/undefined-access-then-proto-change.js [new file with mode: 0644]

index d81edea..797a10d 100644 (file)
@@ -1,3 +1,23 @@
+2015-07-31  Filip Pizlo  <fpizlo@apple.com>
+
+        DFG should have adaptive structure watchpoints
+        https://bugs.webkit.org/show_bug.cgi?id=146929
+
+        Reviewed by Geoffrey Garen.
+
+        * 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):
+
 2015-08-03  Brady Eidson  <beidson@apple.com>
 
         Fix ASSERTy test added in r187752
diff --git a/LayoutTests/js/regress/global-object-access-with-mutating-structure-expected.txt b/LayoutTests/js/regress/global-object-access-with-mutating-structure-expected.txt
new file mode 100644 (file)
index 0000000..ece6469
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/global-object-access-with-mutating-structure
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/global-object-access-with-mutating-structure.html b/LayoutTests/js/regress/global-object-access-with-mutating-structure.html
new file mode 100644 (file)
index 0000000..a772d64
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/global-object-access-with-mutating-structure.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/prototype-access-with-mutating-prototype-expected.txt b/LayoutTests/js/regress/prototype-access-with-mutating-prototype-expected.txt
new file mode 100644 (file)
index 0000000..40c8228
--- /dev/null
@@ -0,0 +1,10 @@
+JSRegress/prototype-access-with-mutating-prototype
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS no exception thrown
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/js/regress/prototype-access-with-mutating-prototype.html b/LayoutTests/js/regress/prototype-access-with-mutating-prototype.html
new file mode 100644 (file)
index 0000000..6a36db1
--- /dev/null
@@ -0,0 +1,12 @@
+<!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML//EN">
+<html>
+<head>
+<script src="../../resources/js-test-pre.js"></script>
+</head>
+<body>
+<script src="../../resources/regress-pre.js"></script>
+<script src="script-tests/prototype-access-with-mutating-prototype.js"></script>
+<script src="../../resources/regress-post.js"></script>
+<script src="../../resources/js-test-post.js"></script>
+</body>
+</html>
diff --git a/LayoutTests/js/regress/script-tests/global-object-access-with-mutating-structure.js b/LayoutTests/js/regress/script-tests/global-object-access-with-mutating-structure.js
new file mode 100644 (file)
index 0000000..6c5e336
--- /dev/null
@@ -0,0 +1,16 @@
+function foo() {
+    var result = 0;
+    for (var i = 0; i < 100; ++i)
+        result += f;
+    return result;
+}
+
+noInline(foo);
+
+f = 42;
+for (var i = 0; i < 10000; ++i) {
+    this["i" + i] = i;
+    var result = foo();
+    if (result != 42 * 100)
+        throw "Error: bad result: " + result;
+}
diff --git a/LayoutTests/js/regress/script-tests/prototype-access-with-mutating-prototype.js b/LayoutTests/js/regress/script-tests/prototype-access-with-mutating-prototype.js
new file mode 100644 (file)
index 0000000..c5dbcef
--- /dev/null
@@ -0,0 +1,18 @@
+function foo(o) {
+    var result = 0;
+    for (var i = 0; i < 100; ++i)
+        result += o.f;
+    return result;
+}
+
+noInline(foo);
+
+var p = {f:42};
+var o = Object.create(p);
+
+for (var i = 0; i < 10000; ++i) {
+    p["i" + i] = i;
+    var result = foo(o);
+    if (result != 42 * 100)
+        throw "Error: bad result: " + result;
+}
index 7a6a848..c9765bc 100644 (file)
@@ -11,7 +11,7 @@ function foo() {
 }
 result = foo();
 if (result != undefined)
-    throw "Bad result: " + result;
+    throw new Error("Bad result: " + result);
 
 // This test checks that a cached property lookup miss doesn't continue to fire when the property suddenly appears on the object.
 
@@ -26,7 +26,7 @@ function bar() {
 }
 var result = bar();
 if (result != 1)
-    throw "Bad result: " + result;
+    throw new Error("Bad result: " + result);
 someGlobal = undefined;
 
 // This test checks that a cached property lookup miss doesn't continue to fire when the property suddenly appears on the object's prototype.
@@ -44,5 +44,5 @@ function baz() {
 }
 var result = baz();
 if (result != 2)
-    throw "Bad result: " + result;
+    throw new Error("Bad result: " + result);
 
index 644add6..4ade0ed 100644 (file)
@@ -84,7 +84,6 @@ set(JavaScriptCore_SOURCES
     bytecode/CodeOrigin.cpp
     bytecode/CodeType.cpp
     bytecode/ComplexGetStatus.cpp
-    bytecode/ConstantStructureCheck.cpp
     bytecode/DFGExitProfile.cpp
     bytecode/DeferredCompilationCallback.cpp
     bytecode/DeferredSourceDump.cpp
@@ -97,10 +96,13 @@ set(JavaScriptCore_SOURCES
     bytecode/JumpTable.cpp
     bytecode/LazyOperandValueProfile.cpp
     bytecode/MethodOfGettingAValueProfile.cpp
+    bytecode/ObjectPropertyCondition.cpp
+    bytecode/ObjectPropertyConditionSet.cpp
     bytecode/Opcode.cpp
     bytecode/PolymorphicGetByIdList.cpp
     bytecode/PolymorphicPutByIdList.cpp
     bytecode/PreciseJumpTargets.cpp
+    bytecode/PropertyCondition.cpp
     bytecode/PutByIdStatus.cpp
     bytecode/PutByIdVariant.cpp
     bytecode/ReduceWhitespace.cpp
@@ -128,6 +130,8 @@ set(JavaScriptCore_SOURCES
 
     dfg/DFGAbstractHeap.cpp
     dfg/DFGAbstractValue.cpp
+    dfg/DFGAdaptiveInferredPropertyValueWatchpoint.cpp
+    dfg/DFGAdaptiveStructureWatchpoint.cpp
     dfg/DFGArgumentsEliminationPhase.cpp
     dfg/DFGArgumentsUtilities.cpp
     dfg/DFGArithMode.cpp
@@ -196,6 +200,7 @@ set(JavaScriptCore_SOURCES
     dfg/DFGMinifiedGraph.cpp
     dfg/DFGMinifiedNode.cpp
     dfg/DFGMovHintRemovalPhase.cpp
+    dfg/DFGMultiGetByOffsetData.cpp
     dfg/DFGNaiveDominators.cpp
     dfg/DFGNaturalLoops.cpp
     dfg/DFGNode.cpp
@@ -476,7 +481,6 @@ set(JavaScriptCore_RUNTIME_SOURCES
     runtime/IndexingType.cpp
     runtime/InferredValue.cpp
     runtime/InitializeThreading.cpp
-    runtime/IntendedStructureChain.cpp
     runtime/InternalFunction.cpp
     runtime/IntlCollator.cpp
     runtime/IntlCollatorConstructor.cpp
index 0022ebf..9aba20c 100644 (file)
@@ -1,3 +1,425 @@
+2015-07-31  Filip Pizlo  <fpizlo@apple.com>
+
+        DFG should have adaptive structure watchpoints
+        https://bugs.webkit.org/show_bug.cgi?id=146929
+
+        Reviewed by Geoffrey Garen.
+
+        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):
+
 2015-08-03  Yusuke Suzuki  <utatane.tea@gmail.com>
 
         JavascriptCore Crash in JSC::ASTBuilder::Property JSC::Parser<JSC::Lexer<unsigned char> >::parseProperty<JSC::ASTBuilder>(JSC::ASTBuilder&, bool)
index 9136970..f41e703 100644 (file)
@@ -1,4 +1,4 @@
-<?xml version="1.0" encoding="utf-8"?>
+<?xml version="1.0" encoding="utf-8"?>
 <Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup Label="ProjectConfigurations">
     <ProjectConfiguration Include="DebugSuffix|Win32">
     <ClCompile Include="..\bytecode\CodeOrigin.cpp" />
     <ClCompile Include="..\bytecode\CodeType.cpp" />
     <ClCompile Include="..\bytecode\ComplexGetStatus.cpp" />
-    <ClCompile Include="..\bytecode\ConstantStructureCheck.cpp" />
     <ClCompile Include="..\bytecode\DeferredCompilationCallback.cpp" />
     <ClCompile Include="..\bytecode\DeferredSourceDump.cpp" />
     <ClCompile Include="..\bytecode\DFGExitProfile.cpp" />
     <ClCompile Include="..\bytecode\JumpTable.cpp" />
     <ClCompile Include="..\bytecode\LazyOperandValueProfile.cpp" />
     <ClCompile Include="..\bytecode\MethodOfGettingAValueProfile.cpp" />
+    <ClCompile Include="..\bytecode\ObjectPropertyCondition.cpp" />
+    <ClCompile Include="..\bytecode\ObjectPropertyConditionSet.cpp" />
     <ClCompile Include="..\bytecode\Opcode.cpp" />
     <ClCompile Include="..\bytecode\PolymorphicGetByIdList.cpp" />
     <ClCompile Include="..\bytecode\PolymorphicPutByIdList.cpp" />
     <ClCompile Include="..\bytecode\PreciseJumpTargets.cpp" />
+    <ClCompile Include="..\bytecode\PropertyCondition.cpp" />
     <ClCompile Include="..\bytecode\PutByIdStatus.cpp" />
     <ClCompile Include="..\bytecode\PutByIdVariant.cpp" />
     <ClCompile Include="..\bytecode\ReduceWhitespace.cpp" />
     <ClCompile Include="..\debugger\DebuggerScope.cpp" />
     <ClCompile Include="..\dfg\DFGAbstractHeap.cpp" />
     <ClCompile Include="..\dfg\DFGAbstractValue.cpp" />
+    <ClCompile Include="..\dfg\DFGAdaptiveInferredPropertyValueWatchpoint.cpp" />
+    <ClCompile Include="..\dfg\DFGAdaptiveStructureWatchpoint.cpp" />
     <ClCompile Include="..\dfg\DFGArgumentsEliminationPhase.cpp" />
     <ClCompile Include="..\dfg\DFGArgumentsUtilities.cpp" />
     <ClCompile Include="..\dfg\DFGArithMode.cpp" />
     <ClCompile Include="..\dfg\DFGMinifiedGraph.cpp" />
     <ClCompile Include="..\dfg\DFGMinifiedNode.cpp" />
     <ClCompile Include="..\dfg\DFGMovHintRemovalPhase.cpp" />
+    <ClCompile Include="..\dfg\DFGMultiGetByOffsetData.cpp" />
     <ClCompile Include="..\dfg\DFGNaiveDominators.cpp" />
     <ClCompile Include="..\dfg\DFGNaturalLoops.cpp" />
     <ClCompile Include="..\dfg\DFGNode.cpp" />
     <ClCompile Include="..\runtime\IndexingType.cpp" />
     <ClCompile Include="..\runtime\InferredValue.cpp" />
     <ClCompile Include="..\runtime\InitializeThreading.cpp" />
-    <ClCompile Include="..\runtime\IntendedStructureChain.cpp" />
     <ClCompile Include="..\runtime\InternalFunction.cpp" />
     <ClCompile Include="..\runtime\IntlCollator.cpp" />
     <ClCompile Include="..\runtime\IntlCollatorConstructor.cpp" />
     <ClInclude Include="..\bytecode\CodeType.h" />
     <ClInclude Include="..\bytecode\Comment.h" />
     <ClInclude Include="..\bytecode\ComplexGetStatus.h" />
-    <ClInclude Include="..\bytecode\ConstantStructureCheck.h" />
     <ClInclude Include="..\bytecode\DataFormat.h" />
     <ClInclude Include="..\bytecode\DeferredCompilationCallback.h" />
     <ClInclude Include="..\bytecode\DeferredSourceDump.h" />
     <ClInclude Include="..\bytecode\LazyOperandValueProfile.h" />
     <ClInclude Include="..\bytecode\LLIntCallLinkInfo.h" />
     <ClInclude Include="..\bytecode\MethodOfGettingAValueProfile.h" />
+    <ClInclude Include="..\bytecode\ObjectPropertyCondition.h" />
+    <ClInclude Include="..\bytecode\ObjectPropertyConditionSet.h" />
     <ClInclude Include="..\bytecode\Opcode.h" />
     <ClInclude Include="..\bytecode\Operands.h" />
     <ClInclude Include="..\bytecode\PolymorphicGetByIdList.h" />
     <ClInclude Include="..\bytecode\PolymorphicPutByIdList.h" />
     <ClInclude Include="..\bytecode\PreciseJumpTargets.h" />
+    <ClInclude Include="..\bytecode\PropertyCondition.h" />
     <ClInclude Include="..\bytecode\PutByIdStatus.h" />
     <ClInclude Include="..\bytecode\PutByIdVariant.h" />
     <ClInclude Include="..\bytecode\PutKind.h" />
     <ClInclude Include="..\dfg\DFGAbstractInterpreter.h" />
     <ClInclude Include="..\dfg\DFGAbstractInterpreterInlines.h" />
     <ClInclude Include="..\dfg\DFGAbstractValue.h" />
+    <ClInclude Include="..\dfg\DFGAdaptiveInferredPropertyValueWatchpoint.h" />
+    <ClInclude Include="..\dfg\DFGAdaptiveStructureWatchpoint.h" />
     <ClInclude Include="..\dfg\DFGAdjacencyList.h" />
     <ClInclude Include="..\dfg\DFGAllocator.h" />
     <ClInclude Include="..\dfg\DFGAnalysis.h" />
     <ClInclude Include="..\dfg\DFGMinifiedID.h" />
     <ClInclude Include="..\dfg\DFGMinifiedNode.h" />
     <ClInclude Include="..\dfg\DFGMovHintRemovalPhase.h" />
+    <ClInclude Include="..\dfg\DFGMultiGetByOffsetData.h" />
     <ClInclude Include="..\dfg\DFGNaiveDominators.h" />
     <ClInclude Include="..\dfg\DFGNaturalLoops.h" />
     <ClInclude Include="..\dfg\DFGNode.h" />
     <ClInclude Include="..\runtime\Int16Array.h" />
     <ClInclude Include="..\runtime\Int32Array.h" />
     <ClInclude Include="..\runtime\Int8Array.h" />
-    <ClInclude Include="..\runtime\IntendedStructureChain.h" />
     <ClInclude Include="..\runtime\InternalFunction.h" />
     <ClInclude Include="..\runtime\IntlCollator.h" />
     <ClInclude Include="..\runtime\IntlCollatorConstructor.h" />
index 6a03982..cf107b6 100644 (file)
                0F13E04E16164A1F00DC8DE7 /* IndexingType.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F13E04C16164A1B00DC8DE7 /* IndexingType.cpp */; };
                0F15F15F14B7A73E005DE37D /* CommonSlowPaths.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F15F15D14B7A73A005DE37D /* CommonSlowPaths.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0F1725FF1B48719A00AC3A55 /* DFGMinifiedGraph.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F1725FE1B48719A00AC3A55 /* DFGMinifiedGraph.cpp */; };
+               0F18D3CF1B55A6E0002C5C9F /* DFGAdaptiveStructureWatchpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F18D3CD1B55A6E0002C5C9F /* DFGAdaptiveStructureWatchpoint.cpp */; };
+               0F18D3D01B55A6E0002C5C9F /* DFGAdaptiveStructureWatchpoint.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F18D3CE1B55A6E0002C5C9F /* DFGAdaptiveStructureWatchpoint.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0F190CAC189D82F6000AE5F0 /* ProfilerJettisonReason.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F190CAA189D82F6000AE5F0 /* ProfilerJettisonReason.cpp */; };
                0F190CAD189D82F6000AE5F0 /* ProfilerJettisonReason.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F190CAB189D82F6000AE5F0 /* ProfilerJettisonReason.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0F1DD84A18A945BE0026F3FA /* JSCInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F1DD84918A945BE0026F3FA /* JSCInlines.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0F3B3A2C15475002003ED0FF /* DFGValidate.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F3B3A2A15474FF4003ED0FF /* DFGValidate.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0F3B7E2A19A11B8000D9BC56 /* CallVariant.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F3B7E2419A11B8000D9BC56 /* CallVariant.cpp */; };
                0F3B7E2B19A11B8000D9BC56 /* CallVariant.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F3B7E2519A11B8000D9BC56 /* CallVariant.h */; settings = {ATTRIBUTES = (Private, ); }; };
-               0F3D0BBC194A414300FC9CF9 /* ConstantStructureCheck.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F3D0BBA194A414300FC9CF9 /* ConstantStructureCheck.cpp */; };
-               0F3D0BBD194A414300FC9CF9 /* ConstantStructureCheck.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F3D0BBB194A414300FC9CF9 /* ConstantStructureCheck.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0F3E01AA19D353A500F61B7F /* DFGPrePostNumbering.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0F3E01A819D353A500F61B7F /* DFGPrePostNumbering.cpp */; };
                0F3E01AB19D353A500F61B7F /* DFGPrePostNumbering.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F3E01A919D353A500F61B7F /* DFGPrePostNumbering.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0F426A481460CBB300131F8F /* ValueRecovery.h in Headers */ = {isa = PBXBuildFile; fileRef = 0F426A451460CBAB00131F8F /* ValueRecovery.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0FD2C92416D01EE900C7803F /* StructureInlines.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FD2C92316D01EE900C7803F /* StructureInlines.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0FD3C82614115D4000FD81CB /* DFGDriver.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FD3C82014115CF800FD81CB /* DFGDriver.cpp */; };
                0FD3C82814115D4F00FD81CB /* DFGDriver.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FD3C82214115D0E00FD81CB /* DFGDriver.h */; };
+               0FD3E4011B618AAF00C80E1E /* DFGAdaptiveInferredPropertyValueWatchpoint.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FD3E3FF1B618AAF00C80E1E /* DFGAdaptiveInferredPropertyValueWatchpoint.cpp */; };
+               0FD3E4021B618AAF00C80E1E /* DFGAdaptiveInferredPropertyValueWatchpoint.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FD3E4001B618AAF00C80E1E /* DFGAdaptiveInferredPropertyValueWatchpoint.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               0FD3E4091B618B6600C80E1E /* ObjectPropertyCondition.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FD3E4031B618B6600C80E1E /* ObjectPropertyCondition.cpp */; };
+               0FD3E40A1B618B6600C80E1E /* ObjectPropertyCondition.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FD3E4041B618B6600C80E1E /* ObjectPropertyCondition.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               0FD3E40B1B618B6600C80E1E /* ObjectPropertyConditionSet.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FD3E4051B618B6600C80E1E /* ObjectPropertyConditionSet.cpp */; };
+               0FD3E40C1B618B6600C80E1E /* ObjectPropertyConditionSet.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FD3E4061B618B6600C80E1E /* ObjectPropertyConditionSet.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               0FD3E40D1B618B6600C80E1E /* PropertyCondition.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FD3E4071B618B6600C80E1E /* PropertyCondition.cpp */; };
+               0FD3E40E1B618B6600C80E1E /* PropertyCondition.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FD3E4081B618B6600C80E1E /* PropertyCondition.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0FD81AD2154FB4EE00983E72 /* DFGDominators.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FD81ACF154FB4EB00983E72 /* DFGDominators.cpp */; };
                0FD81AD3154FB4F000983E72 /* DFGDominators.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FD81AD0154FB4EB00983E72 /* DFGDominators.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0FD82E2114172CE300179C94 /* DFGCapabilities.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FD82E1E14172C2F00179C94 /* DFGCapabilities.cpp */; };
                0FF0F19E16B72A0B005DF95B /* DFGEdge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FB4B51B16B62772003F696B /* DFGEdge.cpp */; };
                0FF0F19F16B72A17005DF95B /* FunctionExecutableDump.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FB4B52116B6278D003F696B /* FunctionExecutableDump.cpp */; };
                0FF0F1A016B72A1A005DF95B /* FunctionExecutableDump.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FB4B52216B6278D003F696B /* FunctionExecutableDump.h */; settings = {ATTRIBUTES = (Private, ); }; };
+               0FF2CD5B1B61A4F8004955A8 /* DFGMultiGetByOffsetData.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FF2CD591B61A4F8004955A8 /* DFGMultiGetByOffsetData.cpp */; };
+               0FF2CD5C1B61A4F8004955A8 /* DFGMultiGetByOffsetData.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FF2CD5A1B61A4F8004955A8 /* DFGMultiGetByOffsetData.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0FF42731158EBD54004CB9FF /* Disassembler.h in Headers */ = {isa = PBXBuildFile; fileRef = 0FF4272F158EBD44004CB9FF /* Disassembler.h */; settings = {ATTRIBUTES = (Private, ); }; };
                0FF42732158EBD58004CB9FF /* UDis86Disassembler.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 0FF42730158EBD44004CB9FF /* UDis86Disassembler.cpp */; };
                0FF42740158EBE8B004CB9FF /* udis86_decode.c in Sources */ = {isa = PBXBuildFile; fileRef = 0FF42734158EBD94004CB9FF /* udis86_decode.c */; };
                A784A26411D16622005776AC /* SyntaxChecker.h in Headers */ = {isa = PBXBuildFile; fileRef = A7A7EE7711B98B8D0065A14F /* SyntaxChecker.h */; settings = {ATTRIBUTES = (Private, ); }; };
                A78507D717CBC6FD0011F6E7 /* MapData.h in Headers */ = {isa = PBXBuildFile; fileRef = A78507D517CBC6FD0011F6E7 /* MapData.h */; settings = {ATTRIBUTES = (Private, ); }; };
                A785F6BC18C553FE00F10626 /* SpillRegistersMode.h in Headers */ = {isa = PBXBuildFile; fileRef = A7FF647A18C52E8500B55307 /* SpillRegistersMode.h */; settings = {ATTRIBUTES = (Private, ); }; };
-               A78853F917972629001440E4 /* IntendedStructureChain.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A78853F717972629001440E4 /* IntendedStructureChain.cpp */; };
-               A78853FA17972629001440E4 /* IntendedStructureChain.h in Headers */ = {isa = PBXBuildFile; fileRef = A78853F817972629001440E4 /* IntendedStructureChain.h */; settings = {ATTRIBUTES = (Private, ); }; };
                A78A9774179738B8009DF744 /* DFGFailedFinalizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A78A976C179738B8009DF744 /* DFGFailedFinalizer.cpp */; };
                A78A9775179738B8009DF744 /* DFGFailedFinalizer.h in Headers */ = {isa = PBXBuildFile; fileRef = A78A976D179738B8009DF744 /* DFGFailedFinalizer.h */; };
                A78A9776179738B8009DF744 /* DFGFinalizer.cpp in Sources */ = {isa = PBXBuildFile; fileRef = A78A976E179738B8009DF744 /* DFGFinalizer.cpp */; };
                0F13E04C16164A1B00DC8DE7 /* IndexingType.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IndexingType.cpp; sourceTree = "<group>"; };
                0F15F15D14B7A73A005DE37D /* CommonSlowPaths.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CommonSlowPaths.h; sourceTree = "<group>"; };
                0F1725FE1B48719A00AC3A55 /* DFGMinifiedGraph.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGMinifiedGraph.cpp; path = dfg/DFGMinifiedGraph.cpp; sourceTree = "<group>"; };
+               0F18D3CD1B55A6E0002C5C9F /* DFGAdaptiveStructureWatchpoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGAdaptiveStructureWatchpoint.cpp; path = dfg/DFGAdaptiveStructureWatchpoint.cpp; sourceTree = "<group>"; };
+               0F18D3CE1B55A6E0002C5C9F /* DFGAdaptiveStructureWatchpoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGAdaptiveStructureWatchpoint.h; path = dfg/DFGAdaptiveStructureWatchpoint.h; sourceTree = "<group>"; };
                0F190CAA189D82F6000AE5F0 /* ProfilerJettisonReason.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ProfilerJettisonReason.cpp; path = profiler/ProfilerJettisonReason.cpp; sourceTree = "<group>"; };
                0F190CAB189D82F6000AE5F0 /* ProfilerJettisonReason.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = ProfilerJettisonReason.h; path = profiler/ProfilerJettisonReason.h; sourceTree = "<group>"; };
                0F1DD84918A945BE0026F3FA /* JSCInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSCInlines.h; sourceTree = "<group>"; };
                0F3B3A2A15474FF4003ED0FF /* DFGValidate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGValidate.h; path = dfg/DFGValidate.h; sourceTree = "<group>"; };
                0F3B7E2419A11B8000D9BC56 /* CallVariant.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CallVariant.cpp; sourceTree = "<group>"; };
                0F3B7E2519A11B8000D9BC56 /* CallVariant.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CallVariant.h; sourceTree = "<group>"; };
-               0F3D0BBA194A414300FC9CF9 /* ConstantStructureCheck.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ConstantStructureCheck.cpp; sourceTree = "<group>"; };
-               0F3D0BBB194A414300FC9CF9 /* ConstantStructureCheck.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ConstantStructureCheck.h; sourceTree = "<group>"; };
                0F3E01A819D353A500F61B7F /* DFGPrePostNumbering.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGPrePostNumbering.cpp; path = dfg/DFGPrePostNumbering.cpp; sourceTree = "<group>"; };
                0F3E01A919D353A500F61B7F /* DFGPrePostNumbering.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGPrePostNumbering.h; path = dfg/DFGPrePostNumbering.h; sourceTree = "<group>"; };
                0F426A451460CBAB00131F8F /* ValueRecovery.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ValueRecovery.h; sourceTree = "<group>"; };
                0FD2C92316D01EE900C7803F /* StructureInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StructureInlines.h; sourceTree = "<group>"; };
                0FD3C82014115CF800FD81CB /* DFGDriver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGDriver.cpp; path = dfg/DFGDriver.cpp; sourceTree = "<group>"; };
                0FD3C82214115D0E00FD81CB /* DFGDriver.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGDriver.h; path = dfg/DFGDriver.h; sourceTree = "<group>"; };
+               0FD3E3FF1B618AAF00C80E1E /* DFGAdaptiveInferredPropertyValueWatchpoint.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGAdaptiveInferredPropertyValueWatchpoint.cpp; path = dfg/DFGAdaptiveInferredPropertyValueWatchpoint.cpp; sourceTree = "<group>"; };
+               0FD3E4001B618AAF00C80E1E /* DFGAdaptiveInferredPropertyValueWatchpoint.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGAdaptiveInferredPropertyValueWatchpoint.h; path = dfg/DFGAdaptiveInferredPropertyValueWatchpoint.h; sourceTree = "<group>"; };
+               0FD3E4031B618B6600C80E1E /* ObjectPropertyCondition.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ObjectPropertyCondition.cpp; sourceTree = "<group>"; };
+               0FD3E4041B618B6600C80E1E /* ObjectPropertyCondition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjectPropertyCondition.h; sourceTree = "<group>"; };
+               0FD3E4051B618B6600C80E1E /* ObjectPropertyConditionSet.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ObjectPropertyConditionSet.cpp; sourceTree = "<group>"; };
+               0FD3E4061B618B6600C80E1E /* ObjectPropertyConditionSet.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ObjectPropertyConditionSet.h; sourceTree = "<group>"; };
+               0FD3E4071B618B6600C80E1E /* PropertyCondition.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PropertyCondition.cpp; sourceTree = "<group>"; };
+               0FD3E4081B618B6600C80E1E /* PropertyCondition.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PropertyCondition.h; sourceTree = "<group>"; };
                0FD5652216AB780A00197653 /* DFGBasicBlockInlines.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGBasicBlockInlines.h; path = dfg/DFGBasicBlockInlines.h; sourceTree = "<group>"; };
                0FD81ACF154FB4EB00983E72 /* DFGDominators.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGDominators.cpp; path = dfg/DFGDominators.cpp; sourceTree = "<group>"; };
                0FD81AD0154FB4EB00983E72 /* DFGDominators.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGDominators.h; path = dfg/DFGDominators.h; sourceTree = "<group>"; };
                0FEFC9A81681A3B000567F53 /* DFGOSRExitJumpPlaceholder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGOSRExitJumpPlaceholder.h; path = dfg/DFGOSRExitJumpPlaceholder.h; sourceTree = "<group>"; };
                0FF054F71AC35B4400E5BE57 /* ExecutableAllocationFuzz.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ExecutableAllocationFuzz.cpp; sourceTree = "<group>"; };
                0FF054F81AC35B4400E5BE57 /* ExecutableAllocationFuzz.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ExecutableAllocationFuzz.h; sourceTree = "<group>"; };
+               0FF2CD591B61A4F8004955A8 /* DFGMultiGetByOffsetData.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGMultiGetByOffsetData.cpp; path = dfg/DFGMultiGetByOffsetData.cpp; sourceTree = "<group>"; };
+               0FF2CD5A1B61A4F8004955A8 /* DFGMultiGetByOffsetData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGMultiGetByOffsetData.h; path = dfg/DFGMultiGetByOffsetData.h; sourceTree = "<group>"; };
                0FF4272F158EBD44004CB9FF /* Disassembler.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Disassembler.h; path = disassembler/Disassembler.h; sourceTree = "<group>"; };
                0FF42730158EBD44004CB9FF /* UDis86Disassembler.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UDis86Disassembler.cpp; path = disassembler/UDis86Disassembler.cpp; sourceTree = "<group>"; };
                0FF42734158EBD94004CB9FF /* udis86_decode.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; name = udis86_decode.c; path = disassembler/udis86/udis86_decode.c; sourceTree = "<group>"; };
                A77F1820164088B200640A47 /* CodeCache.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CodeCache.h; sourceTree = "<group>"; };
                A77F18241641925400640A47 /* ParserModes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ParserModes.h; sourceTree = "<group>"; };
                A78507D517CBC6FD0011F6E7 /* MapData.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MapData.h; sourceTree = "<group>"; };
-               A78853F717972629001440E4 /* IntendedStructureChain.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = IntendedStructureChain.cpp; sourceTree = "<group>"; };
-               A78853F817972629001440E4 /* IntendedStructureChain.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = IntendedStructureChain.h; sourceTree = "<group>"; };
                A78A976C179738B8009DF744 /* DFGFailedFinalizer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGFailedFinalizer.cpp; path = dfg/DFGFailedFinalizer.cpp; sourceTree = "<group>"; };
                A78A976D179738B8009DF744 /* DFGFailedFinalizer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = DFGFailedFinalizer.h; path = dfg/DFGFailedFinalizer.h; sourceTree = "<group>"; };
                A78A976E179738B8009DF744 /* DFGFinalizer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = DFGFinalizer.cpp; path = dfg/DFGFinalizer.cpp; sourceTree = "<group>"; };
                                A7A8AF2B17ADB5F3005AB174 /* Int8Array.h */,
                                A7A8AF2C17ADB5F3005AB174 /* Int16Array.h */,
                                A7A8AF2D17ADB5F3005AB174 /* Int32Array.h */,
-                               A78853F717972629001440E4 /* IntendedStructureChain.cpp */,
-                               A78853F817972629001440E4 /* IntendedStructureChain.h */,
                                BC9BB95B0E19680600DF8855 /* InternalFunction.cpp */,
                                BC11667A0E199C05008066DD /* InternalFunction.h */,
                                A1B9E2331B4E0D6700BC7FED /* IntlCollator.cpp */,
                                A704D8FF17A0BAA8006BA554 /* DFGAbstractInterpreterInlines.h */,
                                0F55C19317276E4600CEABFD /* DFGAbstractValue.cpp */,
                                0F62016F143FCD2F0068B77C /* DFGAbstractValue.h */,
+                               0FD3E3FF1B618AAF00C80E1E /* DFGAdaptiveInferredPropertyValueWatchpoint.cpp */,
+                               0FD3E4001B618AAF00C80E1E /* DFGAdaptiveInferredPropertyValueWatchpoint.h */,
+                               0F18D3CD1B55A6E0002C5C9F /* DFGAdaptiveStructureWatchpoint.cpp */,
+                               0F18D3CE1B55A6E0002C5C9F /* DFGAdaptiveStructureWatchpoint.h */,
                                0F66E16814DF3F1300B7B2E4 /* DFGAdjacencyList.h */,
                                0FB4B51916B62772003F696B /* DFGAllocator.h */,
                                A73781091799EA2E00817533 /* DFGAnalysis.h */,
                                0F2BDC3E1522801700CD8910 /* DFGMinifiedNode.h */,
                                0F8F14311ADF090100ED792C /* DFGMovHintRemovalPhase.cpp */,
                                0F8F14321ADF090100ED792C /* DFGMovHintRemovalPhase.h */,
+                               0FF2CD591B61A4F8004955A8 /* DFGMultiGetByOffsetData.cpp */,
+                               0FF2CD5A1B61A4F8004955A8 /* DFGMultiGetByOffsetData.h */,
                                0FC3CCFA19ADA410006AC72A /* DFGNaiveDominators.cpp */,
                                0FC3CCFB19ADA410006AC72A /* DFGNaiveDominators.h */,
                                A737810A1799EA2E00817533 /* DFGNaturalLoops.cpp */,
                                0F0B83A514BCF50400885B4F /* CodeType.h */,
                                0F6FC74E196110A800E1D02D /* ComplexGetStatus.cpp */,
                                0F6FC74F196110A800E1D02D /* ComplexGetStatus.h */,
-                               0F3D0BBA194A414300FC9CF9 /* ConstantStructureCheck.cpp */,
-                               0F3D0BBB194A414300FC9CF9 /* ConstantStructureCheck.h */,
                                0F426A4A1460CD6B00131F8F /* DataFormat.h */,
                                0FC712DC17CD8778008CC93C /* DeferredCompilationCallback.cpp */,
                                0FC712DD17CD8778008CC93C /* DeferredCompilationCallback.h */,
                                0FB5467C14F5CFD3002C2989 /* MethodOfGettingAValueProfile.cpp */,
                                0FB5467A14F5C7D4002C2989 /* MethodOfGettingAValueProfile.h */,
                                14CA958C16AB50FA00938A06 /* ObjectAllocationProfile.h */,
+                               0FD3E4031B618B6600C80E1E /* ObjectPropertyCondition.cpp */,
+                               0FD3E4041B618B6600C80E1E /* ObjectPropertyCondition.h */,
+                               0FD3E4051B618B6600C80E1E /* ObjectPropertyConditionSet.cpp */,
+                               0FD3E4061B618B6600C80E1E /* ObjectPropertyConditionSet.h */,
                                969A07940ED1D3AE00F1F681 /* Opcode.cpp */,
                                969A07950ED1D3AE00F1F681 /* Opcode.h */,
                                0F2BDC2B151FDE8B00CD8910 /* Operands.h */,
                                0F9FC8C014E1B5FB00D52AE0 /* PolymorphicPutByIdList.h */,
                                0F98205D16BFE37F00240D02 /* PreciseJumpTargets.cpp */,
                                0F98205E16BFE37F00240D02 /* PreciseJumpTargets.h */,
+                               0FD3E4071B618B6600C80E1E /* PropertyCondition.cpp */,
+                               0FD3E4081B618B6600C80E1E /* PropertyCondition.h */,
                                0F93329914CA7DC10085F3C6 /* PutByIdStatus.cpp */,
                                0F93329A14CA7DC10085F3C6 /* PutByIdStatus.h */,
                                0F93B4A718B92C4D00178A3F /* PutByIdVariant.cpp */,
                                C2981FD917BAEE4B00A3BC98 /* DFGDesiredWeakReferences.h in Headers */,
                                0FF427651591A1CE004CB9FF /* DFGDisassembler.h in Headers */,
                                0FD81AD3154FB4F000983E72 /* DFGDominators.h in Headers */,
+                               0FD3E40E1B618B6600C80E1E /* PropertyCondition.h in Headers */,
                                0F1E3A471534CBB9000F9456 /* DFGDoubleFormatState.h in Headers */,
                                0FD3C82814115D4F00FD81CB /* DFGDriver.h in Headers */,
                                0F66E16C14DF3F1600B7B2E4 /* DFGEdge.h in Headers */,
                                A53243981856A489002ED692 /* CombinedDomains.json in Headers */,
                                A532438818568335002ED692 /* InspectorBackendDispatchers.h in Headers */,
                                A532438A18568335002ED692 /* InspectorFrontendDispatchers.h in Headers */,
+                               0FF2CD5C1B61A4F8004955A8 /* DFGMultiGetByOffsetData.h in Headers */,
                                8606DDEA18DA44AB00A383D0 /* IdentifierInlines.h in Headers */,
                                A532438C18568335002ED692 /* InspectorProtocolObjects.h in Headers */,
                                A50E4B6218809DD50068A46D /* InspectorRuntimeAgent.h in Headers */,
                                A7A8AF3B17ADB5F3005AB174 /* Int16Array.h in Headers */,
                                A7A8AF3C17ADB5F3005AB174 /* Int32Array.h in Headers */,
                                A7A8AF3A17ADB5F3005AB174 /* Int8Array.h in Headers */,
-                               A78853FA17972629001440E4 /* IntendedStructureChain.h in Headers */,
                                BC11667B0E199C05008066DD /* InternalFunction.h in Headers */,
                                1429D77C0ED20D7300B89619 /* Interpreter.h in Headers */,
                                860BD801148EA6F200112B2F /* Intrinsic.h in Headers */,
                                BC18C4150E16F5CD00B34460 /* JavaScriptCorePrefix.h in Headers */,
                                1429D9300ED22D7000B89619 /* JIT.h in Headers */,
                                A5EA70E919F5B1010098F5EC /* AlternateDispatchableAgent.h in Headers */,
+                               0F18D3D01B55A6E0002C5C9F /* DFGAdaptiveStructureWatchpoint.h in Headers */,
                                86CCEFDE0F413F8900FD7F9E /* JITCode.h in Headers */,
                                0F0776BF14FF002B00102332 /* JITCompilationEffort.h in Headers */,
                                0FAF7EFE165BA91F000C8455 /* JITDisassembler.h in Headers */,
                                BC18C43C0E16F5CD00B34460 /* MathObject.h in Headers */,
                                90213E3E123A40C200D422F3 /* MemoryStatistics.h in Headers */,
                                0FB5467B14F5C7E1002C2989 /* MethodOfGettingAValueProfile.h in Headers */,
-                               0F3D0BBD194A414300FC9CF9 /* ConstantStructureCheck.h in Headers */,
                                7C008CE7187631B600955C24 /* Microtask.h in Headers */,
                                86C568E211A213EE0007F7F0 /* MIPSAssembler.h in Headers */,
                                BC02E9110E1839DB000F9297 /* NativeErrorConstructor.h in Headers */,
                                2AAD964A18569417001F93BE /* RecursiveAllocationScope.h in Headers */,
                                0FF60AC216740F8300029779 /* ReduceWhitespace.h in Headers */,
                                0FD120301A8AED12000F5280 /* FTLJSCallBase.h in Headers */,
+                               0FD3E40A1B618B6600C80E1E /* ObjectPropertyCondition.h in Headers */,
                                0FA7A8EC18B413C80052371D /* Reg.h in Headers */,
                                BC18C45A0E16F5CD00B34460 /* RegExp.h in Headers */,
                                A1712B3F11C7B228007A5315 /* RegExpCache.h in Headers */,
+                               0FD3E4021B618AAF00C80E1E /* DFGAdaptiveInferredPropertyValueWatchpoint.h in Headers */,
                                4340A4851A9051AF00D73CCA /* MathCommon.h in Headers */,
                                BCD202C20E1706A7002C7E82 /* RegExpConstructor.h in Headers */,
                                BCD202D60E170708002C7E82 /* RegExpConstructor.lut.h in Headers */,
                                0FD2C92416D01EE900C7803F /* StructureInlines.h in Headers */,
                                C2FE18A416BAEC4000AF3061 /* StructureRareData.h in Headers */,
                                C20BA92D16BB1C1500B3AEA2 /* StructureRareDataInlines.h in Headers */,
+                               0FD3E40C1B618B6600C80E1E /* ObjectPropertyConditionSet.h in Headers */,
                                0F2B9CEB19D0BA7D00B1D1B5 /* DFGPhiChildren.h in Headers */,
                                0F9332A514CA7DDD0085F3C6 /* StructureSet.h in Headers */,
                                0F766D3915AE4A1F008F363E /* StructureStubClearingWatchpoint.h in Headers */,
                                0FD8A31917D51F2200CA2C40 /* FTLForOSREntryJITCode.cpp in Sources */,
                                0F25F1AF181635F300522F39 /* FTLInlineCacheSize.cpp in Sources */,
                                0FEA0A281709623B00BB722C /* FTLIntrinsicRepository.cpp in Sources */,
+                               0FD3E40D1B618B6600C80E1E /* PropertyCondition.cpp in Sources */,
                                0FC3CCFF19ADA410006AC72A /* DFGBlockWorklist.cpp in Sources */,
                                0FEA0A0D170513DB00BB722C /* FTLJITCode.cpp in Sources */,
                                A78A9780179738D5009DF744 /* FTLJITFinalizer.cpp in Sources */,
                                0FCEFADF180738C000472CE4 /* FTLLocation.cpp in Sources */,
                                0FEA0A0F170513DB00BB722C /* FTLLowerDFGToLLVM.cpp in Sources */,
                                0FD8A31B17D51F2200CA2C40 /* FTLOSREntry.cpp in Sources */,
+                               0FF2CD5B1B61A4F8004955A8 /* DFGMultiGetByOffsetData.cpp in Sources */,
                                0F235BDC17178E1C00690C7F /* FTLOSRExit.cpp in Sources */,
                                0F235BDF17178E1C00690C7F /* FTLOSRExitCompiler.cpp in Sources */,
                                0FEA0A2A1709629600BB722C /* FTLOutput.cpp in Sources */,
                                A5CEEE14187F3BAD00E55C99 /* InspectorAgent.cpp in Sources */,
                                A593CF86184038CA00BFCE27 /* InspectorAgentRegistry.cpp in Sources */,
                                0F2B9CF619D0BAC100B1D1B5 /* FTLExitTimeObjectMaterialization.cpp in Sources */,
+                               0FD3E4011B618AAF00C80E1E /* DFGAdaptiveInferredPropertyValueWatchpoint.cpp in Sources */,
                                A593CF7C1840360300BFCE27 /* InspectorBackendDispatcher.cpp in Sources */,
                                2AF7382C18BBBF92008A5A37 /* StructureIDTable.cpp in Sources */,
                                A5FD0081189B191A00633231 /* InspectorConsoleAgent.cpp in Sources */,
                                A532438B18568335002ED692 /* InspectorProtocolObjects.cpp in Sources */,
                                A50E4B6118809DD50068A46D /* InspectorRuntimeAgent.cpp in Sources */,
                                A593CF821840377100BFCE27 /* InspectorValues.cpp in Sources */,
-                               A78853F917972629001440E4 /* IntendedStructureChain.cpp in Sources */,
                                147F39CF107EC37600427A48 /* InternalFunction.cpp in Sources */,
                                1429D7D40ED2128200B89619 /* Interpreter.cpp in Sources */,
                                70113D4B1A8DB093003848C4 /* IteratorOperations.cpp in Sources */,
                                BCDD51EB0FB8DF74004A8BDC /* JITOpcodes.cpp in Sources */,
                                A71236E51195F33C00BD2174 /* JITOpcodes32_64.cpp in Sources */,
                                0F6E845A19030BEF00562741 /* DFGVariableAccessData.cpp in Sources */,
+                               0FD3E4091B618B6600C80E1E /* ObjectPropertyCondition.cpp in Sources */,
                                A1587D6D1B4DC14100D69849 /* IntlDateTimeFormat.cpp in Sources */,
                                0F24E54C17EE274900ABB217 /* JITOperations.cpp in Sources */,
                                86CC85C40EE7A89400288682 /* JITPropertyAccess.cpp in Sources */,
                                A503FA1B188E0FB000110F14 /* JSJavaScriptCallFrame.cpp in Sources */,
                                A503FA1D188E0FB000110F14 /* JSJavaScriptCallFramePrototype.cpp in Sources */,
                                14280875107EC13E0013E7B2 /* JSLock.cpp in Sources */,
-                               0F3D0BBC194A414300FC9CF9 /* ConstantStructureCheck.cpp in Sources */,
                                C25D709B16DE99F400FCA6BC /* JSManagedValue.mm in Sources */,
                                A700874117CBE8EB00C3E643 /* JSMap.cpp in Sources */,
                                A74DEF95182D991400522C22 /* JSMapIterator.cpp in Sources */,
                                BCDE3B430E6C832D001453A7 /* Structure.cpp in Sources */,
                                7E4EE70F0EBB7A5B005934AA /* StructureChain.cpp in Sources */,
                                C2F0F2D116BAEEE900187C19 /* StructureRareData.cpp in Sources */,
+                               0FD3E40B1B618B6600C80E1E /* ObjectPropertyConditionSet.cpp in Sources */,
                                7013CA8B1B491A9400CAE613 /* JSJob.cpp in Sources */,
                                0F766D3815AE4A1C008F363E /* StructureStubClearingWatchpoint.cpp in Sources */,
                                BCCF0D0C0EF0B8A500413C8F /* StructureStubInfo.cpp in Sources */,
                                705B41B11A6E501E00716757 /* SymbolPrototype.cpp in Sources */,
                                0F919D2815856773004A4E7D /* SymbolTable.cpp in Sources */,
                                0FC314131814559100033232 /* TempRegisterSet.cpp in Sources */,
+                               0F18D3CF1B55A6E0002C5C9F /* DFGAdaptiveStructureWatchpoint.cpp in Sources */,
                                0FA2C17B17D7CF84009D015F /* TestRunnerUtils.cpp in Sources */,
                                A7386555118697B400540279 /* ThunkGenerators.cpp in Sources */,
                                0F2B670717B6B5AB00A7AE3F /* TypedArrayController.cpp in Sources */,
index 0230927..9f94868 100644 (file)
@@ -340,7 +340,6 @@ void CodeBlock::printGetByIdCacheStatus(PrintStream& out, ExecState* exec, int l
             
             Structure* baseStructure = 0;
             Structure* prototypeStructure = 0;
-            StructureChain* chain = 0;
             PolymorphicGetByIdList* list = 0;
             
             switch (stubInfo.accessType) {
@@ -370,11 +369,6 @@ void CodeBlock::printGetByIdCacheStatus(PrintStream& out, ExecState* exec, int l
                 dumpStructure(out, "prototypeStruct", baseStructure, ident);
             }
             
-            if (chain) {
-                out.printf(", ");
-                dumpChain(out, chain, ident);
-            }
-            
             if (list) {
                 out.printf(", list = %p: [", list);
                 for (unsigned i = 0; i < list->size(); ++i) {
@@ -382,9 +376,9 @@ void CodeBlock::printGetByIdCacheStatus(PrintStream& out, ExecState* exec, int l
                         out.printf(", ");
                     out.printf("(");
                     dumpStructure(out, "base", list->at(i).structure(), ident);
-                    if (list->at(i).chain()) {
+                    if (!list->at(i).conditionSet().isEmpty()) {
                         out.printf(", ");
-                        dumpChain(out, list->at(i).chain(), ident);
+                        out.print(list->at(i).conditionSet());
                     }
                     out.printf(")");
                 }
@@ -456,10 +450,8 @@ void CodeBlock::printPutByIdCacheStatus(PrintStream& out, ExecState* exec, int l
                 dumpStructure(out, "prev", stubInfo.u.putByIdTransition.previousStructure.get(), ident);
                 out.print(", ");
                 dumpStructure(out, "next", stubInfo.u.putByIdTransition.structure.get(), ident);
-                if (StructureChain* chain = stubInfo.u.putByIdTransition.chain.get()) {
-                    out.print(", ");
-                    dumpChain(out, chain, ident);
-                }
+                if (stubInfo.u.putByIdTransition.rawConditionSet)
+                    out.print(", ", ObjectPropertyConditionSet::fromRawPointer(stubInfo.u.putByIdTransition.rawConditionSet));
                 break;
             case access_put_by_id_list: {
                 out.printf("list = [");
@@ -483,10 +475,8 @@ void CodeBlock::printPutByIdCacheStatus(PrintStream& out, ExecState* exec, int l
                         dumpStructure(out, "prev", access.oldStructure(), ident);
                         out.print(", ");
                         dumpStructure(out, "next", access.newStructure(), ident);
-                        if (access.chain()) {
-                            out.print(", ");
-                            dumpChain(out, access.chain(), ident);
-                        }
+                        if (!access.conditionSet().isEmpty())
+                            out.print(", ", access.conditionSet());
                     } else
                         out.print("unknown");
                     
index 177b243..f9c6b1e 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -38,9 +38,6 @@ void CodeBlockJettisoningWatchpoint::fireInternal(const FireDetail& detail)
         dataLog("Firing watchpoint ", RawPointer(this), " on ", *m_codeBlock, "\n");
 
     m_codeBlock->jettison(Profiler::JettisonDueToUnprofiledWatchpoint, CountReoptimization, &detail);
-
-    if (isOnList())
-        remove();
 }
 
 } // namespace JSC
index d813a3d..d4ea3ba 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -31,8 +31,7 @@
 namespace JSC {
 
 ComplexGetStatus ComplexGetStatus::computeFor(
-    CodeBlock* profiledBlock, Structure* headStructure, StructureChain* chain,
-    unsigned chainCount, UniquedStringImpl* uid)
+    Structure* headStructure, const ObjectPropertyConditionSet& conditionSet, UniquedStringImpl* uid)
 {
     // FIXME: We should assert that we never see a structure that
     // hasImpureGetOwnPropertySlot() but for which we don't
@@ -40,32 +39,34 @@ ComplexGetStatus ComplexGetStatus::computeFor(
     // that, yet.
     // https://bugs.webkit.org/show_bug.cgi?id=131810
     
+    ASSERT(conditionSet.isValid());
+    
     if (headStructure->takesSlowPathInDFGForImpureProperty())
         return takesSlowPath();
     
     ComplexGetStatus result;
     result.m_kind = Inlineable;
     
-    if (chain && chainCount) {
-        result.m_chain = adoptRef(new IntendedStructureChain(
-            profiledBlock, headStructure, chain, chainCount));
+    if (!conditionSet.isEmpty()) {
+        result.m_conditionSet = conditionSet;
         
-        if (!result.m_chain->isStillValid())
+        if (!result.m_conditionSet.structuresEnsureValidity())
             return skip();
-        
-        if (headStructure->takesSlowPathInDFGForImpureProperty()
-            || result.m_chain->takesSlowPathInDFGForImpureProperty())
+
+        unsigned numberOfSlotBases =
+            result.m_conditionSet.numberOfConditionsWithKind(PropertyCondition::Presence);
+        RELEASE_ASSERT(numberOfSlotBases <= 1);
+        if (!numberOfSlotBases) {
+            // Currently we don't support misses. That's a bummer.
+            // FIXME: https://bugs.webkit.org/show_bug.cgi?id=133052
             return takesSlowPath();
+        }
+        ObjectPropertyCondition base = result.m_conditionSet.slotBaseCondition();
+        ASSERT(base.kind() == PropertyCondition::Presence);
         
-        JSObject* currentObject = result.m_chain->terminalPrototype();
-        Structure* currentStructure = result.m_chain->last();
-        
-        ASSERT_UNUSED(currentObject, currentObject);
-        
-        result.m_offset = currentStructure->getConcurrently(uid, result.m_attributes);
-    } else {
-        result.m_offset = headStructure->getConcurrently(uid, result.m_attributes);
-    }
+        result.m_offset = base.offset();
+    } else
+        result.m_offset = headStructure->getConcurrently(uid);
     
     if (!isValidOffset(result.m_offset))
         return takesSlowPath();
index 2620405..a06e995 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -26,8 +26,8 @@
 #ifndef ComplexGetStatus_h
 #define ComplexGetStatus_h
 
-#include "IntendedStructureChain.h"
 #include "JSCJSValue.h"
+#include "ObjectPropertyConditionSet.h"
 #include "PropertyOffset.h"
 
 namespace JSC {
@@ -80,7 +80,6 @@ public:
     ComplexGetStatus()
         : m_kind(ShouldSkip)
         , m_offset(invalidOffset)
-        , m_attributes(UINT_MAX)
     {
     }
     
@@ -97,21 +96,16 @@ public:
     }
     
     static ComplexGetStatus computeFor(
-        CodeBlock* profiledBlock, Structure* headStructure, StructureChain* chain,
-        unsigned chainCount, UniquedStringImpl* uid);
+        Structure* headStructure, const ObjectPropertyConditionSet&, UniquedStringImpl* uid);
     
     Kind kind() const { return m_kind; }
-    unsigned attributes() const { return m_attributes; }
-    JSValue specificValue() const { return m_specificValue; }
     PropertyOffset offset() const { return m_offset; }
-    IntendedStructureChain* chain() const { return m_chain.get(); }
+    const ObjectPropertyConditionSet& conditionSet() const { return m_conditionSet; }
     
 private:
     Kind m_kind;
     PropertyOffset m_offset;
-    unsigned m_attributes;
-    JSValue m_specificValue;
-    RefPtr<IntendedStructureChain> m_chain;
+    ObjectPropertyConditionSet m_conditionSet;
 };
 
 } // namespace JSC
index 7afaf92..1d97c9f 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -169,8 +169,7 @@ GetByIdStatus GetByIdStatus::computeForStubInfo(
             Structure* structure = list->at(listIndex).structure();
             
             ComplexGetStatus complexGetStatus = ComplexGetStatus::computeFor(
-                profiledBlock, structure, list->at(listIndex).chain(),
-                list->at(listIndex).chainCount(), uid);
+                structure, list->at(listIndex).conditionSet(), uid);
              
             switch (complexGetStatus.kind()) {
             case ComplexGetStatus::ShouldSkip:
@@ -206,8 +205,8 @@ GetByIdStatus GetByIdStatus::computeForStubInfo(
                 }
                  
                 GetByIdVariant variant(
-                    StructureSet(structure), complexGetStatus.offset(), complexGetStatus.chain(),
-                    WTF::move(callLinkStatus));
+                    StructureSet(structure), complexGetStatus.offset(),
+                    complexGetStatus.conditionSet(), WTF::move(callLinkStatus));
                  
                 if (!result.appendVariant(variant))
                     return GetByIdStatus(slowPathState, true);
index dd8e8df..ea6fa12 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -34,23 +34,19 @@ namespace JSC {
 
 GetByIdVariant::GetByIdVariant(
     const StructureSet& structureSet, PropertyOffset offset,
-    const IntendedStructureChain* chain, std::unique_ptr<CallLinkStatus> callLinkStatus)
+    const ObjectPropertyConditionSet& conditionSet,
+    std::unique_ptr<CallLinkStatus> callLinkStatus)
     : m_structureSet(structureSet)
-    , m_alternateBase(nullptr)
+    , m_conditionSet(conditionSet)
     , m_offset(offset)
     , m_callLinkStatus(WTF::move(callLinkStatus))
 {
     if (!structureSet.size()) {
         ASSERT(offset == invalidOffset);
-        ASSERT(!chain);
-    }
-    
-    if (chain && chain->size()) {
-        m_alternateBase = chain->terminalPrototype();
-        chain->gatherChecks(m_constantChecks);
+        ASSERT(conditionSet.isEmpty());
     }
 }
-
+                     
 GetByIdVariant::~GetByIdVariant() { }
 
 GetByIdVariant::GetByIdVariant(const GetByIdVariant& other)
@@ -62,8 +58,7 @@ GetByIdVariant::GetByIdVariant(const GetByIdVariant& other)
 GetByIdVariant& GetByIdVariant::operator=(const GetByIdVariant& other)
 {
     m_structureSet = other.m_structureSet;
-    m_constantChecks = other.m_constantChecks;
-    m_alternateBase = other.m_alternateBase;
+    m_conditionSet = other.m_conditionSet;
     m_offset = other.m_offset;
     if (other.m_callLinkStatus)
         m_callLinkStatus = std::make_unique<CallLinkStatus>(*other.m_callLinkStatus);
@@ -72,28 +67,24 @@ GetByIdVariant& GetByIdVariant::operator=(const GetByIdVariant& other)
     return *this;
 }
 
-StructureSet GetByIdVariant::baseStructure() const
-{
-    if (!m_alternateBase)
-        return structureSet();
-    
-    Structure* structure = structureFor(m_constantChecks, m_alternateBase);
-    RELEASE_ASSERT(structure);
-    return structure;
-}
-
 bool GetByIdVariant::attemptToMerge(const GetByIdVariant& other)
 {
-    if (m_alternateBase != other.m_alternateBase)
-        return false;
     if (m_offset != other.m_offset)
         return false;
     if (m_callLinkStatus || other.m_callLinkStatus)
         return false;
-    if (!areCompatible(m_constantChecks, other.m_constantChecks))
+
+    if (m_conditionSet.isEmpty() != other.m_conditionSet.isEmpty())
         return false;
     
-    mergeInto(other.m_constantChecks, m_constantChecks);
+    ObjectPropertyConditionSet mergedConditionSet;
+    if (!m_conditionSet.isEmpty()) {
+        mergedConditionSet = m_conditionSet.mergedWith(other.m_conditionSet);
+        if (!mergedConditionSet.isValid() || !mergedConditionSet.hasOneSlotBaseCondition())
+            return false;
+    }
+    m_conditionSet = mergedConditionSet;
+    
     m_structureSet.merge(other.m_structureSet);
     
     return true;
@@ -112,10 +103,7 @@ void GetByIdVariant::dumpInContext(PrintStream& out, DumpContext* context) const
     }
     
     out.print(
-        "<", inContext(structureSet(), context), ", ",
-        "[", listDumpInContext(m_constantChecks, context), "]");
-    if (m_alternateBase)
-        out.print(", alternateBase = ", inContext(JSValue(m_alternateBase), context));
+        "<", inContext(structureSet(), context), ", ", inContext(m_conditionSet, context));
     out.print(", offset = ", offset());
     if (m_callLinkStatus)
         out.print(", call = ", *m_callLinkStatus);
index 8d1ade1..714fb98 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -27,9 +27,8 @@
 #define GetByIdVariant_h
 
 #include "CallLinkStatus.h"
-#include "ConstantStructureCheck.h"
-#include "IntendedStructureChain.h"
 #include "JSCJSValue.h"
+#include "ObjectPropertyConditionSet.h"
 #include "PropertyOffset.h"
 #include "StructureSet.h"
 
@@ -43,7 +42,7 @@ class GetByIdVariant {
 public:
     GetByIdVariant(
         const StructureSet& structureSet = StructureSet(), PropertyOffset offset = invalidOffset,
-        const IntendedStructureChain* chain = nullptr,
+        const ObjectPropertyConditionSet& = ObjectPropertyConditionSet(),
         std::unique_ptr<CallLinkStatus> callLinkStatus = nullptr);
     
     ~GetByIdVariant();
@@ -55,9 +54,10 @@ public:
     bool operator!() const { return !isSet(); }
     const StructureSet& structureSet() const { return m_structureSet; }
     StructureSet& structureSet() { return m_structureSet; }
-    const ConstantStructureCheckVector& constantChecks() const { return m_constantChecks; }
-    JSObject* alternateBase() const { return m_alternateBase; }
-    StructureSet baseStructure() const;
+
+    // A non-empty condition set means that this is a prototype load.
+    const ObjectPropertyConditionSet& conditionSet() const { return m_conditionSet; }
+    
     PropertyOffset offset() const { return m_offset; }
     CallLinkStatus* callLinkStatus() const { return m_callLinkStatus.get(); }
     
@@ -70,8 +70,7 @@ private:
     friend class GetByIdStatus;
     
     StructureSet m_structureSet;
-    ConstantStructureCheckVector m_constantChecks;
-    JSObject* m_alternateBase;
+    ObjectPropertyConditionSet m_conditionSet;
     PropertyOffset m_offset;
     std::unique_ptr<CallLinkStatus> m_callLinkStatus;
 };
diff --git a/Source/JavaScriptCore/bytecode/ObjectPropertyCondition.cpp b/Source/JavaScriptCore/bytecode/ObjectPropertyCondition.cpp
new file mode 100644 (file)
index 0000000..1f153b9
--- /dev/null
@@ -0,0 +1,160 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "config.h"
+#include "ObjectPropertyCondition.h"
+
+#include "JSCInlines.h"
+#include "TrackedReferences.h"
+
+namespace JSC {
+
+void ObjectPropertyCondition::dumpInContext(PrintStream& out, DumpContext* context) const
+{
+    if (!*this) {
+        out.print("<invalid>");
+        return;
+    }
+    
+    out.print("<", inContext(JSValue(m_object), context), ": ", inContext(m_condition, context), ">");
+}
+
+void ObjectPropertyCondition::dump(PrintStream& out) const
+{
+    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());
+}
+
+bool ObjectPropertyCondition::validityRequiresImpurePropertyWatchpoint(Structure* structure) const
+{
+    return m_condition.validityRequiresImpurePropertyWatchpoint(structure);
+}
+
+bool ObjectPropertyCondition::validityRequiresImpurePropertyWatchpoint() const
+{
+    if (!*this)
+        return false;
+    
+    return validityRequiresImpurePropertyWatchpoint(m_object->structure());
+}
+
+bool ObjectPropertyCondition::isStillValid(Structure* structure) const
+{
+    return m_condition.isStillValid(structure, m_object);
+}
+
+bool ObjectPropertyCondition::isStillValid() const
+{
+    if (!*this)
+        return false;
+    
+    return isStillValid(m_object->structure());
+}
+
+bool ObjectPropertyCondition::structureEnsuresValidity(Structure* structure) const
+{
+    return m_condition.isStillValid(structure);
+}
+
+bool ObjectPropertyCondition::structureEnsuresValidity() const
+{
+    if (!*this)
+        return false;
+    
+    return structureEnsuresValidity(m_object->structure());
+}
+
+bool ObjectPropertyCondition::isWatchableAssumingImpurePropertyWatchpoint(
+    Structure* structure, PropertyCondition::WatchabilityEffort effort) const
+{
+    return m_condition.isWatchableAssumingImpurePropertyWatchpoint(structure, m_object, effort);
+}
+
+bool ObjectPropertyCondition::isWatchableAssumingImpurePropertyWatchpoint(
+    PropertyCondition::WatchabilityEffort effort) const
+{
+    if (!*this)
+        return false;
+    
+    return isWatchableAssumingImpurePropertyWatchpoint(m_object->structure(), effort);
+}
+
+bool ObjectPropertyCondition::isWatchable(
+    Structure* structure, PropertyCondition::WatchabilityEffort effort) const
+{
+    return m_condition.isWatchable(structure, m_object, effort);
+}
+
+bool ObjectPropertyCondition::isWatchable(PropertyCondition::WatchabilityEffort effort) const
+{
+    if (!*this)
+        return false;
+    
+    return isWatchable(m_object->structure(), effort);
+}
+
+bool ObjectPropertyCondition::isStillLive() const
+{
+    if (!*this)
+        return false;
+    
+    if (!Heap::isMarked(m_object))
+        return false;
+    
+    return m_condition.isStillLive();
+}
+
+void ObjectPropertyCondition::validateReferences(const TrackedReferences& tracked) const
+{
+    if (!*this)
+        return;
+    
+    tracked.check(m_object);
+    m_condition.validateReferences(tracked);
+}
+
+ObjectPropertyCondition ObjectPropertyCondition::attemptToMakeEquivalenceWithoutBarrier() const
+{
+    PropertyCondition result = condition().attemptToMakeEquivalenceWithoutBarrier(object());
+    if (!result)
+        return ObjectPropertyCondition();
+    return ObjectPropertyCondition(object(), result);
+}
+
+} // namespace JSC
+
diff --git a/Source/JavaScriptCore/bytecode/ObjectPropertyCondition.h b/Source/JavaScriptCore/bytecode/ObjectPropertyCondition.h
new file mode 100644 (file)
index 0000000..372e68a
--- /dev/null
@@ -0,0 +1,268 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef ObjectPropertyCondition_h
+#define ObjectPropertyCondition_h
+
+#include "JSObject.h"
+#include "PropertyCondition.h"
+#include <wtf/HashMap.h>
+
+namespace JSC {
+
+class TrackedReferences;
+
+class ObjectPropertyCondition {
+public:
+    ObjectPropertyCondition()
+        : m_object(nullptr)
+    {
+    }
+    
+    ObjectPropertyCondition(WTF::HashTableDeletedValueType token)
+        : m_object(nullptr)
+        , m_condition(token)
+    {
+    }
+    
+    ObjectPropertyCondition(JSObject* object, const PropertyCondition& condition)
+        : m_object(object)
+        , m_condition(condition)
+    {
+    }
+    
+    static ObjectPropertyCondition presenceWithoutBarrier(
+        JSObject* object, UniquedStringImpl* uid, PropertyOffset offset, unsigned attributes)
+    {
+        ObjectPropertyCondition result;
+        result.m_object = object;
+        result.m_condition = PropertyCondition::presenceWithoutBarrier(uid, offset, attributes); 
+        return result;
+    }
+    
+    static ObjectPropertyCondition presence(
+        VM& vm, JSCell* owner, JSObject* object, UniquedStringImpl* uid, PropertyOffset offset,
+        unsigned attributes)
+    {
+        if (owner)
+            vm.heap.writeBarrier(owner);
+        return presenceWithoutBarrier(object, uid, offset, attributes);
+    }
+
+    // NOTE: The prototype is the storedPrototype, not the prototypeForLookup.
+    static ObjectPropertyCondition absenceWithoutBarrier(
+        JSObject* object, UniquedStringImpl* uid, JSObject* prototype)
+    {
+        ObjectPropertyCondition result;
+        result.m_object = object;
+        result.m_condition = PropertyCondition::absenceWithoutBarrier(uid, prototype);
+        return result;
+    }
+    
+    static ObjectPropertyCondition absence(
+        VM& vm, JSCell* owner, JSObject* object, UniquedStringImpl* uid, JSObject* prototype)
+    {
+        if (owner)
+            vm.heap.writeBarrier(owner);
+        return absenceWithoutBarrier(object, uid, prototype);
+    }
+    
+    static ObjectPropertyCondition absenceOfSetterWithoutBarrier(
+        JSObject* object, UniquedStringImpl* uid, JSObject* prototype)
+    {
+        ObjectPropertyCondition result;
+        result.m_object = object;
+        result.m_condition = PropertyCondition::absenceOfSetterWithoutBarrier(uid, prototype);
+        return result;
+    }
+    
+    static ObjectPropertyCondition absenceOfSetter(
+        VM& vm, JSCell* owner, JSObject* object, UniquedStringImpl* uid, JSObject* prototype)
+    {
+        if (owner)
+            vm.heap.writeBarrier(owner);
+        return absenceOfSetterWithoutBarrier(object, uid, prototype);
+    }
+    
+    static ObjectPropertyCondition equivalenceWithoutBarrier(
+        JSObject* object, UniquedStringImpl* uid, JSValue value)
+    {
+        ObjectPropertyCondition result;
+        result.m_object = object;
+        result.m_condition = PropertyCondition::equivalenceWithoutBarrier(uid, value);
+        return result;
+    }
+    
+    static ObjectPropertyCondition equivalence(
+        VM& vm, JSCell* owner, JSObject* object, UniquedStringImpl* uid, JSValue value)
+    {
+        if (owner)
+            vm.heap.writeBarrier(owner);
+        return equivalenceWithoutBarrier(object, uid, value);
+    }
+    
+    bool operator!() const { return !m_condition; };
+    
+    JSObject* object() const { return m_object; }
+    PropertyCondition condition() const { return m_condition; }
+    
+    PropertyCondition::Kind kind() const { return condition().kind(); }
+    UniquedStringImpl* uid() const { return condition().uid(); }
+    bool hasOffset() const { return condition().hasOffset(); }
+    PropertyOffset offset() const { return condition().offset(); }
+    unsigned hasAttributes() const { return condition().hasAttributes(); }
+    unsigned attributes() const { return condition().attributes(); }
+    bool hasPrototype() const { return condition().hasPrototype(); }
+    JSObject* prototype() const { return condition().prototype(); }
+    bool hasRequiredValue() const { return condition().hasRequiredValue(); }
+    JSValue requiredValue() const { return condition().requiredValue(); }
+    
+    void dumpInContext(PrintStream&, DumpContext*) const;
+    void dump(PrintStream&) const;
+    
+    unsigned hash() const
+    {
+        return WTF::PtrHash<JSObject*>::hash(m_object) ^ m_condition.hash();
+    }
+    
+    bool operator==(const ObjectPropertyCondition& other) const
+    {
+        return m_object == other.m_object
+            && m_condition == other.m_condition;
+    }
+    
+    bool isHashTableDeletedValue() const
+    {
+        return !m_object && m_condition.isHashTableDeletedValue();
+    }
+    
+    // Two conditions are compatible if they are identical or if they speak of different uids or
+    // different objects. If false is returned, you have to decide how to resolve the conflict -
+    // for example if there is a Presence and an Equivalence then in some cases you'll want the
+    // more general of the two while in other cases you'll want the more specific of the two. This
+    // will also return false for contradictions, like Presence and Absence on the same
+    // object/uid. By convention, invalid conditions aren't compatible with anything.
+    bool isCompatibleWith(const ObjectPropertyCondition& other) const
+    {
+        if (!*this || !other)
+            return false;
+        return *this == other || uid() != other.uid() || object() != other.object();
+    }
+    
+    // These validity-checking methods can optionally take a Struture* instead of loading the
+    // Structure* from the object. If you're in the concurrent JIT, then you must use the forms
+    // that take an explicit Structure* because you want the compiler to optimize for the same
+    // structure that you validated (i.e. avoid a TOCTOU race).
+    
+    // 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
+    // isStillValidAccordingToStructure() returned true.
+    bool validityRequiresImpurePropertyWatchpoint(Structure*) const;
+    bool validityRequiresImpurePropertyWatchpoint() const;
+
+    // Checks if the condition still holds. May conservatively return false, if the object and
+    // structure alone don't guarantee the condition. Note that this may return true if the
+    // condition still requires some watchpoints on the object in addition to checking the
+    // structure. If you want to check if the condition holds by using the structure alone,
+    // use structureEnsuresValidity().
+    bool isStillValid(Structure*) const;
+    bool isStillValid() const;
+    
+    // Shorthand for condition().isStillValid(structure).
+    bool structureEnsuresValidity(Structure*) const;
+    bool structureEnsuresValidity() const;
+    
+    // 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*,
+        PropertyCondition::WatchabilityEffort = PropertyCondition::MakeNoChanges) const;
+    bool isWatchableAssumingImpurePropertyWatchpoint(
+        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.
+    bool isWatchable(
+        Structure*,
+        PropertyCondition::WatchabilityEffort = PropertyCondition::MakeNoChanges) const;
+    bool isWatchable(
+        PropertyCondition::WatchabilityEffort = PropertyCondition::MakeNoChanges) const;
+    
+    bool watchingRequiresStructureTransitionWatchpoint() const
+    {
+        return condition().watchingRequiresStructureTransitionWatchpoint();
+    }
+    bool watchingRequiresReplacementWatchpoint() const
+    {
+        return condition().watchingRequiresReplacementWatchpoint();
+    }
+    
+    // This means that the objects involved in this are still live.
+    bool isStillLive() const;
+    
+    void validateReferences(const TrackedReferences&) const;
+
+    bool isValidValueForPresence(JSValue value) const
+    {
+        return condition().isValidValueForPresence(value);
+    }
+
+    ObjectPropertyCondition attemptToMakeEquivalenceWithoutBarrier() const;
+
+private:
+    JSObject* m_object;
+    PropertyCondition m_condition;
+};
+
+struct ObjectPropertyConditionHash {
+    static unsigned hash(const ObjectPropertyCondition& key) { return key.hash(); }
+    static bool equal(
+        const ObjectPropertyCondition& a, const ObjectPropertyCondition& b)
+    {
+        return a == b;
+    }
+    static const bool safeToCompareToEmptyOrDeleted = true;
+};
+
+} // namespace JSC
+
+namespace WTF {
+
+template<typename T> struct DefaultHash;
+template<> struct DefaultHash<JSC::ObjectPropertyCondition> {
+    typedef JSC::ObjectPropertyConditionHash Hash;
+};
+
+template<typename T> struct HashTraits;
+template<> struct HashTraits<JSC::ObjectPropertyCondition> : SimpleClassHashTraits<JSC::ObjectPropertyCondition> { };
+
+} // namespace WTF
+
+#endif // ObjectPropertyCondition_h
+
diff --git a/Source/JavaScriptCore/bytecode/ObjectPropertyConditionSet.cpp b/Source/JavaScriptCore/bytecode/ObjectPropertyConditionSet.cpp
new file mode 100644 (file)
index 0000000..8186065
--- /dev/null
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "config.h"
+#include "ObjectPropertyConditionSet.h"
+
+#include "JSCInlines.h"
+#include <wtf/ListDump.h>
+
+namespace JSC {
+
+ObjectPropertyCondition ObjectPropertyConditionSet::forObject(JSObject* object) const
+{
+    for (const ObjectPropertyCondition& condition : *this) {
+        if (condition.object() == object)
+            return condition;
+    }
+    return ObjectPropertyCondition();
+}
+
+ObjectPropertyCondition ObjectPropertyConditionSet::forConditionKind(
+    PropertyCondition::Kind kind) const
+{
+    for (const ObjectPropertyCondition& condition : *this) {
+        if (condition.kind() == kind)
+            return condition;
+    }
+    return ObjectPropertyCondition();
+}
+
+unsigned ObjectPropertyConditionSet::numberOfConditionsWithKind(PropertyCondition::Kind kind) const
+{
+    unsigned result = 0;
+    for (const ObjectPropertyCondition& condition : *this) {
+        if (condition.kind() == kind)
+            result++;
+    }
+    return result;
+}
+
+bool ObjectPropertyConditionSet::hasOneSlotBaseCondition() const
+{
+    return numberOfConditionsWithKind(PropertyCondition::Presence) == 1;
+}
+
+ObjectPropertyCondition ObjectPropertyConditionSet::slotBaseCondition() const
+{
+    ObjectPropertyCondition result;
+    unsigned numFound = 0;
+    for (const ObjectPropertyCondition& condition : *this) {
+        if (condition.kind() == PropertyCondition::Presence) {
+            result = condition;
+            numFound++;
+        }
+    }
+    RELEASE_ASSERT(numFound == 1);
+    return result;
+}
+
+ObjectPropertyConditionSet ObjectPropertyConditionSet::mergedWith(
+    const ObjectPropertyConditionSet& other) const
+{
+    if (!isValid() || !other.isValid())
+        return invalid();
+
+    Vector<ObjectPropertyCondition> result;
+    
+    if (!isEmpty())
+        result.appendVector(m_data->vector);
+    
+    for (const ObjectPropertyCondition& newCondition : other) {
+        for (const ObjectPropertyCondition& existingCondition : *this) {
+            if (newCondition == existingCondition)
+                continue;
+            if (!newCondition.isCompatibleWith(existingCondition))
+                return invalid();
+            result.append(newCondition);
+        }
+    }
+
+    return create(result);
+}
+
+bool ObjectPropertyConditionSet::structuresEnsureValidity() const
+{
+    if (!isValid())
+        return false;
+    
+    for (const ObjectPropertyCondition& condition : *this) {
+        if (!condition.structureEnsuresValidity())
+            return false;
+    }
+    return true;
+}
+
+bool ObjectPropertyConditionSet::structuresEnsureValidityAssumingImpurePropertyWatchpoint() const
+{
+    if (!isValid())
+        return false;
+    
+    for (const ObjectPropertyCondition& condition : *this) {
+        if (!condition.structureEnsuresValidityAssumingImpurePropertyWatchpoint())
+            return false;
+    }
+    return true;
+}
+
+bool ObjectPropertyConditionSet::needImpurePropertyWatchpoint() const
+{
+    for (const ObjectPropertyCondition& condition : *this) {
+        if (condition.validityRequiresImpurePropertyWatchpoint())
+            return true;
+    }
+    return false;
+}
+
+bool ObjectPropertyConditionSet::areStillLive() const
+{
+    for (const ObjectPropertyCondition& condition : *this) {
+        if (!condition.isStillLive())
+            return false;
+    }
+    return true;
+}
+
+void ObjectPropertyConditionSet::dumpInContext(PrintStream& out, DumpContext* context) const
+{
+    if (!isValid()) {
+        out.print("<invalid>");
+        return;
+    }
+    
+    out.print("[");
+    if (m_data)
+        out.print(listDumpInContext(m_data->vector, context));
+    out.print("]");
+}
+
+void ObjectPropertyConditionSet::dump(PrintStream& out) const
+{
+    dumpInContext(out, nullptr);
+}
+
+namespace {
+
+bool verbose = false;
+
+ObjectPropertyCondition generateCondition(
+    VM& vm, JSCell* owner, JSObject* object, UniquedStringImpl* uid, PropertyCondition::Kind conditionKind)
+{
+    Structure* structure = object->structure();
+    if (verbose)
+        dataLog("Creating condition ", conditionKind, " for ", pointerDump(structure), "\n");
+
+    ObjectPropertyCondition result;
+    switch (conditionKind) {
+    case PropertyCondition::Presence: {
+        unsigned attributes;
+        PropertyOffset offset = structure->getConcurrently(uid, attributes);
+        if (offset == invalidOffset)
+            return ObjectPropertyCondition();
+        result = ObjectPropertyCondition::presence(vm, owner, object, uid, offset, attributes);
+        break;
+    }
+    case PropertyCondition::Absence: {
+        result = ObjectPropertyCondition::absence(
+            vm, owner, object, uid, object->structure()->storedPrototypeObject());
+        break;
+    }
+    case PropertyCondition::AbsenceOfSetter: {
+        result = ObjectPropertyCondition::absenceOfSetter(
+            vm, owner, object, uid, object->structure()->storedPrototypeObject());
+        break;
+    }
+    default:
+        RELEASE_ASSERT_NOT_REACHED();
+        return ObjectPropertyCondition();
+    }
+
+    if (!result.structureEnsuresValidityAssumingImpurePropertyWatchpoint()) {
+        if (verbose)
+            dataLog("Failed to create condition: ", result, "\n");
+        return ObjectPropertyCondition();
+    }
+
+    if (verbose)
+        dataLog("New condition: ", result, "\n");
+    return result;
+}
+
+enum Concurrency {
+    MainThread,
+    Concurrent
+};
+template<typename Functor>
+ObjectPropertyConditionSet generateConditions(
+    VM& vm, JSGlobalObject* globalObject, Structure* structure, JSObject* prototype, const Functor& functor,
+    Concurrency concurrency = MainThread)
+{
+    Vector<ObjectPropertyCondition> conditions;
+    
+    for (;;) {
+        if (verbose)
+            dataLog("Considering structure: ", pointerDump(structure), "\n");
+        
+        if (structure->isProxy()) {
+            if (verbose)
+                dataLog("It's a proxy, so invalid.\n");
+            return ObjectPropertyConditionSet::invalid();
+        }
+        
+        JSValue value = structure->prototypeForLookup(globalObject);
+        
+        if (value.isNull()) {
+            if (!prototype) {
+                if (verbose)
+                    dataLog("Reached end up prototype chain as expected, done.\n");
+                break;
+            }
+            if (verbose)
+                dataLog("Unexpectedly reached end of prototype chain, so invalid.\n");
+            return ObjectPropertyConditionSet::invalid();
+        }
+        
+        JSObject* object = jsCast<JSObject*>(value);
+        structure = object->structure(vm);
+        
+        // Since we're accessing a prototype repeatedly, it's a good bet that it should not be
+        // treated as a dictionary.
+        if (structure->isDictionary()) {
+            if (concurrency == MainThread)
+                structure->flattenDictionaryStructure(vm, object);
+            else {
+                if (verbose)
+                    dataLog("Cannot flatten dictionary when not on main thread, so invalid.\n");
+                return ObjectPropertyConditionSet::invalid();
+            }
+        }
+
+        if (!functor(conditions, object)) {
+            if (verbose)
+                dataLog("Functor failed, invalid.\n");
+            return ObjectPropertyConditionSet::invalid();
+        }
+        
+        if (object == prototype) {
+            if (verbose)
+                dataLog("Reached desired prototype, done.\n");
+            break;
+        }
+    }
+
+    if (verbose)
+        dataLog("Returning conditions: ", listDump(conditions), "\n");
+    return ObjectPropertyConditionSet::create(conditions);
+}
+
+} // anonymous namespace
+
+ObjectPropertyConditionSet generateConditionsForPropertyMiss(
+    VM& vm, JSCell* owner, ExecState* exec, Structure* headStructure, UniquedStringImpl* uid)
+{
+    return generateConditions(
+        vm, exec->lexicalGlobalObject(), headStructure, nullptr,
+        [&] (Vector<ObjectPropertyCondition>& conditions, JSObject* object) -> bool {
+            ObjectPropertyCondition result =
+                generateCondition(vm, owner, object, uid, PropertyCondition::Absence);
+            if (!result)
+                return false;
+            conditions.append(result);
+            return true;
+        });
+}
+
+ObjectPropertyConditionSet generateConditionsForPropertySetterMiss(
+    VM& vm, JSCell* owner, ExecState* exec, Structure* headStructure, UniquedStringImpl* uid)
+{
+    return generateConditions(
+        vm, exec->lexicalGlobalObject(), headStructure, nullptr,
+        [&] (Vector<ObjectPropertyCondition>& conditions, JSObject* object) -> bool {
+            ObjectPropertyCondition result =
+                generateCondition(vm, owner, object, uid, PropertyCondition::AbsenceOfSetter);
+            if (!result)
+                return false;
+            conditions.append(result);
+            return true;
+        });
+}
+
+ObjectPropertyConditionSet generateConditionsForPrototypePropertyHit(
+    VM& vm, JSCell* owner, ExecState* exec, Structure* headStructure, JSObject* prototype,
+    UniquedStringImpl* uid)
+{
+    return generateConditions(
+        vm, exec->lexicalGlobalObject(), headStructure, prototype,
+        [&] (Vector<ObjectPropertyCondition>& conditions, JSObject* object) -> bool {
+            PropertyCondition::Kind kind =
+                object == prototype ? PropertyCondition::Presence : PropertyCondition::Absence;
+            ObjectPropertyCondition result =
+                generateCondition(vm, owner, object, uid, kind);
+            if (!result)
+                return false;
+            conditions.append(result);
+            return true;
+        });
+}
+
+ObjectPropertyConditionSet generateConditionsForPrototypePropertyHitCustom(
+    VM& vm, JSCell* owner, ExecState* exec, Structure* headStructure, JSObject* prototype,
+    UniquedStringImpl* uid)
+{
+    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);
+            if (!result)
+                return false;
+            conditions.append(result);
+            return true;
+        });
+}
+
+ObjectPropertyConditionSet generateConditionsForPropertySetterMissConcurrently(
+    VM& vm, JSGlobalObject* globalObject, Structure* headStructure, UniquedStringImpl* uid)
+{
+    return generateConditions(
+        vm, globalObject, headStructure, nullptr,
+        [&] (Vector<ObjectPropertyCondition>& conditions, JSObject* object) -> bool {
+            ObjectPropertyCondition result =
+                generateCondition(vm, nullptr, object, uid, PropertyCondition::AbsenceOfSetter);
+            if (!result)
+                return false;
+            conditions.append(result);
+            return true;
+        }, Concurrent);
+}
+
+} // namespace JSC
+
diff --git a/Source/JavaScriptCore/bytecode/ObjectPropertyConditionSet.h b/Source/JavaScriptCore/bytecode/ObjectPropertyConditionSet.h
new file mode 100644 (file)
index 0000000..69fdfd1
--- /dev/null
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef ObjectPropertyConditionSet_h
+#define ObjectPropertyConditionSet_h
+
+#include "ObjectPropertyCondition.h"
+#include <wtf/FastMalloc.h>
+#include <wtf/RefCounted.h>
+#include <wtf/Vector.h>
+
+namespace JSC {
+
+// An object property condition set is used to represent the set of additional conditions
+// that need to be met for some heap access to be valid. The set can have the following
+// interesting states:
+//
+// Empty: There are no special conditions that need to be met.
+// Invalid: The heap access is never valid.
+// Non-empty: The heap access is valid if all the ObjectPropertyConditions in the set are valid.
+
+class ObjectPropertyConditionSet {
+public:
+    ObjectPropertyConditionSet() { }
+    
+    static ObjectPropertyConditionSet invalid()
+    {
+        ObjectPropertyConditionSet result;
+        result.m_data = adoptRef(new Data());
+        return result;
+    }
+    
+    static ObjectPropertyConditionSet create(const Vector<ObjectPropertyCondition>& vector)
+    {
+        if (vector.isEmpty())
+            return ObjectPropertyConditionSet();
+        
+        ObjectPropertyConditionSet result;
+        result.m_data = adoptRef(new Data());
+        result.m_data->vector = vector;
+        return result;
+    }
+    
+    bool isValid() const
+    {
+        return !m_data || !m_data->vector.isEmpty();
+    }
+    
+    bool isEmpty() const
+    {
+        return !m_data;
+    }
+    
+    typedef const ObjectPropertyCondition* iterator;
+    
+    iterator begin() const
+    {
+        if (!m_data)
+            return nullptr;
+        return m_data->vector.begin();
+    }
+    iterator end() const
+    {
+        if (!m_data)
+            return nullptr;
+        return m_data->vector.end();
+    }
+    
+    ObjectPropertyCondition forObject(JSObject*) const;
+    ObjectPropertyCondition forConditionKind(PropertyCondition::Kind) const;
+
+    unsigned numberOfConditionsWithKind(PropertyCondition::Kind) const;
+
+    bool hasOneSlotBaseCondition() const;
+    
+    // If this is a condition set for a prototype hit, then this is guaranteed to return the
+    // condition on the prototype itself. This allows you to get the object, offset, and
+    // attributes for the prototype. This will RELEASE_ASSERT that there is exactly one Presence
+    // in the set, and it will return that presence.
+    ObjectPropertyCondition slotBaseCondition() const;
+    
+    // Attempt to create a new condition set by merging this one with the other one. This will
+    // fail if any of the conditions are incompatible with each other. When if fails, it returns
+    // invalid().
+    ObjectPropertyConditionSet mergedWith(const ObjectPropertyConditionSet& other) const;
+    
+    bool structuresEnsureValidity() const;
+    bool structuresEnsureValidityAssumingImpurePropertyWatchpoint() const;
+    
+    bool needImpurePropertyWatchpoint() const;
+    bool areStillLive() const;
+    
+    void dumpInContext(PrintStream&, DumpContext*) const;
+    void dump(PrintStream&) const;
+    
+    // Helpers for using this in a union.
+    void* releaseRawPointer()
+    {
+        return static_cast<void*>(m_data.leakRef());
+    }
+    static ObjectPropertyConditionSet adoptRawPointer(void* rawPointer)
+    {
+        ObjectPropertyConditionSet result;
+        result.m_data = adoptRef(static_cast<Data*>(rawPointer));
+        return result;
+    }
+    static ObjectPropertyConditionSet fromRawPointer(void* rawPointer)
+    {
+        ObjectPropertyConditionSet result;
+        result.m_data = static_cast<Data*>(rawPointer);
+        return result;
+    }
+    
+private:
+    // Internally, this represents Invalid using a pointer to a Data that has an empty vector.
+    
+    // FIXME: This could be made more compact by having it internally use a vector that just has
+    // the non-uid portion of ObjectPropertyCondition, and then requiring that the callers of all
+    // of the APIs supply the uid.
+    
+    class Data : public ThreadSafeRefCounted<Data> {
+        WTF_MAKE_NONCOPYABLE(Data);
+        WTF_MAKE_FAST_ALLOCATED;
+        
+    public:
+        Data() { }
+        
+        Vector<ObjectPropertyCondition> vector;
+    };
+    
+    RefPtr<Data> m_data;
+};
+
+ObjectPropertyConditionSet generateConditionsForPropertyMiss(
+    VM&, JSCell* owner, ExecState*, Structure* headStructure, UniquedStringImpl* uid);
+ObjectPropertyConditionSet generateConditionsForPropertySetterMiss(
+    VM&, JSCell* owner, ExecState*, Structure* headStructure, UniquedStringImpl* uid);
+ObjectPropertyConditionSet generateConditionsForPrototypePropertyHit(
+    VM&, JSCell* owner, ExecState*, Structure* headStructure, JSObject* prototype,
+    UniquedStringImpl* uid);
+ObjectPropertyConditionSet generateConditionsForPrototypePropertyHitCustom(
+    VM&, JSCell* owner, ExecState*, Structure* headStructure, JSObject* prototype,
+    UniquedStringImpl* uid);
+
+ObjectPropertyConditionSet generateConditionsForPropertySetterMissConcurrently(
+    VM&, JSGlobalObject*, Structure* headStructure, UniquedStringImpl* uid);
+
+} // namespace JSC
+
+#endif // ObjectPropertyConditionSet_h
+
index c1e5e67..27e806d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -37,14 +37,12 @@ namespace JSC {
 
 GetByIdAccess::GetByIdAccess(
     VM& vm, JSCell* owner, AccessType type, PassRefPtr<JITStubRoutine> stubRoutine,
-    Structure* structure, StructureChain* chain, unsigned chainCount)
+    Structure* structure, const ObjectPropertyConditionSet& conditionSet)
     : m_type(type)
-    , m_chainCount(chainCount)
     , m_structure(vm, owner, structure)
+    , m_conditionSet(conditionSet)
     , m_stubRoutine(stubRoutine)
 {
-    if (chain)
-        m_chain.set(vm, owner, chain);
 }
 
 GetByIdAccess::~GetByIdAccess()
@@ -71,7 +69,7 @@ bool GetByIdAccess::visitWeak(RepatchBuffer& repatchBuffer) const
 {
     if (m_structure && !Heap::isMarked(m_structure.get()))
         return false;
-    if (m_chain && !Heap::isMarked(m_chain.get()))
+    if (!m_conditionSet.areStillLive())
         return false;
     if (!m_stubRoutine->visitWeak(repatchBuffer))
         return false;
index 4867d43..60476cc 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -30,6 +30,7 @@
 
 #include "CodeOrigin.h"
 #include "MacroAssembler.h"
+#include "ObjectPropertyConditionSet.h"
 #include "Opcode.h"
 #include "Structure.h"
 #include <wtf/Vector.h>
@@ -53,13 +54,12 @@ public:
     
     GetByIdAccess()
         : m_type(Invalid)
-        , m_chainCount(0)
     {
     }
     
     GetByIdAccess(
         VM&, JSCell* owner, AccessType, PassRefPtr<JITStubRoutine>, Structure*,
-        StructureChain* = 0, unsigned chainCount = 0);
+        const ObjectPropertyConditionSet& = ObjectPropertyConditionSet());
     
     ~GetByIdAccess();
     
@@ -72,8 +72,7 @@ public:
     
     Structure* structure() const { return m_structure.get(); }
     
-    StructureChain* chain() const { return m_chain.get(); }
-    unsigned chainCount() const { return m_chainCount; }
+    const ObjectPropertyConditionSet& conditionSet() const { return m_conditionSet; }
     
     JITStubRoutine* stubRoutine() const
     {
@@ -91,9 +90,8 @@ private:
     friend class CodeBlock;
     
     AccessType m_type;
-    unsigned m_chainCount;
     WriteBarrier<Structure> m_structure;
-    WriteBarrier<StructureChain> m_chain;
+    ObjectPropertyConditionSet m_conditionSet;
     RefPtr<JITStubRoutine> m_stubRoutine;
 };
 
index 91b87d4..7eddb86 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2012, 2014, 2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -51,7 +51,8 @@ PutByIdAccess PutByIdAccess::fromStructureStubInfo(StructureStubInfo& stubInfo)
         result.m_type = Transition;
         result.m_oldStructure.copyFrom(stubInfo.u.putByIdTransition.previousStructure);
         result.m_newStructure.copyFrom(stubInfo.u.putByIdTransition.structure);
-        result.m_chain.copyFrom(stubInfo.u.putByIdTransition.chain);
+        result.m_conditionSet = ObjectPropertyConditionSet::adoptRawPointer(
+            stubInfo.u.putByIdTransition.rawConditionSet);
         result.m_stubRoutine = stubInfo.stubRoutine;
         break;
         
@@ -64,6 +65,9 @@ PutByIdAccess PutByIdAccess::fromStructureStubInfo(StructureStubInfo& stubInfo)
 
 bool PutByIdAccess::visitWeak(RepatchBuffer& repatchBuffer) const
 {
+    if (!m_conditionSet.areStillLive())
+        return false;
+    
     switch (m_type) {
     case Replace:
         if (!Heap::isMarked(m_oldStructure.get()))
@@ -74,15 +78,11 @@ bool PutByIdAccess::visitWeak(RepatchBuffer& repatchBuffer) const
             return false;
         if (!Heap::isMarked(m_newStructure.get()))
             return false;
-        if (!Heap::isMarked(m_chain.get()))
-            return false;
         break;
     case Setter:
     case CustomSetter:
         if (!Heap::isMarked(m_oldStructure.get()))
             return false;
-        if (m_chain && !Heap::isMarked(m_chain.get()))
-            return false;
         break;
     default:
         RELEASE_ASSERT_NOT_REACHED();
index 2d35501..02939f3 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2012, 2014, 2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -30,6 +30,7 @@
 
 #include "CodeOrigin.h"
 #include "MacroAssembler.h"
+#include "ObjectPropertyConditionSet.h"
 #include "Opcode.h"
 #include "PutKind.h"
 #include "PutPropertySlot.h"
@@ -53,7 +54,6 @@ public:
     
     PutByIdAccess()
         : m_type(Invalid)
-        , m_chainCount(UINT_MAX)
     {
     }
     
@@ -62,14 +62,14 @@ public:
         JSCell* owner,
         Structure* oldStructure,
         Structure* newStructure,
-        StructureChain* chain,
+        const ObjectPropertyConditionSet& conditionSet,
         PassRefPtr<JITStubRoutine> stubRoutine)
     {
         PutByIdAccess result;
         result.m_type = Transition;
         result.m_oldStructure.set(vm, owner, oldStructure);
         result.m_newStructure.set(vm, owner, newStructure);
-        result.m_chain.set(vm, owner, chain);
+        result.m_conditionSet = conditionSet;
         result.m_customSetter = 0;
         result.m_stubRoutine = stubRoutine;
         return result;
@@ -95,8 +95,7 @@ public:
         JSCell* owner,
         AccessType accessType,
         Structure* structure,
-        StructureChain* chain,
-        unsigned chainCount,
+        const ObjectPropertyConditionSet& conditionSet,
         PutPropertySlot::PutValueFunc customSetter,
         PassRefPtr<JITStubRoutine> stubRoutine)
     {
@@ -104,10 +103,7 @@ public:
         PutByIdAccess result;
         result.m_oldStructure.set(vm, owner, structure);
         result.m_type = accessType;
-        if (chain) {
-            result.m_chain.set(vm, owner, chain);
-            result.m_chainCount = chainCount;
-        }
+        result.m_conditionSet = conditionSet;
         result.m_customSetter = customSetter;
         result.m_stubRoutine = stubRoutine;
         return result;
@@ -146,17 +142,7 @@ public:
         return m_newStructure.get();
     }
     
-    StructureChain* chain() const
-    {
-        ASSERT(isTransition() || isSetter() || isCustom());
-        return m_chain.get();
-    }
-    
-    unsigned chainCount() const
-    {
-        ASSERT(isSetter() || isCustom());
-        return m_chainCount;
-    }
+    const ObjectPropertyConditionSet& conditionSet() const { return m_conditionSet; }
     
     JITStubRoutine* stubRoutine() const
     {
@@ -178,8 +164,7 @@ private:
     AccessType m_type;
     WriteBarrier<Structure> m_oldStructure;
     WriteBarrier<Structure> m_newStructure;
-    WriteBarrier<StructureChain> m_chain;
-    unsigned m_chainCount;
+    ObjectPropertyConditionSet m_conditionSet;
     PutPropertySlot::PutValueFunc m_customSetter;
     RefPtr<JITStubRoutine> m_stubRoutine;
 };
diff --git a/Source/JavaScriptCore/bytecode/PropertyCondition.cpp b/Source/JavaScriptCore/bytecode/PropertyCondition.cpp
new file mode 100644 (file)
index 0000000..8aab4ea
--- /dev/null
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "config.h"
+#include "PropertyCondition.h"
+
+#include "GetterSetter.h"
+#include "JSCInlines.h"
+#include "TrackedReferences.h"
+
+namespace JSC {
+
+static bool verbose = false;
+
+void PropertyCondition::dumpInContext(PrintStream& out, DumpContext* context) const
+{
+    if (!*this) {
+        out.print("<invalid>");
+        return;
+    }
+    
+    out.print(m_kind, " of ", m_uid);
+    switch (m_kind) {
+    case Presence:
+        out.print(" at ", offset(), " with attributes ", attributes());
+        return;
+    case Absence:
+    case AbsenceOfSetter:
+        out.print(" with prototype ", inContext(JSValue(prototype()), context));
+        return;
+    case Equivalence:
+        out.print(" with ", inContext(requiredValue(), context));
+        return;
+    }
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
+void PropertyCondition::dump(PrintStream& out) const
+{
+    dumpInContext(out, nullptr);
+}
+
+bool PropertyCondition::isStillValidAssumingImpurePropertyWatchpoint(
+    Structure* structure, JSObject* base) const
+{
+    if (verbose) {
+        dataLog(
+            "Determining validity of ", *this, " with structure ", pointerDump(structure), " and base ",
+            JSValue(base), " assuming impure property watchpoints are set.\n");
+    }
+    
+    if (!*this) {
+        if (verbose)
+            dataLog("Invalid because unset.\n");
+        return false;
+    }
+    
+    if (!structure->propertyAccessesAreCacheable()) {
+        if (verbose)
+            dataLog("Invalid because accesses are not cacheable.\n");
+        return false;
+    }
+    
+    switch (m_kind) {
+    case Presence: {
+        unsigned currentAttributes;
+        PropertyOffset currentOffset = structure->getConcurrently(uid(), currentAttributes);
+        if (currentOffset != offset() || currentAttributes != attributes()) {
+            if (verbose) {
+                dataLog(
+                    "Invalid because we need offset, attributes to be ", offset(), ", ", attributes(),
+                    " but they are ", currentOffset, ", ", currentAttributes, "\n");
+            }
+            return false;
+        }
+        return true;
+    }
+        
+    case Absence: {
+        if (structure->isDictionary()) {
+            if (verbose)
+                dataLog("Invalid because it's a dictionary.\n");
+            return false;
+        }
+
+        PropertyOffset currentOffset = structure->getConcurrently(uid());
+        if (currentOffset != invalidOffset) {
+            if (verbose)
+                dataLog("Invalid because the property exists at offset: ", currentOffset, "\n");
+            return false;
+        }
+        
+        if (structure->storedPrototypeObject() != prototype()) {
+            if (verbose) {
+                dataLog(
+                    "Invalid because the prototype is ", structure->storedPrototype(), " even though "
+                    "it should have been ", JSValue(prototype()), "\n");
+            }
+            return false;
+        }
+        
+        return true;
+    }
+    
+    case AbsenceOfSetter: {
+        if (structure->isDictionary()) {
+            if (verbose)
+                dataLog("Invalid because it's a dictionary.\n");
+            return false;
+        }
+        
+        unsigned currentAttributes;
+        PropertyOffset currentOffset = structure->getConcurrently(uid(), currentAttributes);
+        if (currentOffset != invalidOffset) {
+            if (currentAttributes & (Accessor | CustomAccessor)) {
+                if (verbose) {
+                    dataLog(
+                        "Invalid because we expected not to have a setter, but we have one at offset ",
+                        currentOffset, " with attributes ", currentAttributes, "\n");
+                }
+                return false;
+            }
+        }
+        
+        if (structure->storedPrototypeObject() != prototype()) {
+            if (verbose) {
+                dataLog(
+                    "Invalid because the prototype is ", structure->storedPrototype(), " even though "
+                    "it should have been ", JSValue(prototype()), "\n");
+            }
+            return false;
+        }
+        
+        return true;
+    }
+        
+    case Equivalence: {
+        if (!base || base->structure() != structure) {
+            // Conservatively return false, since we cannot verify this one without having the
+            // object.
+            if (verbose) {
+                dataLog(
+                    "Invalid because we don't have a base or the base has the wrong structure: ",
+                    RawPointer(base), "\n");
+            }
+            return false;
+        }
+        
+        // FIXME: This is somewhat racy, and maybe more risky than we want.
+        // https://bugs.webkit.org/show_bug.cgi?id=134641
+        
+        PropertyOffset currentOffset = structure->getConcurrently(uid());
+        JSValue currentValue = base->getDirect(currentOffset);
+        if (currentValue != requiredValue()) {
+            if (verbose) {
+                dataLog(
+                    "Invalid because the value is ", currentValue, " but we require ", requiredValue(),
+                    "\n");
+            }
+            return false;
+        }
+        
+        return true;
+    } }
+    
+    RELEASE_ASSERT_NOT_REACHED();
+    return false;
+}
+
+bool PropertyCondition::validityRequiresImpurePropertyWatchpoint(Structure* structure) const
+{
+    if (!*this)
+        return false;
+    
+    switch (m_kind) {
+    case Presence:
+    case Absence:
+    case Equivalence:
+        return structure->needImpurePropertyWatchpoint();
+    default:
+        return false;
+    }
+}
+
+bool PropertyCondition::isStillValid(Structure* structure, JSObject* base) const
+{
+    if (!isStillValidAssumingImpurePropertyWatchpoint(structure, base))
+        return false;
+
+    // Currently we assume that an impure property can cause a property to appear, and can also
+    // "shadow" an existing JS property on the same object. Hence it affects both presence and
+    // absence. It doesn't affect AbsenceOfSetter because impure properties aren't ever setters.
+    switch (m_kind) {
+    case Presence:
+    case Absence:
+    case Equivalence:
+        if (structure->typeInfo().hasImpureGetOwnPropertySlot())
+            return false;
+        break;
+    default:
+        break;
+    }
+    
+    return true;
+}
+
+bool PropertyCondition::isWatchableWhenValid(
+    Structure* structure, WatchabilityEffort effort) const
+{
+    if (structure->transitionWatchpointSetHasBeenInvalidated())
+        return false;
+    
+    switch (m_kind) {
+    case Equivalence: {
+        PropertyOffset offset = structure->getConcurrently(uid());
+        
+        // This method should only be called when some variant of isValid returned true, which
+        // implies that we already confirmed that the structure knows of the property. We should
+        // also have verified that the Structure is a cacheable dictionary, which means we
+        // shouldn't have a TOCTOU race either.
+        RELEASE_ASSERT(offset != invalidOffset);
+        
+        WatchpointSet* set;
+        switch (effort) {
+        case MakeNoChanges:
+            set = structure->propertyReplacementWatchpointSet(offset);
+            break;
+        case EnsureWatchability:
+            set = structure->ensurePropertyReplacementWatchpointSet(
+                *Heap::heap(structure)->vm(), offset);
+            break;
+        }
+        
+        if (!set || !set->isStillValid())
+            return false;
+        
+        break;
+    }
+        
+    default:
+        break;
+    }
+    
+    return true;
+}
+
+bool PropertyCondition::isWatchableAssumingImpurePropertyWatchpoint(
+    Structure* structure, JSObject* base, WatchabilityEffort effort) const
+{
+    return isStillValidAssumingImpurePropertyWatchpoint(structure, base)
+        && isWatchableWhenValid(structure, effort);
+}
+
+bool PropertyCondition::isWatchable(
+    Structure* structure, JSObject* base, WatchabilityEffort effort) const
+{
+    return isStillValid(structure, base)
+        && isWatchableWhenValid(structure, effort);
+}
+
+bool PropertyCondition::isStillLive() const
+{
+    if (hasPrototype() && prototype() && !Heap::isMarked(prototype()))
+        return false;
+    
+    if (hasRequiredValue()
+        && requiredValue()
+        && requiredValue().isCell()
+        && !Heap::isMarked(requiredValue().asCell()))
+        return false;
+    
+    return true;
+}
+
+void PropertyCondition::validateReferences(const TrackedReferences& tracked) const
+{
+    if (hasPrototype())
+        tracked.check(prototype());
+    
+    if (hasRequiredValue())
+        tracked.check(requiredValue());
+}
+
+bool PropertyCondition::isValidValueForAttributes(JSValue value, unsigned attributes)
+{
+    bool attributesClaimAccessor = !!(attributes & Accessor);
+    bool valueClaimsAccessor = !!jsDynamicCast<GetterSetter*>(value);
+    return attributesClaimAccessor == valueClaimsAccessor;
+}
+
+bool PropertyCondition::isValidValueForPresence(JSValue value) const
+{
+    return isValidValueForAttributes(value, attributes());
+}
+
+PropertyCondition PropertyCondition::attemptToMakeEquivalenceWithoutBarrier(JSObject* base) const
+{
+    Structure* structure = base->structure();
+    if (!structure->isValidOffset(offset()))
+        return PropertyCondition();
+    JSValue value = base->getDirect(offset());
+    if (!isValidValueForPresence(value))
+        return PropertyCondition();
+    return equivalenceWithoutBarrier(uid(), value);
+}
+
+} // namespace JSC
+
+namespace WTF {
+
+void printInternal(PrintStream& out, JSC::PropertyCondition::Kind condition)
+{
+    switch (condition) {
+    case JSC::PropertyCondition::Presence:
+        out.print("Presence");
+        return;
+    case JSC::PropertyCondition::Absence:
+        out.print("Absence");
+        return;
+    case JSC::PropertyCondition::AbsenceOfSetter:
+        out.print("Absence");
+        return;
+    case JSC::PropertyCondition::Equivalence:
+        out.print("Equivalence");
+        return;
+    }
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
+} // namespace WTF
diff --git a/Source/JavaScriptCore/bytecode/PropertyCondition.h b/Source/JavaScriptCore/bytecode/PropertyCondition.h
new file mode 100644 (file)
index 0000000..bd08c3b
--- /dev/null
@@ -0,0 +1,338 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef PropertyCondition_h
+#define PropertyCondition_h
+
+#include "JSObject.h"
+#include <wtf/HashMap.h>
+
+namespace JSC {
+
+class TrackedReferences;
+
+class PropertyCondition {
+public:
+    enum Kind {
+        Presence,
+        Absence,
+        AbsenceOfSetter,
+        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.
+    };
+    
+    PropertyCondition()
+        : m_uid(nullptr)
+        , m_kind(Presence)
+    {
+        memset(&u, 0, sizeof(u));
+    }
+    
+    PropertyCondition(WTF::HashTableDeletedValueType)
+        : m_uid(nullptr)
+        , m_kind(Absence)
+    {
+        memset(&u, 0, sizeof(u));
+    }
+    
+    static PropertyCondition presenceWithoutBarrier(UniquedStringImpl* uid, PropertyOffset offset, unsigned attributes)
+    {
+        PropertyCondition result;
+        result.m_uid = uid;
+        result.m_kind = Presence;
+        result.u.presence.offset = offset;
+        result.u.presence.attributes = attributes;
+        return result;
+    }
+    
+    static PropertyCondition presence(
+        VM&, JSCell*, UniquedStringImpl* uid, PropertyOffset offset, unsigned attributes)
+    {
+        return presenceWithoutBarrier(uid, offset, attributes);
+    }
+
+    // NOTE: The prototype is the storedPrototype not the prototypeForLookup.
+    static PropertyCondition absenceWithoutBarrier(UniquedStringImpl* uid, JSObject* prototype)
+    {
+        PropertyCondition result;
+        result.m_uid = uid;
+        result.m_kind = Absence;
+        result.u.absence.prototype = prototype;
+        return result;
+    }
+    
+    static PropertyCondition absence(
+        VM& vm, JSCell* owner, UniquedStringImpl* uid, JSObject* prototype)
+    {
+        if (owner)
+            vm.heap.writeBarrier(owner);
+        return absenceWithoutBarrier(uid, prototype);
+    }
+    
+    static PropertyCondition absenceOfSetterWithoutBarrier(
+        UniquedStringImpl* uid, JSObject* prototype)
+    {
+        PropertyCondition result;
+        result.m_uid = uid;
+        result.m_kind = AbsenceOfSetter;
+        result.u.absence.prototype = prototype;
+        return result;
+    }
+    
+    static PropertyCondition absenceOfSetter(
+        VM& vm, JSCell* owner, UniquedStringImpl* uid, JSObject* prototype)
+    {
+        if (owner)
+            vm.heap.writeBarrier(owner);
+        return absenceOfSetterWithoutBarrier(uid, prototype);
+    }
+    
+    static PropertyCondition equivalenceWithoutBarrier(
+        UniquedStringImpl* uid, JSValue value)
+    {
+        PropertyCondition result;
+        result.m_uid = uid;
+        result.m_kind = Equivalence;
+        result.u.equivalence.value = JSValue::encode(value);
+        return result;
+    }
+        
+    static PropertyCondition equivalence(
+        VM& vm, JSCell* owner, UniquedStringImpl* uid, JSValue value)
+    {
+        if (value.isCell() && owner)
+            vm.heap.writeBarrier(owner);
+        return equivalenceWithoutBarrier(uid, value);
+    }
+        
+    bool operator!() const { return !m_uid && m_kind == Presence; };
+    
+    Kind kind() const { return m_kind; }
+    UniquedStringImpl* uid() const { return m_uid; }
+    
+    bool hasOffset() const { return !!*this && m_kind == Presence; };
+    PropertyOffset offset() const
+    {
+        ASSERT(hasOffset());
+        return u.presence.offset;
+    }
+    bool hasAttributes() const { return !!*this && m_kind == Presence; };
+    unsigned attributes() const
+    {
+        ASSERT(hasAttributes());
+        return u.presence.attributes;
+    }
+    
+    bool hasPrototype() const { return !!*this && (m_kind == Absence || m_kind == AbsenceOfSetter); }
+    JSObject* prototype() const
+    {
+        ASSERT(hasPrototype());
+        return u.absence.prototype;
+    }
+    
+    bool hasRequiredValue() const { return !!*this && m_kind == Equivalence; }
+    JSValue requiredValue() const
+    {
+        ASSERT(hasRequiredValue());
+        return JSValue::decode(u.equivalence.value);
+    }
+    
+    void dumpInContext(PrintStream&, DumpContext*) const;
+    void dump(PrintStream&) const;
+    
+    unsigned hash() const
+    {
+        unsigned result = WTF::PtrHash<UniquedStringImpl*>::hash(m_uid) + static_cast<unsigned>(m_kind);
+        switch (m_kind) {
+        case Presence:
+            result ^= u.presence.offset;
+            result ^= u.presence.attributes;
+            break;
+        case Absence:
+        case AbsenceOfSetter:
+            result ^= WTF::PtrHash<JSObject*>::hash(u.absence.prototype);
+            break;
+        case Equivalence:
+            result ^= EncodedJSValueHash::hash(u.equivalence.value);
+            break;
+        }
+        return result;
+    }
+    
+    bool operator==(const PropertyCondition& other) const
+    {
+        if (m_uid != other.m_uid)
+            return false;
+        if (m_kind != other.m_kind)
+            return false;
+        switch (m_kind) {
+        case Presence:
+            return u.presence.offset == other.u.presence.offset
+                && u.presence.attributes == other.u.presence.attributes;
+        case Absence:
+        case AbsenceOfSetter:
+            return u.absence.prototype == other.u.absence.prototype;
+        case Equivalence:
+            return u.equivalence.value == other.u.equivalence.value;
+        }
+        RELEASE_ASSERT_NOT_REACHED();
+        return false;
+    }
+    
+    bool isHashTableDeletedValue() const
+    {
+        return !m_uid && m_kind == Absence;
+    }
+    
+    // Two conditions are compatible if they are identical or if they speak of different uids. If
+    // false is returned, you have to decide how to resolve the conflict - for example if there is
+    // a Presence and an Equivalence then in some cases you'll want the more general of the two
+    // while in other cases you'll want the more specific of the two. This will also return false
+    // for contradictions, like Presence and Absence on the same uid. By convention, invalid
+    // conditions aren't compatible with anything.
+    bool isCompatibleWith(const PropertyCondition& other) const
+    {
+        if (!*this || !other)
+            return false;
+        return *this == other || uid() != other.uid();
+    }
+    
+    // Checks if the object's structure claims that the property won't be intercepted.
+    bool isStillValidAssumingImpurePropertyWatchpoint(Structure*, JSObject* base = nullptr) const;
+    
+    // Returns true if we need an impure property watchpoint to ensure validity even if
+    // isStillValidAccordingToStructure() returned true.
+    bool validityRequiresImpurePropertyWatchpoint(Structure*) const;
+    
+    // Checks if the condition is still valid right now for the given object and structure.
+    // May conservatively return false, if the object and structure alone don't guarantee the
+    // condition. This happens for an Absence condition on an object that may have impure
+    // properties. If the object is not supplied, then a "true" return indicates that checking if
+    // an object has the given structure guarantees the condition still holds. If an object is
+    // supplied, then you may need to use some other watchpoints on the object to guarantee the
+    // condition in addition to the structure check.
+    bool isStillValid(Structure*, JSObject* base = nullptr) const;
+    
+    // In some cases, the condition is not watchable, but could be made watchable by enabling the
+    // appropriate watchpoint. For example, replacement watchpoints are enabled only when some
+    // access is cached on the property in some structure. This is mainly to save space for
+    // dictionary properties or properties that never get very hot. But, it's always safe to
+    // enable watching, provided that this is called from the main thread.
+    enum WatchabilityEffort {
+        // This is the default. It means that we don't change the state of any Structure or
+        // object, and implies that if the property happens not to be watchable then we don't make
+        // it watchable. This is mandatory if calling from a JIT thread. This is also somewhat
+        // preferable when first deciding whether to watch a condition for the first time (i.e.
+        // not from a watchpoint fire that causes us to see if we should adapt), since a
+        // watchpoint not being initialized for watching implies that maybe we don't know enough
+        // yet to make it profitable to watch -- as in, the thing being watched may not have
+        // stabilized yet. We prefer to only assume that a condition will hold if it has been
+        // known to hold for a while already.
+        MakeNoChanges,
+        
+        // Do what it takes to ensure that the property can be watched, if doing so has no
+        // user-observable effect. For now this just means that we will ensure that a property
+        // replacement watchpoint is enabled if it hadn't been enabled already. Do not use this
+        // from JIT threads, since the act of enabling watchpoints is not thread-safe.
+        EnsureWatchability
+    };
+    
+    // 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;
+    
+    // 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;
+    
+    bool watchingRequiresStructureTransitionWatchpoint() const
+    {
+        // Currently, this is required for all of our conditions.
+        return !!*this;
+    }
+    bool watchingRequiresReplacementWatchpoint() const
+    {
+        return !!*this && m_kind == Equivalence;
+    }
+    
+    // This means that the objects involved in this are still live.
+    bool isStillLive() const;
+    
+    void validateReferences(const TrackedReferences&) const;
+
+    static bool isValidValueForAttributes(JSValue value, unsigned attributes);
+
+    bool isValidValueForPresence(JSValue) const;
+
+    PropertyCondition attemptToMakeEquivalenceWithoutBarrier(JSObject* base) const;
+
+private:
+    bool isWatchableWhenValid(Structure*, WatchabilityEffort) const;
+
+    UniquedStringImpl* m_uid;
+    Kind m_kind;
+    union {
+        struct {
+            PropertyOffset offset;
+            unsigned attributes;
+        } presence;
+        struct {
+            JSObject* prototype;
+        } absence;
+        struct {
+            EncodedJSValue value;
+        } equivalence;
+    } u;
+};
+
+struct PropertyConditionHash {
+    static unsigned hash(const PropertyCondition& key) { return key.hash(); }
+    static bool equal(
+        const PropertyCondition& a, const PropertyCondition& b)
+    {
+        return a == b;
+    }
+    static const bool safeToCompareToEmptyOrDeleted = true;
+};
+
+} // namespace JSC
+
+namespace WTF {
+
+void printInternal(PrintStream&, JSC::PropertyCondition::Kind);
+
+template<typename T> struct DefaultHash;
+template<> struct DefaultHash<JSC::PropertyCondition> {
+    typedef JSC::PropertyConditionHash Hash;
+};
+
+template<typename T> struct HashTraits;
+template<> struct HashTraits<JSC::PropertyCondition> : SimpleClassHashTraits<JSC::PropertyCondition> { };
+
+} // namespace WTF
+
+#endif // PropertyCondition_h
+
index 9e219d2..cc5da3b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012, 2013, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2012-2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -90,19 +90,22 @@ PutByIdStatus PutByIdStatus::computeFromLLInt(CodeBlock* profiledBlock, unsigned
         || instruction[0].u.opcode == LLInt::getOpcode(op_put_by_id_transition_normal_out_of_line));
     
     Structure* newStructure = instruction[6].u.structure.get();
-    StructureChain* chain = instruction[7].u.structureChain.get();
-    ASSERT(newStructure);
-    ASSERT(chain);
     
     PropertyOffset offset = newStructure->getConcurrently(uid);
     if (!isValidOffset(offset))
         return PutByIdStatus(NoInformation);
     
-    RefPtr<IntendedStructureChain> intendedChain;
-    if (chain)
-        intendedChain = adoptRef(new IntendedStructureChain(profiledBlock, structure, chain));
+    ObjectPropertyConditionSet conditionSet;
+    if (instruction[0].u.opcode == LLInt::getOpcode(op_put_by_id_transition_normal)
+        || instruction[0].u.opcode == LLInt::getOpcode(op_put_by_id_transition_normal_out_of_line)) {
+        conditionSet =
+            generateConditionsForPropertySetterMissConcurrently(
+                *profiledBlock->vm(), profiledBlock->globalObject(), structure, uid);
+        if (!conditionSet.isValid())
+            return PutByIdStatus(NoInformation);
+    }
     
-    return PutByIdVariant::transition(structure, newStructure, intendedChain.get(), offset);
+    return PutByIdVariant::transition(structure, newStructure, conditionSet, offset);
 }
 
 PutByIdStatus PutByIdStatus::computeFor(CodeBlock* profiledBlock, StubInfoMap& map, unsigned bytecodeIndex, UniquedStringImpl* uid)
@@ -165,16 +168,14 @@ PutByIdStatus PutByIdStatus::computeForStubInfo(
         PropertyOffset offset = 
             stubInfo->u.putByIdTransition.structure->getConcurrently(uid);
         if (isValidOffset(offset)) {
-            RefPtr<IntendedStructureChain> chain;
-            if (stubInfo->u.putByIdTransition.chain) {
-                chain = adoptRef(new IntendedStructureChain(
-                    profiledBlock, stubInfo->u.putByIdTransition.previousStructure.get(),
-                    stubInfo->u.putByIdTransition.chain.get()));
-            }
+            ObjectPropertyConditionSet conditionSet = ObjectPropertyConditionSet::fromRawPointer(
+                stubInfo->u.putByIdTransition.rawConditionSet);
+            if (!conditionSet.structuresEnsureValidity())
+                return PutByIdStatus(TakesSlowPath);
             return PutByIdVariant::transition(
                 stubInfo->u.putByIdTransition.previousStructure.get(),
                 stubInfo->u.putByIdTransition.structure.get(),
-                chain.get(), offset);
+                conditionSet, offset);
         }
         return PutByIdStatus(TakesSlowPath);
     }
@@ -219,15 +220,11 @@ PutByIdStatus PutByIdStatus::computeForStubInfo(
                     access.newStructure()->getConcurrently(uid);
                 if (!isValidOffset(offset))
                     return PutByIdStatus(slowPathState);
-                RefPtr<IntendedStructureChain> chain;
-                if (access.chain()) {
-                    chain = adoptRef(new IntendedStructureChain(
-                        profiledBlock, access.oldStructure(), access.chain()));
-                    if (!chain->isStillValid())
-                        continue;
-                }
+                ObjectPropertyConditionSet conditionSet = access.conditionSet();
+                if (!conditionSet.structuresEnsureValidity())
+                    return PutByIdStatus(slowPathState);
                 variant = PutByIdVariant::transition(
-                    access.oldStructure(), access.newStructure(), chain.get(), offset);
+                    access.oldStructure(), access.newStructure(), conditionSet, offset);
                 break;
             }
                 
@@ -235,7 +232,7 @@ PutByIdStatus PutByIdStatus::computeForStubInfo(
                 Structure* structure = access.structure();
                 
                 ComplexGetStatus complexGetStatus = ComplexGetStatus::computeFor(
-                    profiledBlock, structure, access.chain(), access.chainCount(), uid);
+                    structure, access.conditionSet(), uid);
                 
                 switch (complexGetStatus.kind()) {
                 case ComplexGetStatus::ShouldSkip:
@@ -253,7 +250,7 @@ PutByIdStatus PutByIdStatus::computeForStubInfo(
                                 locker, profiledBlock, *stub->m_callLinkInfo, callExitSiteData));
                     
                     variant = PutByIdVariant::setter(
-                        structure, complexGetStatus.offset(), complexGetStatus.chain(),
+                        structure, complexGetStatus.offset(), complexGetStatus.conditionSet(),
                         WTF::move(callLinkStatus));
                 } }
                 break;
@@ -368,22 +365,11 @@ PutByIdStatus PutByIdStatus::computeFor(JSGlobalObject* globalObject, const Stru
         if (!structure->typeInfo().isObject())
             return PutByIdStatus(TakesSlowPath);
     
-        RefPtr<IntendedStructureChain> chain;
+        ObjectPropertyConditionSet conditionSet;
         if (!isDirect) {
-            chain = adoptRef(new IntendedStructureChain(globalObject, structure));
-        
-            // If the prototype chain has setters or read-only properties, then give up.
-            if (chain->mayInterceptStoreTo(uid))
-                return PutByIdStatus(TakesSlowPath);
-        
-            // If the prototype chain hasn't been normalized (i.e. there are proxies or dictionaries)
-            // then give up. The dictionary case would only happen if this structure has not been
-            // used in an optimized put_by_id transition. And really the only reason why we would
-            // bail here is that I don't really feel like having the optimizing JIT go and flatten
-            // dictionaries if we have evidence to suggest that those objects were never used as
-            // prototypes in a cacheable prototype access - i.e. there's a good chance that some of
-            // the other checks below will fail.
-            if (structure->isProxy() || !chain->isNormalized())
+            conditionSet = generateConditionsForPropertySetterMissConcurrently(
+                globalObject->vm(), globalObject, structure, uid);
+            if (!conditionSet.isValid())
                 return PutByIdStatus(TakesSlowPath);
         }
     
@@ -394,7 +380,7 @@ PutByIdStatus PutByIdStatus::computeFor(JSGlobalObject* globalObject, const Stru
         ASSERT(isValidOffset(offset));
     
         bool didAppend = result.appendVariant(
-            PutByIdVariant::transition(structure, transition, chain.get(), offset));
+            PutByIdVariant::transition(structure, transition, conditionSet, offset));
         if (!didAppend)
             return PutByIdStatus(TakesSlowPath);
     }
index e9d10df..e1b94ef 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -43,8 +43,7 @@ PutByIdVariant& PutByIdVariant::operator=(const PutByIdVariant& other)
     m_kind = other.m_kind;
     m_oldStructure = other.m_oldStructure;
     m_newStructure = other.m_newStructure;
-    m_constantChecks = other.m_constantChecks;
-    m_alternateBase = other.m_alternateBase;
+    m_conditionSet = other.m_conditionSet;
     m_offset = other.m_offset;
     if (other.m_callLinkStatus)
         m_callLinkStatus = std::make_unique<CallLinkStatus>(*other.m_callLinkStatus);
@@ -64,29 +63,26 @@ PutByIdVariant PutByIdVariant::replace(const StructureSet& structure, PropertyOf
 
 PutByIdVariant PutByIdVariant::transition(
     const StructureSet& oldStructure, Structure* newStructure,
-    const IntendedStructureChain* structureChain, PropertyOffset offset)
+    const ObjectPropertyConditionSet& conditionSet, PropertyOffset offset)
 {
     PutByIdVariant result;
     result.m_kind = Transition;
     result.m_oldStructure = oldStructure;
     result.m_newStructure = newStructure;
-    if (structureChain)
-        structureChain->gatherChecks(result.m_constantChecks);
+    result.m_conditionSet = conditionSet;
     result.m_offset = offset;
     return result;
 }
 
 PutByIdVariant PutByIdVariant::setter(
     const StructureSet& structure, PropertyOffset offset,
-    IntendedStructureChain* chain, std::unique_ptr<CallLinkStatus> callLinkStatus)
+    const ObjectPropertyConditionSet& conditionSet,
+    std::unique_ptr<CallLinkStatus> callLinkStatus)
 {
     PutByIdVariant result;
     result.m_kind = Setter;
     result.m_oldStructure = structure;
-    if (chain) {
-        chain->gatherChecks(result.m_constantChecks);
-        result.m_alternateBase = chain->terminalPrototype();
-    }
+    result.m_conditionSet = conditionSet;
     result.m_offset = offset;
     result.m_callLinkStatus = WTF::move(callLinkStatus);
     return result;
@@ -134,18 +130,6 @@ bool PutByIdVariant::makesCalls() const
     return kind() == Setter;
 }
 
-StructureSet PutByIdVariant::baseStructure() const
-{
-    ASSERT(kind() == Setter);
-    
-    if (!m_alternateBase)
-        return structure();
-    
-    Structure* structure = structureFor(m_constantChecks, m_alternateBase);
-    RELEASE_ASSERT(structure);
-    return structure;
-}
-
 bool PutByIdVariant::attemptToMerge(const PutByIdVariant& other)
 {
     if (m_offset != other.m_offset)
@@ -155,8 +139,8 @@ bool PutByIdVariant::attemptToMerge(const PutByIdVariant& other)
     case Replace:
         switch (other.m_kind) {
         case Replace: {
-            ASSERT(m_constantChecks.isEmpty());
-            ASSERT(other.m_constantChecks.isEmpty());
+            ASSERT(m_conditionSet.isEmpty());
+            ASSERT(other.m_conditionSet.isEmpty());
             
             m_oldStructure.merge(other.m_oldStructure);
             return true;
@@ -196,6 +180,7 @@ bool PutByIdVariant::attemptToMergeTransitionWithReplace(const PutByIdVariant& r
     ASSERT(m_offset == replace.m_offset);
     ASSERT(!replace.writesStructures());
     ASSERT(!replace.reallocatesStorage());
+    ASSERT(replace.conditionSet().isEmpty());
     
     // This sort of merging only works when we have one path along which we add a new field which
     // transitions to structure S while the other path was already on structure S. This doesn't
@@ -232,15 +217,13 @@ void PutByIdVariant::dumpInContext(PrintStream& out, DumpContext* context) const
         out.print(
             "<Transition: ", inContext(oldStructure(), context), " -> ",
             pointerDumpInContext(newStructure(), context), ", [",
-            listDumpInContext(constantChecks(), context), "], offset = ", offset(), ">");
+            inContext(m_conditionSet, context), "], offset = ", offset(), ">");
         return;
         
     case Setter:
         out.print(
             "<Setter: ", inContext(structure(), context), ", [",
-            listDumpInContext(constantChecks(), context), "]");
-        if (m_alternateBase)
-            out.print(", alternateBase = ", inContext(JSValue(m_alternateBase), context));
+            inContext(m_conditionSet, context), "]");
         out.print(", offset = ", m_offset);
         out.print(", call = ", *m_callLinkStatus);
         out.print(">");
index 9dfedbd..657cdac 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2014, 2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -26,7 +26,7 @@
 #ifndef PutByIdVariant_h
 #define PutByIdVariant_h
 
-#include "IntendedStructureChain.h"
+#include "ObjectPropertyConditionSet.h"
 #include "PropertyOffset.h"
 #include "StructureSet.h"
 
@@ -46,7 +46,6 @@ public:
     PutByIdVariant()
         : m_kind(NotSet)
         , m_newStructure(nullptr)
-        , m_alternateBase(nullptr)
         , m_offset(invalidOffset)
     {
     }
@@ -54,16 +53,15 @@ public:
     PutByIdVariant(const PutByIdVariant&);
     PutByIdVariant& operator=(const PutByIdVariant&);
 
-    static PutByIdVariant replace(
-        const StructureSet& structure, PropertyOffset offset);
+    static PutByIdVariant replace(const StructureSet&, PropertyOffset);
     
     static PutByIdVariant transition(
         const StructureSet& oldStructure, Structure* newStructure,
-        const IntendedStructureChain* structureChain, PropertyOffset offset);
+        const ObjectPropertyConditionSet&, PropertyOffset);
     
     static PutByIdVariant setter(
-        const StructureSet& structure, PropertyOffset offset,
-        IntendedStructureChain* chain, std::unique_ptr<CallLinkStatus> callLinkStatus);
+        const StructureSet&, PropertyOffset, const ObjectPropertyConditionSet&,
+        std::unique_ptr<CallLinkStatus>);
     
     Kind kind() const { return m_kind; }
     
@@ -76,6 +74,11 @@ public:
         return m_oldStructure;
     }
     
+    const StructureSet& structureSet() const
+    {
+        return structure();
+    }
+    
     const StructureSet& oldStructure() const
     {
         ASSERT(kind() == Transition || kind() == Replace || kind() == Setter);
@@ -100,10 +103,7 @@ public:
     bool reallocatesStorage() const;
     bool makesCalls() const;
     
-    const ConstantStructureCheckVector& constantChecks() const
-    {
-        return m_constantChecks;
-    }
+    const ObjectPropertyConditionSet& conditionSet() const { return m_conditionSet; }
     
     PropertyOffset offset() const
     {
@@ -111,14 +111,6 @@ public:
         return m_offset;
     }
     
-    JSObject* alternateBase() const
-    {
-        ASSERT(kind() == Setter);
-        return m_alternateBase;
-    }
-    
-    StructureSet baseStructure() const;
-    
     CallLinkStatus* callLinkStatus() const
     {
         ASSERT(kind() == Setter);
@@ -136,8 +128,7 @@ private:
     Kind m_kind;
     StructureSet m_oldStructure;
     Structure* m_newStructure;
-    ConstantStructureCheckVector m_constantChecks;
-    JSObject* m_alternateBase;
+    ObjectPropertyConditionSet m_conditionSet;
     PropertyOffset m_offset;
     std::unique_ptr<CallLinkStatus> m_callLinkStatus;
 };
index f08460d..59c088b 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2012, 2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -37,33 +37,46 @@ namespace JSC {
 StructureStubClearingWatchpoint::~StructureStubClearingWatchpoint() { }
 
 StructureStubClearingWatchpoint* StructureStubClearingWatchpoint::push(
+    const ObjectPropertyCondition& key,
     WatchpointsOnStructureStubInfo& holder,
     std::unique_ptr<StructureStubClearingWatchpoint>& head)
 {
-    head = std::make_unique<StructureStubClearingWatchpoint>(holder, WTF::move(head));
+    head = std::make_unique<StructureStubClearingWatchpoint>(key, holder, WTF::move(head));
     return head.get();
 }
 
 void StructureStubClearingWatchpoint::fireInternal(const FireDetail&)
 {
-    // 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.
-    m_holder.codeBlock()->resetStub(*m_holder.stubInfo());
+    if (!m_key || !m_key.isWatchable(PropertyCondition::EnsureWatchability)) {
+        // 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.
+        m_holder.codeBlock()->resetStub(*m_holder.stubInfo());
+        return;
+    }
+
+    if (m_key.kind() == PropertyCondition::Presence) {
+        // If this was a presence condition, let's watch the property for replacements. This is profitable
+        // for the DFG, which will want the replacement set to be valid in order to do constant folding.
+        VM& vm = *Heap::heap(m_key.object())->vm();
+        m_key.object()->structure()->startWatchingPropertyForReplacements(vm, m_key.offset());
+    }
+
+    m_key.object()->structure()->addTransitionWatchpoint(this);
 }
 
 WatchpointsOnStructureStubInfo::~WatchpointsOnStructureStubInfo()
 {
 }
 
-StructureStubClearingWatchpoint* WatchpointsOnStructureStubInfo::addWatchpoint()
+StructureStubClearingWatchpoint* WatchpointsOnStructureStubInfo::addWatchpoint(const ObjectPropertyCondition& key)
 {
-    return StructureStubClearingWatchpoint::push(*this, m_head);
+    return StructureStubClearingWatchpoint::push(key, *this, m_head);
 }
 
 StructureStubClearingWatchpoint* WatchpointsOnStructureStubInfo::ensureReferenceAndAddWatchpoint(
     RefPtr<WatchpointsOnStructureStubInfo>& holderRef, CodeBlock* codeBlock,
-    StructureStubInfo* stubInfo)
+    StructureStubInfo* stubInfo, const ObjectPropertyCondition& key)
 {
     if (!holderRef)
         holderRef = adoptRef(new WatchpointsOnStructureStubInfo(codeBlock, stubInfo));
@@ -72,7 +85,7 @@ StructureStubClearingWatchpoint* WatchpointsOnStructureStubInfo::ensureReference
         ASSERT(holderRef->m_stubInfo == stubInfo);
     }
     
-    return holderRef->addWatchpoint();
+    return holderRef->addWatchpoint(key);
 }
 
 } // namespace JSC
index 528e3ba..abacf31 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2012 Apple Inc. All rights reserved.
+ * Copyright (C) 2012, 2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -26,6 +26,7 @@
 #ifndef StructureStubClearingWatchpoint_h
 #define StructureStubClearingWatchpoint_h
 
+#include "ObjectPropertyCondition.h"
 #include "Watchpoint.h"
 
 #if ENABLE(JIT)
@@ -46,15 +47,11 @@ class StructureStubClearingWatchpoint : public Watchpoint {
     WTF_MAKE_FAST_ALLOCATED;
 public:
     StructureStubClearingWatchpoint(
-        WatchpointsOnStructureStubInfo& holder)
-        : m_holder(holder)
-    {
-    }
-    
-    StructureStubClearingWatchpoint(
+        const ObjectPropertyCondition& key,
         WatchpointsOnStructureStubInfo& holder,
         std::unique_ptr<StructureStubClearingWatchpoint> next)
-        : m_holder(holder)
+        : m_key(key)
+        , m_holder(holder)
         , m_next(WTF::move(next))
     {
     }
@@ -62,6 +59,7 @@ public:
     virtual ~StructureStubClearingWatchpoint();
     
     static StructureStubClearingWatchpoint* push(
+        const ObjectPropertyCondition& key,
         WatchpointsOnStructureStubInfo& holder,
         std::unique_ptr<StructureStubClearingWatchpoint>& head);
 
@@ -69,6 +67,7 @@ protected:
     virtual void fireInternal(const FireDetail&) override;
 
 private:
+    ObjectPropertyCondition m_key;
     WatchpointsOnStructureStubInfo& m_holder;
     std::unique_ptr<StructureStubClearingWatchpoint> m_next;
 };
@@ -83,11 +82,11 @@ public:
     
     ~WatchpointsOnStructureStubInfo();
     
-    StructureStubClearingWatchpoint* addWatchpoint();
+    StructureStubClearingWatchpoint* addWatchpoint(const ObjectPropertyCondition& key);
     
     static StructureStubClearingWatchpoint* ensureReferenceAndAddWatchpoint(
         RefPtr<WatchpointsOnStructureStubInfo>& holderRef,
-        CodeBlock*, StructureStubInfo*);
+        CodeBlock*, StructureStubInfo*, const ObjectPropertyCondition& key);
     
     CodeBlock* codeBlock() const { return m_codeBlock; }
     StructureStubInfo* stubInfo() const { return m_stubInfo; }
index 5ea530c..6db79a0 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2008, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2008, 2014, 2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -48,9 +48,12 @@ void StructureStubInfo::deref()
         delete polymorphicStructures;
         return;
     }
-    case access_get_by_id_self:
     case access_put_by_id_transition_normal:
     case access_put_by_id_transition_direct:
+        ObjectPropertyConditionSet::adoptRawPointer(u.putByIdTransition.rawConditionSet);
+        u.putByIdTransition.rawConditionSet = nullptr;
+        return;
+    case access_get_by_id_self:
     case access_put_by_id_replace:
     case access_unset:
         // These instructions don't have to release any allocated memory
@@ -75,8 +78,9 @@ bool StructureStubInfo::visitWeakReferences(RepatchBuffer& repatchBuffer)
     case access_put_by_id_transition_normal:
     case access_put_by_id_transition_direct:
         if (!Heap::isMarked(u.putByIdTransition.previousStructure.get())
-            || !Heap::isMarked(u.putByIdTransition.structure.get())
-            || !Heap::isMarked(u.putByIdTransition.chain.get()))
+            || !Heap::isMarked(u.putByIdTransition.structure.get()))
+            return false;
+        if (!ObjectPropertyConditionSet::fromRawPointer(u.putByIdTransition.rawConditionSet).areStillLive())
             return false;
         break;
     case access_put_by_id_replace:
index d402ace..e6c1cee 100644 (file)
@@ -30,6 +30,7 @@
 #include "Instruction.h"
 #include "JITStubRoutine.h"
 #include "MacroAssembler.h"
+#include "ObjectPropertyConditionSet.h"
 #include "Opcode.h"
 #include "PolymorphicAccessStructureList.h"
 #include "RegisterSet.h"
@@ -113,7 +114,7 @@ struct StructureStubInfo {
 
     // PutById*
 
-    void initPutByIdTransition(VM& vm, JSCell* owner, Structure* previousStructure, Structure* structure, StructureChain* chain, bool isDirect)
+    void initPutByIdTransition(VM& vm, JSCell* owner, Structure* previousStructure, Structure* structure, ObjectPropertyConditionSet conditionSet, bool isDirect)
     {
         if (isDirect)
             accessType = access_put_by_id_transition_direct;
@@ -122,7 +123,7 @@ struct StructureStubInfo {
 
         u.putByIdTransition.previousStructure.set(vm, owner, previousStructure);
         u.putByIdTransition.structure.set(vm, owner, structure);
-        u.putByIdTransition.chain.set(vm, owner, chain);
+        u.putByIdTransition.rawConditionSet = conditionSet.releaseRawPointer();
     }
 
     void initPutByIdReplace(VM& vm, JSCell* owner, Structure* baseObjectStructure)
@@ -175,10 +176,11 @@ struct StructureStubInfo {
         seen = true;
     }
         
-    StructureStubClearingWatchpoint* addWatchpoint(CodeBlock* codeBlock)
+    StructureStubClearingWatchpoint* addWatchpoint(
+        CodeBlock* codeBlock, const ObjectPropertyCondition& condition = ObjectPropertyCondition())
     {
         return WatchpointsOnStructureStubInfo::ensureReferenceAndAddWatchpoint(
-            watchpoints, codeBlock, this);
+            watchpoints, codeBlock, this, condition);
     }
     
     int8_t accessType;
@@ -217,23 +219,12 @@ struct StructureStubInfo {
             WriteBarrierBase<Structure> baseObjectStructure;
         } getByIdSelf;
         struct {
-            WriteBarrierBase<Structure> baseObjectStructure;
-            WriteBarrierBase<Structure> prototypeStructure;
-            bool isDirect;
-        } getByIdProto;
-        struct {
-            WriteBarrierBase<Structure> baseObjectStructure;
-            WriteBarrierBase<StructureChain> chain;
-            unsigned count : 31;
-            bool isDirect : 1;
-        } getByIdChain;
-        struct {
             PolymorphicGetByIdList* list;
         } getByIdList;
         struct {
             WriteBarrierBase<Structure> previousStructure;
             WriteBarrierBase<Structure> structure;
-            WriteBarrierBase<StructureChain> chain;
+            void* rawConditionSet;
         } putByIdTransition;
         struct {
             WriteBarrierBase<Structure> baseObjectStructure;
index 007fc9a..3734c76 100644 (file)
@@ -1779,7 +1779,10 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
                 // something more subtle?
                 AbstractValue result;
                 for (unsigned i = status.numVariants(); i--;) {
-                    DFG_ASSERT(m_graph, node, !status[i].alternateBase());
+                    // This thing won't give us a variant that involves prototypes. If it did, we'd
+                    // have more work to do here.
+                    DFG_ASSERT(m_graph, node, status[i].conditionSet().isEmpty());
+                    
                     JSValue constantResult =
                         m_graph.tryGetConstantProperty(value, status[i].offset());
                     if (!constantResult) {
@@ -2060,30 +2063,22 @@ bool AbstractInterpreter<AbstractStateType>::executeEffects(unsigned clobberLimi
         AbstractValue base = forNode(node->child1());
         StructureSet baseSet;
         AbstractValue result;
-        for (unsigned i = node->multiGetByOffsetData().variants.size(); i--;) {
-            GetByIdVariant& variant = node->multiGetByOffsetData().variants[i];
-            StructureSet set = variant.structureSet();
+        for (const MultiGetByOffsetCase& getCase : node->multiGetByOffsetData().cases) {
+            StructureSet set = getCase.set();
             set.filter(base);
             if (set.isEmpty())
                 continue;
             baseSet.merge(set);
             
-            JSValue baseForLoad;
-            if (variant.alternateBase())
-                baseForLoad = variant.alternateBase();
-            else
-                baseForLoad = base.m_value;
-            JSValue constantResult =
-                m_graph.tryGetConstantProperty(
-                    baseForLoad, variant.baseStructure(), variant.offset());
-            if (!constantResult) {
+            if (getCase.method().kind() != GetByOffsetMethod::Constant) {
                 result.makeHeapTop();
                 continue;
             }
+            
             AbstractValue thisResult;
             thisResult.set(
                 m_graph,
-                *m_graph.freeze(constantResult),
+                *getCase.method().constant(),
                 m_state.structureClobberState());
             result.merge(thisResult);
         }
diff --git a/Source/JavaScriptCore/dfg/DFGAdaptiveInferredPropertyValueWatchpoint.cpp b/Source/JavaScriptCore/dfg/DFGAdaptiveInferredPropertyValueWatchpoint.cpp
new file mode 100644 (file)
index 0000000..2038a19
--- /dev/null
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "config.h"
+#include "DFGAdaptiveInferredPropertyValueWatchpoint.h"
+
+#if ENABLE(DFG_JIT)
+
+#include "JSCInlines.h"
+
+namespace JSC { namespace DFG {
+
+AdaptiveInferredPropertyValueWatchpoint::AdaptiveInferredPropertyValueWatchpoint(
+    const ObjectPropertyCondition& key,
+    CodeBlock* codeBlock)
+    : m_key(key)
+    , m_codeBlock(codeBlock)
+{
+    RELEASE_ASSERT(key.kind() == PropertyCondition::Equivalence);
+}
+
+void AdaptiveInferredPropertyValueWatchpoint::install()
+{
+    RELEASE_ASSERT(m_key.isWatchable());
+    
+    m_key.object()->structure()->addTransitionWatchpoint(&m_structureWatchpoint);
+    
+    PropertyOffset offset = m_key.object()->structure()->getConcurrently(m_key.uid());
+    WatchpointSet* set = m_key.object()->structure()->propertyReplacementWatchpointSet(offset);
+    set->add(&m_propertyWatchpoint);
+}
+
+void AdaptiveInferredPropertyValueWatchpoint::fire(const FireDetail& detail)
+{
+    // One of the watchpoints fired, but the other one didn't. Make sure that neither of them are
+    // in any set anymore. This simplifies things by allowing us to reinstall the watchpoints
+    // wherever from scratch.
+    if (m_structureWatchpoint.isOnList())
+        m_structureWatchpoint.remove();
+    if (m_propertyWatchpoint.isOnList())
+        m_propertyWatchpoint.remove();
+    
+    if (m_key.isWatchable(PropertyCondition::EnsureWatchability)) {
+        install();
+        return;
+    }
+    
+    if (DFG::shouldShowDisassembly()) {
+        dataLog(
+            "Firing watchpoint ", RawPointer(this), " (", m_key, ") on ", *m_codeBlock, "\n");
+    }
+    
+    StringPrintStream out;
+    out.print("Adaptation of ", m_key, " failed: ", detail);
+    
+    StringFireDetail stringDetail(out.toCString().data());
+    
+    m_codeBlock->jettison(
+        Profiler::JettisonDueToUnprofiledWatchpoint, CountReoptimization, &stringDetail);
+}
+
+void AdaptiveInferredPropertyValueWatchpoint::StructureWatchpoint::fireInternal(
+    const FireDetail& detail)
+{
+    ptrdiff_t myOffset = OBJECT_OFFSETOF(
+        AdaptiveInferredPropertyValueWatchpoint, m_structureWatchpoint);
+    
+    AdaptiveInferredPropertyValueWatchpoint* parent =
+        bitwise_cast<AdaptiveInferredPropertyValueWatchpoint*>(
+            bitwise_cast<char*>(this) - myOffset);
+    
+    parent->fire(detail);
+}
+
+void AdaptiveInferredPropertyValueWatchpoint::PropertyWatchpoint::fireInternal(
+    const FireDetail& detail)
+{
+    ptrdiff_t myOffset = OBJECT_OFFSETOF(
+        AdaptiveInferredPropertyValueWatchpoint, m_propertyWatchpoint);
+    
+    AdaptiveInferredPropertyValueWatchpoint* parent =
+        bitwise_cast<AdaptiveInferredPropertyValueWatchpoint*>(
+            bitwise_cast<char*>(this) - myOffset);
+    
+    parent->fire(detail);
+}
+
+} } // namespace JSC::DFG
+
+#endif // ENABLE(DFG_JIT)
+
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
  */
 
-#ifndef ConstantStructureCheck_h
-#define ConstantStructureCheck_h
+#ifndef DFGAdaptiveInferredPropertyValueWatchpoint_h
+#define DFGAdaptiveInferredPropertyValueWatchpoint_h
 
-#include "DumpContext.h"
-#include "JSCell.h"
-#include "Structure.h"
-#include <wtf/PrintStream.h>
-#include <wtf/Vector.h>
+#if ENABLE(DFG_JIT)
 
-namespace JSC {
+#include "ObjectPropertyCondition.h"
+#include "Watchpoint.h"
+#include <wtf/FastMalloc.h>
+#include <wtf/Noncopyable.h>
+
+namespace JSC { namespace DFG {
+
+class AdaptiveInferredPropertyValueWatchpoint {
+    WTF_MAKE_NONCOPYABLE(AdaptiveInferredPropertyValueWatchpoint);
+    WTF_MAKE_FAST_ALLOCATED;
 
-class ConstantStructureCheck {
 public:
-    ConstantStructureCheck()
-        : m_constant(nullptr)
-        , m_structure(nullptr)
-    {
-    }
+    AdaptiveInferredPropertyValueWatchpoint(const ObjectPropertyCondition&, CodeBlock*);
     
-    ConstantStructureCheck(JSCell* constant, Structure* structure)
-        : m_constant(constant)
-        , m_structure(structure)
-    {
-        ASSERT(!!m_constant == !!m_structure);
-    }
+    const ObjectPropertyCondition& key() const { return m_key; }
     
-    bool operator!() const { return !m_constant; }
-    
-    JSCell* constant() const { return m_constant; }
-    Structure* structure() const { return m_structure; }
+    void install();
+
+private:
+    class StructureWatchpoint : public Watchpoint {
+    public:
+        StructureWatchpoint() { }
+    protected:
+        virtual void fireInternal(const FireDetail&) override;
+    };
+    class PropertyWatchpoint : public Watchpoint {
+    public:
+        PropertyWatchpoint() { }
+    protected:
+        virtual void fireInternal(const FireDetail&) override;
+    };
     
-    void dumpInContext(PrintStream&, DumpContext*) const;
-    void dump(PrintStream&) const;
+    void fire(const FireDetail&);
     
-private:
-    JSCell* m_constant;
-    Structure* m_structure;
+    ObjectPropertyCondition m_key;
+    CodeBlock* m_codeBlock;
+    StructureWatchpoint m_structureWatchpoint;
+    PropertyWatchpoint m_propertyWatchpoint;
 };
 
-typedef Vector<ConstantStructureCheck, 2> ConstantStructureCheckVector;
-
-Structure* structureFor(const ConstantStructureCheckVector& vector, JSCell* constant);
-bool areCompatible(const ConstantStructureCheckVector&, const ConstantStructureCheckVector&);
-void mergeInto(const ConstantStructureCheckVector& source, ConstantStructureCheckVector& target);
+} } // namespace JSC::DFG
 
-} // namespace JSC
+#endif // ENABLE(DFG_JIT)
 
-#endif // ConstantStructureCheck_h
+#endif // DFGAdaptiveInferredPropertyValueWatchpoint_h
 
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
  */
 
 #include "config.h"
-#include "ConstantStructureCheck.h"
+#include "DFGAdaptiveStructureWatchpoint.h"
+
+#if ENABLE(DFG_JIT)
 
 #include "JSCInlines.h"
 
-namespace JSC {
+namespace JSC { namespace DFG {
 
-void ConstantStructureCheck::dumpInContext(PrintStream& out, DumpContext* context) const
+AdaptiveStructureWatchpoint::AdaptiveStructureWatchpoint(
+    const ObjectPropertyCondition& key,
+    CodeBlock* codeBlock)
+    : m_key(key)
+    , m_codeBlock(codeBlock)
 {
-    out.print(
-        "(Check if ", inContext(JSValue(m_constant), context), " has structure ",
-        pointerDumpInContext(m_structure, context), ")");
+    RELEASE_ASSERT(key.watchingRequiresStructureTransitionWatchpoint());
+    RELEASE_ASSERT(!key.watchingRequiresReplacementWatchpoint());
 }
 
-void ConstantStructureCheck::dump(PrintStream& out) const
+void AdaptiveStructureWatchpoint::install()
 {
-    dumpInContext(out, nullptr);
+    RELEASE_ASSERT(m_key.isWatchable());
+    
+    m_key.object()->structure()->addTransitionWatchpoint(this);
 }
 
-Structure* structureFor(const ConstantStructureCheckVector& vector, JSCell* constant)
+void AdaptiveStructureWatchpoint::fireInternal(const FireDetail& detail)
 {
-    for (unsigned i = vector.size(); i--;) {
-        if (vector[i].constant() == constant)
-            return vector[i].structure();
+    if (m_key.isWatchable(PropertyCondition::EnsureWatchability)) {
+        install();
+        return;
     }
-    return nullptr;
-}
-
-bool areCompatible(const ConstantStructureCheckVector& a, const ConstantStructureCheckVector& b)
-{
-    for (unsigned i = a.size(); i--;) {
-        Structure* otherStructure = structureFor(b, a[i].constant());
-        if (!otherStructure)
-            continue;
-        if (a[i].structure() != otherStructure)
-            return false;
+    
+    if (DFG::shouldShowDisassembly()) {
+        dataLog(
+            "Firing watchpoint ", RawPointer(this), " (", m_key, ") on ", *m_codeBlock, "\n");
     }
-    return true;
+    
+    StringPrintStream out;
+    out.print("Adaptation of ", m_key, " failed: ", detail);
+    
+    StringFireDetail stringDetail(out.toCString().data());
+    
+    m_codeBlock->jettison(
+        Profiler::JettisonDueToUnprofiledWatchpoint, CountReoptimization, &stringDetail);
 }
 
-void mergeInto(const ConstantStructureCheckVector& source, ConstantStructureCheckVector& target)
-{
-    for (unsigned i = source.size(); i--;) {
-        if (structureFor(target, source[i].constant()))
-            continue;
-        target.append(source[i]);
-    }
-}
+} } // namespace JSC::DFG
 
-} // namespace JSC
+#endif // ENABLE(DFG_JIT)
 
diff --git a/Source/JavaScriptCore/dfg/DFGAdaptiveStructureWatchpoint.h b/Source/JavaScriptCore/dfg/DFGAdaptiveStructureWatchpoint.h
new file mode 100644 (file)
index 0000000..f153e23
--- /dev/null
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef DFGAdaptiveStructureWatchpoint_h
+#define DFGAdaptiveStructureWatchpoint_h
+
+#if ENABLE(DFG_JIT)
+
+#include "ObjectPropertyCondition.h"
+#include "Watchpoint.h"
+
+namespace JSC { namespace DFG {
+
+class AdaptiveStructureWatchpoint : public Watchpoint {
+public:
+    AdaptiveStructureWatchpoint(const ObjectPropertyCondition&, CodeBlock*);
+    
+    const ObjectPropertyCondition& key() const { return m_key; }
+    
+    void install();
+
+protected:
+    virtual void fireInternal(const FireDetail&) override;
+
+private:
+    ObjectPropertyCondition m_key;
+    CodeBlock* m_codeBlock;
+};
+
+} } // namespace JSC::DFG
+
+#endif // ENABLE(DFG_JIT)
+
+#endif // DFGAdaptiveStructureWatchpoint_h
+
index 295b448..56422b1 100644 (file)
@@ -203,7 +203,27 @@ private:
     template<typename ChecksFunctor>
     bool handleConstantInternalFunction(int resultOperand, InternalFunction*, int registerOffset, int argumentCountIncludingThis, CodeSpecializationKind, const ChecksFunctor& insertChecks);
     Node* handlePutByOffset(Node* base, unsigned identifier, PropertyOffset, Node* value);
-    Node* handleGetByOffset(SpeculatedType, Node* base, const StructureSet&, unsigned identifierNumber, PropertyOffset, NodeType op = GetByOffset);
+    Node* handleGetByOffset(SpeculatedType, Node* base, unsigned identifierNumber, PropertyOffset, NodeType = GetByOffset);
+    Node* handleGetByOffset(SpeculatedType, Node* base, const StructureSet&, unsigned identifierNumber, PropertyOffset, NodeType = GetByOffset);
+    Node* handleGetByOffset(SpeculatedType, Node* base, UniquedStringImpl*, PropertyOffset, NodeType = GetByOffset);
+
+    // Create a presence ObjectPropertyCondition based on some known offset and structure set. Does not
+    // check the validity of the condition, but it may return a null one if it encounters a contradiction.
+    ObjectPropertyCondition presenceLike(
+        JSObject* knownBase, UniquedStringImpl*, PropertyOffset, const StructureSet&);
+    
+    // Attempt to watch the presence of a property. It will watch that the property is present in the same
+    // way as in all of the structures in the set. It may emit code instead of just setting a watchpoint.
+    // Returns true if this all works out.
+    bool checkPresenceLike(JSObject* knownBase, UniquedStringImpl*, PropertyOffset, const StructureSet&);
+    void checkPresenceLike(Node* base, UniquedStringImpl*, PropertyOffset, const StructureSet&);
+    
+    // Works with both GetByIdVariant and the setter form of PutByIdVariant.
+    template<typename VariantType>
+    Node* load(SpeculatedType, Node* base, unsigned identifierNumber, const VariantType&);
+
+    Node* store(Node* base, unsigned identifier, const PutByIdVariant&, Node* value);
+        
     void handleGetById(
         int destinationOperand, SpeculatedType, Node* base, unsigned identifierNumber,
         const GetByIdStatus&);
@@ -212,7 +232,32 @@ private:
     void handlePutById(
         Node* base, unsigned identifierNumber, Node* value, const PutByIdStatus&,
         bool isDirect);
-    void emitChecks(const ConstantStructureCheckVector&);
+    
+    // Either register a watchpoint or emit a check for this condition. Returns false if the
+    // condition no longer holds, and therefore no reasonable check can be emitted.
+    bool check(const ObjectPropertyCondition&);
+    
+    GetByOffsetMethod promoteToConstant(GetByOffsetMethod);
+    
+    // Either register a watchpoint or emit a check for this condition. It must be a Presence
+    // condition. It will attempt to promote a Presence condition to an Equivalence condition.
+    // Emits code for the loaded value that the condition guards, and returns a node containing
+    // the loaded value. Returns null if the condition no longer holds.
+    GetByOffsetMethod planLoad(const ObjectPropertyCondition&);
+    Node* load(SpeculatedType, unsigned identifierNumber, const GetByOffsetMethod&, NodeType = GetByOffset);
+    Node* load(SpeculatedType, const ObjectPropertyCondition&, NodeType = GetByOffset);
+    
+    // Calls check() for each condition in the set: that is, it either emits checks or registers
+    // watchpoints (or a combination of the two) to make the conditions hold. If any of those
+    // conditions are no longer checkable, returns false.
+    bool check(const ObjectPropertyConditionSet&);
+    
+    // Calls check() for those conditions that aren't the slot base, and calls load() for the slot
+    // base. Does a combination of watchpoint registration and check emission to guard the
+    // conditions, and emits code to load the value from the slot base. Returns a node containing
+    // the loaded value. Returns null if any of the conditions were no longer checkable.
+    GetByOffsetMethod planLoad(const ObjectPropertyConditionSet&);
+    Node* load(SpeculatedType, const ObjectPropertyConditionSet&, NodeType = GetByOffset);
 
     void prepareToParseBlock();
     void clearCaches();
@@ -706,6 +751,9 @@ private:
     
     Node* cellConstantWithStructureCheck(JSCell* object, Structure* structure)
     {
+        // FIXME: This should route to emitPropertyCheck, not the other way around. But currently,
+        // this gets no profit from using emitPropertyCheck() since we'll non-adaptively watch the
+        // object's structure as soon as we make it a weakJSCosntant.
         Node* objectNode = weakJSConstant(object);
         addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(structure)), objectNode);
         return objectNode;
@@ -2221,15 +2269,8 @@ bool ByteCodeParser::handleConstantInternalFunction(
     return false;
 }
 
-Node* ByteCodeParser::handleGetByOffset(SpeculatedType prediction, Node* base, const StructureSet& structureSet, unsigned identifierNumber, PropertyOffset offset, NodeType op)
+Node* ByteCodeParser::handleGetByOffset(SpeculatedType prediction, Node* base, unsigned identifierNumber, PropertyOffset offset, NodeType op)
 {
-    if (base->hasConstant()) {
-        if (JSValue constant = m_graph.tryGetConstantProperty(base->asJSValue(), structureSet, offset)) {
-            addToGraph(Phantom, base);
-            return weakJSConstant(constant);
-        }
-    }
-    
     Node* propertyStorage;
     if (isInlineOffset(offset))
         propertyStorage = base;
@@ -2245,6 +2286,18 @@ Node* ByteCodeParser::handleGetByOffset(SpeculatedType prediction, Node* base, c
     return getByOffset;
 }
 
+Node* ByteCodeParser::handleGetByOffset(SpeculatedType prediction, Node* base, const StructureSet& structureSet, unsigned identifierNumber, PropertyOffset offset, NodeType op)
+{
+    if (base->hasConstant()) {
+        if (JSValue constant = m_graph.tryGetConstantProperty(base->asJSValue(), structureSet, offset)) {
+            addToGraph(Phantom, base);
+            return weakJSConstant(constant);
+        }
+    }
+    
+    return handleGetByOffset(prediction, base, identifierNumber, offset, op);
+}
+
 Node* ByteCodeParser::handlePutByOffset(Node* base, unsigned identifier, PropertyOffset offset, Node* value)
 {
     Node* propertyStorage;
@@ -2262,10 +2315,297 @@ Node* ByteCodeParser::handlePutByOffset(Node* base, unsigned identifier, Propert
     return result;
 }
 
-void ByteCodeParser::emitChecks(const ConstantStructureCheckVector& vector)
+bool ByteCodeParser::check(const ObjectPropertyCondition& condition)
+{
+    if (m_graph.watchCondition(condition))
+        return true;
+    
+    Structure* structure = condition.object()->structure();
+    if (!condition.structureEnsuresValidity(structure))
+        return false;
+    
+    addToGraph(
+        CheckStructure,
+        OpInfo(m_graph.addStructureSet(structure)),
+        weakJSConstant(condition.object()));
+    return true;
+}
+
+GetByOffsetMethod ByteCodeParser::promoteToConstant(GetByOffsetMethod method)
 {
-    for (unsigned i = 0; i < vector.size(); ++i)
-        cellConstantWithStructureCheck(vector[i].constant(), vector[i].structure());
+    if (method.kind() == GetByOffsetMethod::LoadFromPrototype
+        && method.prototype()->structure()->dfgShouldWatch()) {
+        if (JSValue constant = m_graph.tryGetConstantProperty(method.prototype()->value(), method.prototype()->structure(), method.offset()))
+            return GetByOffsetMethod::constant(m_graph.freeze(constant));
+    }
+    
+    return method;
+}
+
+GetByOffsetMethod ByteCodeParser::planLoad(const ObjectPropertyCondition& condition)
+{
+    if (verbose)
+        dataLog("Planning a load: ", condition, "\n");
+    
+    // We might promote this to Equivalence, and a later DFG pass might also do such promotion
+    // even if we fail, but for simplicity this cannot be asked to load an equivalence condition.
+    // None of the clients of this method will request a load of an Equivalence condition anyway,
+    // and supporting it would complicate the heuristics below.
+    RELEASE_ASSERT(condition.kind() == PropertyCondition::Presence);
+    
+    // Here's the ranking of how to handle this, from most preferred to least preferred:
+    //
+    // 1) Watchpoint on an equivalence condition and return a constant node for the loaded value.
+    //    No other code is emitted, and the structure of the base object is never registered.
+    //    Hence this results in zero code and we won't jettison this compilation if the object
+    //    transitions, even if the structure is watchable right now.
+    //
+    // 2) Need to emit a load, and the current structure of the base is going to be watched by the
+    //    DFG anyway (i.e. dfgShouldWatch). Watch the structure and emit the load. Don't watch the
+    //    condition, since the act of turning the base into a constant in IR will cause the DFG to
+    //    watch the structure anyway and doing so would subsume watching the condition.
+    //
+    // 3) Need to emit a load, and the current structure of the base is watchable but not by the
+    //    DFG (i.e. transitionWatchpointSetIsStillValid() and !dfgShouldWatchIfPossible()). Watch
+    //    the condition, and emit a load.
+    //
+    // 4) Need to emit a load, and the current structure of the base is not watchable. Emit a
+    //    structure check, and emit a load.
+    //
+    // 5) The condition does not hold. Give up and return null.
+    
+    // First, try to promote Presence to Equivalence. We do this before doing anything else
+    // because it's the most profitable. Also, there are cases where the presence is watchable but
+    // we don't want to watch it unless it became an equivalence (see the relationship between
+    // (1), (2), and (3) above).
+    ObjectPropertyCondition equivalenceCondition = condition.attemptToMakeEquivalenceWithoutBarrier();
+    if (m_graph.watchCondition(equivalenceCondition))
+        return GetByOffsetMethod::constant(m_graph.freeze(equivalenceCondition.requiredValue()));
+    
+    // At this point, we'll have to materialize the condition's base as a constant in DFG IR. Once
+    // we do this, the frozen value will have its own idea of what the structure is. Use that from
+    // now on just because it's less confusing.
+    FrozenValue* base = m_graph.freeze(condition.object());
+    Structure* structure = base->structure();
+    
+    // Check if the structure that we've registered makes the condition hold. If not, just give
+    // up. This is case (5) above.
+    if (!condition.structureEnsuresValidity(structure))
+        return GetByOffsetMethod();
+    
+    // If the structure is watched by the DFG already, then just use this fact to emit the load.
+    // This is case (2) above.
+    if (structure->dfgShouldWatch())
+        return promoteToConstant(GetByOffsetMethod::loadFromPrototype(base, condition.offset()));
+    
+    // If we can watch the condition right now, then we can emit the load after watching it. This
+    // is case (3) above.
+    if (m_graph.watchCondition(condition))
+        return promoteToConstant(GetByOffsetMethod::loadFromPrototype(base, condition.offset()));
+    
+    // We can't watch anything but we know that the current structure satisfies the condition. So,
+    // check for that structure and then emit the load.
+    addToGraph(
+        CheckStructure, 
+        OpInfo(m_graph.addStructureSet(structure)),
+        addToGraph(JSConstant, OpInfo(base)));
+    return promoteToConstant(GetByOffsetMethod::loadFromPrototype(base, condition.offset()));
+}
+
+Node* ByteCodeParser::load(
+    SpeculatedType prediction, unsigned identifierNumber, const GetByOffsetMethod& method,
+    NodeType op)
+{
+    switch (method.kind()) {
+    case GetByOffsetMethod::Invalid:
+        return nullptr;
+    case GetByOffsetMethod::Constant:
+        return addToGraph(JSConstant, OpInfo(method.constant()));
+    case GetByOffsetMethod::LoadFromPrototype: {
+        Node* baseNode = addToGraph(JSConstant, OpInfo(method.prototype()));
+        return handleGetByOffset(prediction, baseNode, identifierNumber, method.offset(), op);
+    }
+    case GetByOffsetMethod::Load:
+        // Will never see this from planLoad().
+        RELEASE_ASSERT_NOT_REACHED();
+        return nullptr;
+    }
+    
+    RELEASE_ASSERT_NOT_REACHED();
+    return nullptr;
+}
+
+Node* ByteCodeParser::load(
+    SpeculatedType prediction, const ObjectPropertyCondition& condition, NodeType op)
+{
+    GetByOffsetMethod method = planLoad(condition);
+    return load(prediction, m_graph.identifiers().ensure(condition.uid()), method, op);
+}
+
+bool ByteCodeParser::check(const ObjectPropertyConditionSet& conditionSet)
+{
+    for (const ObjectPropertyCondition condition : conditionSet) {
+        if (!check(condition))
+            return false;
+    }
+    return true;
+}
+
+GetByOffsetMethod ByteCodeParser::planLoad(const ObjectPropertyConditionSet& conditionSet)
+{
+    if (verbose)
+        dataLog("conditionSet = ", conditionSet, "\n");
+    
+    GetByOffsetMethod result;
+    for (const ObjectPropertyCondition condition : conditionSet) {
+        switch (condition.kind()) {
+        case PropertyCondition::Presence:
+            RELEASE_ASSERT(!result); // Should only see exactly one of these.
+            result = planLoad(condition);
+            if (!result)
+                return GetByOffsetMethod();
+            break;
+        default:
+            if (!check(condition))
+                return GetByOffsetMethod();
+            break;
+        }
+    }
+    RELEASE_ASSERT(!!result);
+    return result;
+}
+
+Node* ByteCodeParser::load(
+    SpeculatedType prediction, const ObjectPropertyConditionSet& conditionSet, NodeType op)
+{
+    GetByOffsetMethod method = planLoad(conditionSet);
+    return load(
+        prediction,
+        m_graph.identifiers().ensure(conditionSet.slotBaseCondition().uid()),
+        method, op);
+}
+
+ObjectPropertyCondition ByteCodeParser::presenceLike(
+    JSObject* knownBase, UniquedStringImpl* uid, PropertyOffset offset, const StructureSet& set)
+{
+    if (set.isEmpty())
+        return ObjectPropertyCondition();
+    unsigned attributes;
+    PropertyOffset firstOffset = set[0]->getConcurrently(uid, attributes);
+    if (firstOffset != offset)
+        return ObjectPropertyCondition();
+    for (unsigned i = 1; i < set.size(); ++i) {
+        unsigned otherAttributes;
+        PropertyOffset otherOffset = set[i]->getConcurrently(uid, otherAttributes);
+        if (otherOffset != offset || otherAttributes != attributes)
+            return ObjectPropertyCondition();
+    }
+    return ObjectPropertyCondition::presenceWithoutBarrier(knownBase, uid, offset, attributes);
+}
+
+bool ByteCodeParser::checkPresenceLike(
+    JSObject* knownBase, UniquedStringImpl* uid, PropertyOffset offset, const StructureSet& set)
+{
+    return check(presenceLike(knownBase, uid, offset, set));
+}
+
+void ByteCodeParser::checkPresenceLike(
+    Node* base, UniquedStringImpl* uid, PropertyOffset offset, const StructureSet& set)
+{
+    if (JSObject* knownBase = base->dynamicCastConstant<JSObject*>()) {
+        if (checkPresenceLike(knownBase, uid, offset, set))
+            return;
+    }
+
+    addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(set)), base);
+}
+
+template<typename VariantType>
+Node* ByteCodeParser::load(
+    SpeculatedType prediction, Node* base, unsigned identifierNumber, const VariantType& variant)
+{
+    // Make sure backwards propagation knows that we've used base.
+    addToGraph(Phantom, base);
+    
+    bool needStructureCheck = true;
+    
+    if (JSObject* knownBase = base->dynamicCastConstant<JSObject*>()) {
+        // Try to optimize away the structure check. Note that it's not worth doing anything about this
+        // if the base's structure is watched.
+        Structure* structure = base->constant()->structure();
+        if (!structure->dfgShouldWatch()) {
+            UniquedStringImpl* uid = m_graph.identifiers()[identifierNumber];
+            
+            if (!variant.conditionSet().isEmpty()) {
+                // This means that we're loading from a prototype. We expect the base not to have the
+                // property. We can only use ObjectPropertyCondition if all of the structures in the
+                // variant.structureSet() agree on the prototype (it would be hilariously rare if they
+                // didn't). Note that we are relying on structureSet() having at least one element. That
+                // will always be true here because of how GetByIdStatus/PutByIdStatus work.
+                JSObject* prototype = variant.structureSet()[0]->storedPrototypeObject();
+                bool allAgree = true;
+                for (unsigned i = 1; i < variant.structureSet().size(); ++i) {
+                    if (variant.structureSet()[i]->storedPrototypeObject() != prototype) {
+                        allAgree = false;
+                        break;
+                    }
+                }
+                if (allAgree) {
+                    ObjectPropertyCondition condition = ObjectPropertyCondition::absenceWithoutBarrier(
+                        knownBase, uid, prototype);
+                    if (check(condition))
+                        needStructureCheck = false;
+                }
+            } else {
+                // This means we're loading directly from base. We can avoid all of the code that follows
+                // if we can prove that the property is a constant. Otherwise, we try to prove that the
+                // property is watchably present, in which case we get rid of the structure check.
+
+                ObjectPropertyCondition presenceCondition =
+                    presenceLike(knownBase, uid, variant.offset(), variant.structureSet());
+
+                ObjectPropertyCondition equivalenceCondition =
+                    presenceCondition.attemptToMakeEquivalenceWithoutBarrier();
+                if (m_graph.watchCondition(equivalenceCondition))
+                    return weakJSConstant(equivalenceCondition.requiredValue());
+
+                if (check(presenceCondition))
+                    needStructureCheck = false;
+            }
+        }
+    }
+
+    if (needStructureCheck)
+        addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(variant.structureSet())), base);
+    
+    SpeculatedType loadPrediction;
+    NodeType loadOp;
+    if (variant.callLinkStatus()) {
+        loadPrediction = SpecCellOther;
+        loadOp = GetGetterSetterByOffset;
+    } else {
+        loadPrediction = prediction;
+        loadOp = GetByOffset;
+    }
+    
+    Node* loadedValue;
+    if (!variant.conditionSet().isEmpty())
+        loadedValue = load(loadPrediction, variant.conditionSet(), loadOp);
+    else {
+        loadedValue = handleGetByOffset(
+            loadPrediction, base, variant.structureSet(), identifierNumber, variant.offset(),
+            loadOp);
+    }
+
+    return loadedValue;
+}
+
+Node* ByteCodeParser::store(Node* base, unsigned identifier, const PutByIdVariant& variant, Node* value)
+{
+    RELEASE_ASSERT(variant.kind() == PutByIdVariant::Replace);
+
+    checkPresenceLike(base, m_graph.identifiers()[identifier], variant.offset(), variant.structure());
+    return handlePutByOffset(base, identifier, variant.offset(), value);
 }
 
 void ByteCodeParser::handleGetById(
@@ -2288,18 +2628,36 @@ void ByteCodeParser::handleGetById(
             return;
         }
         
-        if (m_graph.compilation())
-            m_graph.compilation()->noticeInlinedGetById();
-    
+        Vector<MultiGetByOffsetCase, 2> cases;
+        
         // 1) Emit prototype structure checks for all chains. This could sort of maybe not be
         //    optimal, if there is some rarely executed case in the chain that requires a lot
         //    of checks and those checks are not watchpointable.
-        for (unsigned variantIndex = getByIdStatus.numVariants(); variantIndex--;)
-            emitChecks(getByIdStatus[variantIndex].constantChecks());
-        
+        for (const GetByIdVariant& variant : getByIdStatus.variants()) {
+            if (variant.conditionSet().isEmpty()) {
+                cases.append(
+                    MultiGetByOffsetCase(
+                        variant.structureSet(),
+                        GetByOffsetMethod::load(variant.offset())));
+                continue;
+            }
+            
+            GetByOffsetMethod method = planLoad(variant.conditionSet());
+            if (!method) {
+                set(VirtualRegister(destinationOperand),
+                    addToGraph(getById, OpInfo(identifierNumber), OpInfo(prediction), base));
+                return;
+            }
+            
+            cases.append(MultiGetByOffsetCase(variant.structureSet(), method));
+        }
+
+        if (m_graph.compilation())
+            m_graph.compilation()->noticeInlinedGetById();
+    
         // 2) Emit a MultiGetByOffset
         MultiGetByOffsetData* data = m_graph.m_multiGetByOffsetData.add();
-        data->variants = getByIdStatus.variants();
+        data->cases = cases;
         data->identifierNumber = identifierNumber;
         set(VirtualRegister(destinationOperand),
             addToGraph(MultiGetByOffset, OpInfo(data), OpInfo(prediction), base));
@@ -2309,30 +2667,16 @@ void ByteCodeParser::handleGetById(
     ASSERT(getByIdStatus.numVariants() == 1);
     GetByIdVariant variant = getByIdStatus[0];
                 
+    Node* loadedValue = load(prediction, base, identifierNumber, variant);
+    if (!loadedValue) {
+        set(VirtualRegister(destinationOperand),
+            addToGraph(getById, OpInfo(identifierNumber), OpInfo(prediction), base));
+        return;
+    }
+
     if (m_graph.compilation())
         m_graph.compilation()->noticeInlinedGetById();
     
-    Node* originalBase = base;
-                
-    addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(variant.structureSet())), base);
-    
-    emitChecks(variant.constantChecks());
-
-    if (variant.alternateBase())
-        base = weakJSConstant(variant.alternateBase());
-    
-    // Unless we want bugs like https://bugs.webkit.org/show_bug.cgi?id=88783, we need to
-    // ensure that the base of the original get_by_id is kept alive until we're done with
-    // all of the speculations. We only insert the Phantom if there had been a CheckStructure
-    // on something other than the base following the CheckStructure on base.
-    if (originalBase != base)
-        addToGraph(Phantom, originalBase);
-    
-    Node* loadedValue = handleGetByOffset(
-        variant.callLinkStatus() ? SpecCellOther : prediction,
-        base, variant.baseStructure(), identifierNumber, variant.offset(),
-        variant.callLinkStatus() ? GetGetterSetterByOffset : GetByOffset);
-    
     if (!variant.callLinkStatus()) {
         set(VirtualRegister(destinationOperand), loadedValue);
         return;
@@ -2369,7 +2713,7 @@ void ByteCodeParser::handleGetById(
     //    since we only really care about 'this' in this case. But we're not going to take that
     //    shortcut.
     int nextRegister = registerOffset + JSStack::CallFrameHeaderSize;
-    set(VirtualRegister(nextRegister++), originalBase, ImmediateNakedSet);
+    set(VirtualRegister(nextRegister++), base, ImmediateNakedSet);
     
     handleCall(
         destinationOperand, Call, InlineCallFrame::GetterCall, OPCODE_LENGTH(op_get_by_id),
@@ -2403,17 +2747,20 @@ void ByteCodeParser::handlePutById(
             return;
         }
         
-        if (m_graph.compilation())
-            m_graph.compilation()->noticeInlinedPutById();
-        
         if (!isDirect) {
             for (unsigned variantIndex = putByIdStatus.numVariants(); variantIndex--;) {
                 if (putByIdStatus[variantIndex].kind() != PutByIdVariant::Transition)
                     continue;
-                emitChecks(putByIdStatus[variantIndex].constantChecks());
+                if (!check(putByIdStatus[variantIndex].conditionSet())) {
+                    emitPutById(base, identifierNumber, value, putByIdStatus, isDirect);
+                    return;
+                }
             }
         }
         
+        if (m_graph.compilation())
+            m_graph.compilation()->noticeInlinedPutById();
+        
         MultiPutByOffsetData* data = m_graph.m_multiPutByOffsetData.add();
         data->variants = putByIdStatus.variants();
         data->identifierNumber = identifierNumber;
@@ -2426,8 +2773,7 @@ void ByteCodeParser::handlePutById(
     
     switch (variant.kind()) {
     case PutByIdVariant::Replace: {
-        addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(variant.structure())), base);
-        handlePutByOffset(base, identifierNumber, variant.offset(), value);
+        store(base, identifierNumber, variant, value);
         if (m_graph.compilation())
             m_graph.compilation()->noticeInlinedPutById();
         return;
@@ -2435,7 +2781,10 @@ void ByteCodeParser::handlePutById(
     
     case PutByIdVariant::Transition: {
         addToGraph(CheckStructure, OpInfo(m_graph.addStructureSet(variant.oldStructure())), base);
-        emitChecks(variant.constantChecks());
+        if (!check(variant.conditionSet())) {
+            emitPutById(base, identifierNumber, value, putByIdStatus, isDirect);
+            return;
+        }
 
         ASSERT(variant.oldStructureForTransition()->transitionWatchpointSetHasBeenInvalidated());
     
@@ -2486,19 +2835,11 @@ void ByteCodeParser::handlePutById(
     }
         
     case PutByIdVariant::Setter: {
-        Node* originalBase = base;
-        
-        addToGraph(
-            CheckStructure, OpInfo(m_graph.addStructureSet(variant.structure())), base);
-        
-        emitChecks(variant.constantChecks());
-        
-        if (variant.alternateBase())
-            base = weakJSConstant(variant.alternateBase());
-        
-        Node* loadedValue = handleGetByOffset(
-            SpecCellOther, base, variant.baseStructure(), identifierNumber, variant.offset(),
-            GetGetterSetterByOffset);
+        Node* loadedValue = load(SpecCellOther, base, identifierNumber, variant);
+        if (!loadedValue) {
+            emitPutById(base, identifierNumber, value, putByIdStatus, isDirect);
+            return;
+        }
         
         Node* setter = addToGraph(GetSetter, loadedValue);
         
@@ -2526,7 +2867,7 @@ void ByteCodeParser::handlePutById(
                 VirtualRegister(registerOffset)).toLocal());
     
         int nextRegister = registerOffset + JSStack::CallFrameHeaderSize;
-        set(VirtualRegister(nextRegister++), originalBase, ImmediateNakedSet);
+        set(VirtualRegister(nextRegister++), base, ImmediateNakedSet);
         set(VirtualRegister(nextRegister++), value, ImmediateNakedSet);
     
         handleCall(
@@ -3466,6 +3807,7 @@ bool ByteCodeParser::parseBlock(unsigned limit)
             case GlobalProperty:
             case GlobalPropertyWithVarInjectionChecks: {
                 SpeculatedType prediction = getPrediction();
+
                 GetByIdStatus status = GetByIdStatus::computeFor(structure, uid);
                 if (status.state() != GetByIdStatus::Simple
                     || status.numVariants() != 1
@@ -3473,9 +3815,11 @@ bool ByteCodeParser::parseBlock(unsigned limit)
                     set(VirtualRegister(dst), addToGraph(GetByIdFlush, OpInfo(identifierNumber), OpInfo(prediction), get(VirtualRegister(scope))));
                     break;
                 }
-                Node* base = cellConstantWithStructureCheck(globalObject, status[0].structureSet().onlyStructure());
+
+                Node* base = weakJSConstant(globalObject);
+                Node* result = load(prediction, base, identifierNumber, status[0]);
                 addToGraph(Phantom, get(VirtualRegister(scope)));
-                set(VirtualRegister(dst), handleGetByOffset(prediction, base, status[0].structureSet(), identifierNumber, operand));
+                set(VirtualRegister(dst), result);
                 break;
             }
             case GlobalVar:
@@ -3619,10 +3963,8 @@ bool ByteCodeParser::parseBlock(unsigned limit)
                     addToGraph(PutById, OpInfo(identifierNumber), get(VirtualRegister(scope)), get(VirtualRegister(value)));
                     break;
                 }
-                ASSERT(status[0].structure().onlyStructure() == structure);
-                Node* base = cellConstantWithStructureCheck(globalObject, structure);
-                addToGraph(Phantom, get(VirtualRegister(scope)));
-                handlePutByOffset(base, identifierNumber, static_cast<PropertyOffset>(operand), get(VirtualRegister(value)));
+                Node* base = weakJSConstant(globalObject);
+                store(base, identifierNumber, status[0], get(VirtualRegister(value)));
                 // Keep scope alive until after put.
                 addToGraph(Phantom, get(VirtualRegister(scope)));
                 break;
index 73860a1..881d19f 100644 (file)
@@ -89,6 +89,9 @@ void CommonData::validateReferences(const TrackedReferences& trackedReferences)
                 trackedReferences.check(inlineCallFrame->calleeRecovery.constant());
         }
     }
+    
+    for (AdaptiveStructureWatchpoint* watchpoint : adaptiveStructureWatchpoints)
+        watchpoint->key().validateReferences(trackedReferences);
 }
 
 } } // namespace JSC::DFG
index ec599c3..2a2d0a2 100644 (file)
@@ -29,6 +29,8 @@
 #if ENABLE(DFG_JIT)
 
 #include "CodeBlockJettisoningWatchpoint.h"
+#include "DFGAdaptiveInferredPropertyValueWatchpoint.h"
+#include "DFGAdaptiveStructureWatchpoint.h"
 #include "DFGJumpReplacement.h"
 #include "InlineCallFrameSet.h"
 #include "JSCell.h"
@@ -98,6 +100,8 @@ public:
     Vector<WriteBarrier<JSCell>> weakReferences;
     Vector<WriteBarrier<Structure>> weakStructureReferences;
     Bag<CodeBlockJettisoningWatchpoint> watchpoints;
+    Bag<AdaptiveStructureWatchpoint> adaptiveStructureWatchpoints;
+    Bag<AdaptiveInferredPropertyValueWatchpoint> adaptiveInferredPropertyValueWatchpoints;
     Vector<JumpReplacement> jumpReplacements;
     
     RefPtr<Profiler::Compilation> compilation;
index fd8df4d..ba4c035 100644 (file)
@@ -294,21 +294,20 @@ private:
                 m_interpreter.execute(indexInBlock); // Push CFA over this node after we get the state before.
                 alreadyHandled = true; // Don't allow the default constant folder to do things to this.
                 
-                for (unsigned i = 0; i < data.variants.size(); ++i) {
-                    GetByIdVariant& variant = data.variants[i];
-                    variant.structureSet().filter(baseValue);
-                    if (variant.structureSet().isEmpty()) {
-                        data.variants[i--] = data.variants.last();
-                        data.variants.removeLast();
+                for (unsigned i = 0; i < data.cases.size(); ++i) {
+                    MultiGetByOffsetCase& getCase = data.cases[i];
+                    getCase.set().filter(baseValue);
+                    if (getCase.set().isEmpty()) {
+                        data.cases[i--] = data.cases.last();
+                        data.cases.removeLast();
                         changed = true;
                     }
                 }
                 
-                if (data.variants.size() != 1)
+                if (data.cases.size() != 1)
                     break;
                 
-                emitGetByOffset(
-                    indexInBlock, node, baseValue, data.variants[0], data.identifierNumber);
+                emitGetByOffset(indexInBlock, node, baseValue, data.cases[0], data.identifierNumber);
                 changed = true;
                 break;
             }
@@ -374,8 +373,7 @@ private:
                     break;
                 
                 for (unsigned i = status.numVariants(); i--;) {
-                    if (!status[i].constantChecks().isEmpty()
-                        || status[i].alternateBase()) {
+                    if (!status[i].conditionSet().isEmpty()) {
                         // FIXME: We could handle prototype cases.
                         // https://bugs.webkit.org/show_bug.cgi?id=110386
                         break;
@@ -392,7 +390,12 @@ private:
                     break;
                 
                 MultiGetByOffsetData* data = m_graph.m_multiGetByOffsetData.add();
-                data->variants = status.variants();
+                for (const GetByIdVariant& variant : status.variants()) {
+                    data->cases.append(
+                        MultiGetByOffsetCase(
+                            variant.structureSet(),
+                            GetByOffsetMethod::load(variant.offset())));
+                }
                 data->identifierNumber = identifierNumber;
                 node->convertToMultiGetByOffset(data);
                 changed = true;
@@ -432,9 +435,31 @@ private:
                     break;
                 
                 changed = true;
-                
-                for (unsigned i = status.numVariants(); i--;)
-                    addChecks(origin, indexInBlock, status[i].constantChecks());
+
+                bool allGood = true;
+                for (const PutByIdVariant& variant : status.variants()) {
+                    if (!allGood)
+                        break;
+                    for (const ObjectPropertyCondition& condition : variant.conditionSet()) {
+                        if (m_graph.watchCondition(condition))
+                            continue;
+
+                        Structure* structure = condition.object()->structure();
+                        if (!condition.structureEnsuresValidity(structure)) {
+                            allGood = false;
+                            break;
+                        }
+
+                        m_insertionSet.insertNode(
+                            indexInBlock, SpecNone, CheckStructure, node->origin,
+                            OpInfo(m_graph.addStructureSet(structure)),
+                            m_insertionSet.insertConstantForUse(
+                                indexInBlock, node->origin, condition.object(), KnownCellUse));
+                    }
+                }
+
+                if (!allGood)
+                    break;
                 
                 if (status.numVariants() == 1) {
                     emitPutByOffset(indexInBlock, node, baseValue, status[0], identifierNumber);
@@ -529,41 +554,72 @@ private:
         
         return changed;
     }
+    
+    void emitGetByOffset(unsigned indexInBlock, Node* node, const AbstractValue& baseValue, const MultiGetByOffsetCase& getCase, unsigned identifierNumber)
+    {
+        // When we get to here we have already emitted all of the requisite checks for everything.
+        // So, we just need to emit what the method object tells us to emit.
+        
+        addBaseCheck(indexInBlock, node, baseValue, getCase.set());
+
+        GetByOffsetMethod method = getCase.method();
         
+        switch (method.kind()) {
+        case GetByOffsetMethod::Invalid:
+            RELEASE_ASSERT_NOT_REACHED();
+            return;
+            
+        case GetByOffsetMethod::Constant:
+            m_graph.convertToConstant(node, method.constant());
+            return;
+            
+        case GetByOffsetMethod::Load:
+            emitGetByOffset(indexInBlock, node, node->child1(), identifierNumber, method.offset());
+            return;
+            
+        case GetByOffsetMethod::LoadFromPrototype: {
+            Node* child = m_insertionSet.insertConstant(
+                indexInBlock, node->origin, method.prototype());
+            emitGetByOffset(
+                indexInBlock, node, Edge(child, KnownCellUse), identifierNumber, method.offset());
+            return;
+        } }
+        
+        RELEASE_ASSERT_NOT_REACHED();
+    }
+    
     void emitGetByOffset(unsigned indexInBlock, Node* node, const AbstractValue& baseValue, const GetByIdVariant& variant, unsigned identifierNumber)
     {
-        NodeOrigin origin = node->origin;
         Edge childEdge = node->child1();
 
         addBaseCheck(indexInBlock, node, baseValue, variant.structureSet());
         
-        JSValue baseForLoad;
-        if (variant.alternateBase())
-            baseForLoad = variant.alternateBase();
-        else
-            baseForLoad = baseValue.m_value;
-        if (JSValue value = m_graph.tryGetConstantProperty(baseForLoad, variant.baseStructure(), variant.offset())) {
+        // We aren't set up to handle prototype stuff.
+        DFG_ASSERT(m_graph, node, variant.conditionSet().isEmpty());
+
+        if (JSValue value = m_graph.tryGetConstantProperty(baseValue.m_value, variant.structureSet(), variant.offset())) {
             m_graph.convertToConstant(node, m_graph.freeze(value));
             return;
         }
         
-        if (variant.alternateBase()) {
-            Node* child = m_insertionSet.insertConstant(indexInBlock, origin, variant.alternateBase());
-            childEdge = Edge(child, KnownCellUse);
-        } else
-            childEdge.setUseKind(KnownCellUse);
+        emitGetByOffset(indexInBlock, node, childEdge, identifierNumber, variant.offset());
+    }
+    
+    void emitGetByOffset(unsigned indexInBlock, Node* node, Edge childEdge, unsigned identifierNumber, PropertyOffset offset)
+    {
+        childEdge.setUseKind(KnownCellUse);
         
         Edge propertyStorage;
         
-        if (isInlineOffset(variant.offset()))
+        if (isInlineOffset(offset))
             propertyStorage = childEdge;
         else {
             propertyStorage = Edge(m_insertionSet.insertNode(
-                indexInBlock, SpecNone, GetButterfly, origin, childEdge));
+                indexInBlock, SpecNone, GetButterfly, node->origin, childEdge));
         }
         
         StorageAccessData& data = *m_graph.m_storageAccessData.add();
-        data.offset = variant.offset();
+        data.offset = offset;
         data.identifierNumber = identifierNumber;
         
         node->convertToGetByOffset(data, propertyStorage);
@@ -643,15 +699,6 @@ private:
             m_insertionSet.insertCheck(indexInBlock, node->origin, node->child1());
     }
     
-    void addChecks(
-        NodeOrigin origin, unsigned indexInBlock, const ConstantStructureCheckVector& checks)
-    {
-        for (unsigned i = 0; i < checks.size(); ++i) {
-            addStructureTransitionCheck(
-                origin, indexInBlock, checks[i].constant(), checks[i].structure());
-        }
-    }
-
     void addStructureTransitionCheck(NodeOrigin origin, unsigned indexInBlock, JSCell* cell, Structure* structure)
     {
         if (m_graph.registerStructure(cell->structure()) == StructureRegisteredAndWatched)
index 7f5b01c..6bea704 100644 (file)
@@ -35,8 +35,9 @@
 namespace JSC { namespace DFG {
 
 void ArrayBufferViewWatchpointAdaptor::add(
-    CodeBlock* codeBlock, JSArrayBufferView* view, Watchpoint* watchpoint)
+    CodeBlock* codeBlock, JSArrayBufferView* view, CommonData& common)
 {
+    Watchpoint* watchpoint = common.watchpoints.add(codeBlock);
     ArrayBufferNeuteringWatchpoint* neuteringWatchpoint =
         ArrayBufferNeuteringWatchpoint::create(*codeBlock->vm());
     neuteringWatchpoint->set()->add(watchpoint);
@@ -45,10 +46,23 @@ void ArrayBufferViewWatchpointAdaptor::add(
 }
 
 void InferredValueAdaptor::add(
-    CodeBlock* codeBlock, InferredValue* inferredValue, Watchpoint* watchpoint)
+    CodeBlock* codeBlock, InferredValue* inferredValue, CommonData& common)
 {
     codeBlock->addConstant(inferredValue); // For common users, it doesn't really matter if it's weak or not. If references to it go away, we go away, too.
-    inferredValue->add(watchpoint);
+    inferredValue->add(common.watchpoints.add(codeBlock));
+}
+
+void AdaptiveStructureWatchpointAdaptor::add(
+    CodeBlock* codeBlock, const ObjectPropertyCondition& key, CommonData& common)
+{
+    switch (key.kind()) {
+    case PropertyCondition::Equivalence:
+        common.adaptiveInferredPropertyValueWatchpoints.add(key, codeBlock)->install();
+        break;
+    default:
+        common.adaptiveStructureWatchpoints.add(key, codeBlock)->install();
+        break;
+    }
 }
 
 DesiredWatchpoints::DesiredWatchpoints() { }
@@ -74,6 +88,11 @@ void DesiredWatchpoints::addLazily(JSArrayBufferView* view)
     m_bufferViews.addLazily(view);
 }
 
+void DesiredWatchpoints::addLazily(const ObjectPropertyCondition& key)
+{
+    m_adaptiveStructureSets.addLazily(key);
+}
+
 bool DesiredWatchpoints::consider(Structure* structure)
 {
     if (!structure->dfgShouldWatch())
@@ -88,6 +107,7 @@ void DesiredWatchpoints::reallyAdd(CodeBlock* codeBlock, CommonData& commonData)
     m_inlineSets.reallyAdd(codeBlock, commonData);
     m_inferredValues.reallyAdd(codeBlock, commonData);
     m_bufferViews.reallyAdd(codeBlock, commonData);
+    m_adaptiveStructureSets.reallyAdd(codeBlock, commonData);
 }
 
 bool DesiredWatchpoints::areStillValid() const
@@ -95,7 +115,18 @@ bool DesiredWatchpoints::areStillValid() const
     return m_sets.areStillValid()
         && m_inlineSets.areStillValid()
         && m_inferredValues.areStillValid()
-        && m_bufferViews.areStillValid();
+        && m_bufferViews.areStillValid()
+        && m_adaptiveStructureSets.areStillValid();
+}
+
+void DesiredWatchpoints::dumpInContext(PrintStream& out, DumpContext* context) const
+{
+    out.print("Desired watchpoints:\n");
+    out.print("    Watchpoint sets: ", inContext(m_sets, context), "\n");
+    out.print("    Inline watchpoint sets: ", inContext(m_inlineSets, context), "\n");
+    out.print("    Inferred values: ", inContext(m_inferredValues, context), "\n");
+    out.print("    Buffer views: ", inContext(m_bufferViews, context), "\n");
+    out.print("    Object property conditions: ", inContext(m_adaptiveStructureSets, context), "\n");
 }
 
 } } // namespace JSC::DFG
index e970526..bc86f43 100644 (file)
@@ -32,7 +32,9 @@
 #include "DFGCommonData.h"
 #include "InferredValue.h"
 #include "JSArrayBufferView.h"
+#include "ObjectPropertyCondition.h"
 #include "Watchpoint.h"
+#include <wtf/CommaPrinter.h>
 #include <wtf/HashSet.h>
 
 namespace JSC { namespace DFG {
@@ -40,33 +42,56 @@ namespace JSC { namespace DFG {
 class Graph;
 
 template<typename T>
-struct GenericSetAdaptor {
-    static void add(CodeBlock*, T set, Watchpoint* watchpoint)
+struct SetPointerAdaptor {
+    static void add(CodeBlock* codeBlock, T set, CommonData& common)
     {
-        return set->add(watchpoint);
+        return set->add(common.watchpoints.add(codeBlock));
     }
     static bool hasBeenInvalidated(T set) { return set->hasBeenInvalidated(); }
+    static void dumpInContext(PrintStream& out, T set, DumpContext*)
+    {
+        out.print(RawPointer(set));
+    }
 };
 
 struct InferredValueAdaptor {
-    static void add(CodeBlock*, InferredValue*, Watchpoint*);
+    static void add(CodeBlock*, InferredValue*, CommonData&);
     static bool hasBeenInvalidated(InferredValue* inferredValue)
     {
         return inferredValue->hasBeenInvalidated();
     }
+    static void dumpInContext(PrintStream& out, InferredValue* inferredValue, DumpContext*)
+    {
+        out.print(RawPointer(inferredValue));
+    }
 };
 
 struct ArrayBufferViewWatchpointAdaptor {
-    static void add(CodeBlock*, JSArrayBufferView*, Watchpoint*);
+    static void add(CodeBlock*, JSArrayBufferView*, CommonData&);
     static bool hasBeenInvalidated(JSArrayBufferView* view)
     {
-        bool result = !view->length();
-        WTF::loadLoadFence();
-        return result;
+        return !view->length();
+    }
+    static void dumpInContext(PrintStream& out, JSArrayBufferView* view, DumpContext* context)
+    {
+        out.print(inContext(JSValue(view), context));
+    }
+};
+
+struct AdaptiveStructureWatchpointAdaptor {
+    static void add(CodeBlock*, const ObjectPropertyCondition&, CommonData&);
+    static bool hasBeenInvalidated(const ObjectPropertyCondition& key)
+    {
+        return !key.isWatchable();
+    }
+    static void dumpInContext(
+        PrintStream& out, const ObjectPropertyCondition& key, DumpContext* context)
+    {
+        out.print(inContext(key, context));
     }
 };
 
-template<typename WatchpointSetType, typename Adaptor = GenericSetAdaptor<WatchpointSetType>>
+template<typename WatchpointSetType, typename Adaptor = SetPointerAdaptor<WatchpointSetType>>
 class GenericDesiredWatchpoints {
 #if !ASSERT_DISABLED
     typedef HashMap<WatchpointSetType, bool> StateMap;
@@ -87,7 +112,7 @@ public:
         RELEASE_ASSERT(!m_reallyAdded);
         
         for (auto& set : m_sets)
-            Adaptor::add(codeBlock, set, common.watchpoints.add(codeBlock));
+            Adaptor::add(codeBlock, set, common);
         
         m_reallyAdded = true;
     }
@@ -107,6 +132,15 @@ public:
         return m_sets.contains(set);
     }
 
+    void dumpInContext(PrintStream& out, DumpContext* context) const
+    {
+        CommaPrinter comma;
+        for (const WatchpointSetType& entry : m_sets) {
+            out.print(comma);
+            Adaptor::dumpInContext(out, entry, context);
+        }
+    }
+
 private:
     HashSet<WatchpointSetType> m_sets;
     bool m_reallyAdded;
@@ -122,6 +156,10 @@ public:
     void addLazily(InferredValue*);
     void addLazily(JSArrayBufferView*);
     
+    // It's recommended that you don't call this directly. Use Graph::watchCondition(), which does
+    // the required GC magic as well as some other bookkeeping.
+    void addLazily(const ObjectPropertyCondition&);
+    
     bool consider(Structure*);
     
     void reallyAdd(CodeBlock*, CommonData&);
@@ -144,12 +182,20 @@ public:
     {
         return m_bufferViews.isWatched(view);
     }
+    bool isWatched(const ObjectPropertyCondition& key)
+    {
+        return m_adaptiveStructureSets.isWatched(key);
+    }
+
+    void dumpInContext(PrintStream&, DumpContext*) const;
+    void dump(PrintStream&) const;
     
 private:
     GenericDesiredWatchpoints<WatchpointSet*> m_sets;
     GenericDesiredWatchpoints<InlineWatchpointSet*> m_inlineSets;
     GenericDesiredWatchpoints<InferredValue*, InferredValueAdaptor> m_inferredValues;
     GenericDesiredWatchpoints<JSArrayBufferView*, ArrayBufferViewWatchpointAdaptor> m_bufferViews;
+    GenericDesiredWatchpoints<ObjectPropertyCondition, AdaptiveStructureWatchpointAdaptor> m_adaptiveStructureSets;
 };
 
 } } // namespace JSC::DFG
index f14968c..0df9606 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -50,7 +50,14 @@ DesiredWeakReferences::~DesiredWeakReferences()
 
 void DesiredWeakReferences::addLazily(JSCell* cell)
 {
-    m_references.add(cell);
+    if (cell)
+        m_references.add(cell);
+}
+
+void DesiredWeakReferences::addLazily(JSValue value)
+{
+    if (value.isCell())
+        addLazily(value.asCell());
 }
 
 bool DesiredWeakReferences::contains(JSCell* cell)
index e312255..303b8df 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
+ * Copyright (C) 2013-2015 Apple Inc. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -34,6 +34,7 @@ namespace JSC {
 
 class CodeBlock;
 class JSCell;
+class JSValue;
 class SlotVisitor;
 class VM;
 
@@ -48,6 +49,7 @@ public:
     ~DesiredWeakReferences();
 
     void addLazily(JSCell*);
+    void addLazily(JSValue);
     bool contains(JSCell*);
     
     void reallyAdd(VM&, CommonData*);
index 3991faf..e2c4f01 100644 (file)
@@ -257,8 +257,8 @@ void Graph::dump(PrintStream& out, const char* prefix, Node* node, DumpContext*
     if (node->hasMultiGetByOffsetData()) {
         MultiGetByOffsetData& data = node->multiGetByOffsetData();
         out.print(comma, "id", data.identifierNumber, "{", identifiers()[data.identifierNumber], "}");
-        for (unsigned i = 0; i < data.variants.size(); ++i)
-            out.print(comma, inContext(data.variants[i], context));
+        for (unsigned i = 0; i < data.cases.size(); ++i)
+            out.print(comma, inContext(data.cases[i], context));
     }
     if (node->hasMultiPutByOffsetData()) {
         MultiPutByOffsetData& data = node->multiPutByOffsetData();
@@ -530,6 +530,8 @@ void Graph::dump(PrintStream& out, DumpContext* context)
         if (value->pointsToHeap())
             out.print("    ", inContext(*value, &myContext), "\n");
     }
+
+    out.print(inContext(watchpoints(), &myContext));
     
     if (!myContext.isEmpty()) {
         myContext.dump(out);
@@ -848,6 +850,30 @@ void Graph::clearFlagsOnAllNodes(NodeFlags flags)
     }
 }
 
+bool Graph::watchCondition(const ObjectPropertyCondition& key)
+{
+    if (!key.isWatchable())
+        return false;
+    
+    m_plan.weakReferences.addLazily(key.object());
+    if (key.hasPrototype())
+        m_plan.weakReferences.addLazily(key.prototype());
+    if (key.hasRequiredValue())
+        m_plan.weakReferences.addLazily(key.requiredValue());
+    
+    m_plan.watchpoints.addLazily(key);
+
+    if (key.kind() == PropertyCondition::Presence)
+        m_safeToLoad.add(std::make_pair(key.object(), key.offset()));
+    
+    return true;
+}
+
+bool Graph::isSafeToLoad(JSObject* base, PropertyOffset offset)
+{
+    return m_safeToLoad.contains(std::make_pair(base, offset));
+}
+
 FullBytecodeLiveness& Graph::livenessFor(CodeBlock* codeBlock)
 {
     HashMap<CodeBlock*, std::unique_ptr<FullBytecodeLiveness>>::iterator iter = m_bytecodeLiveness.find(codeBlock);
@@ -1020,8 +1046,12 @@ JSValue Graph::tryGetConstantProperty(JSValue base, Structure* structure, Proper
 JSValue Graph::tryGetConstantProperty(
     JSValue base, const StructureAbstractValue& structure, PropertyOffset offset)
 {
-    if (structure.isTop() || structure.isClobbered())
+    if (structure.isTop() || structure.isClobbered()) {
+        // FIXME: If we just converted the offset to a uid, we could do ObjectPropertyCondition
+        // watching to constant-fold the property.
+        // https://bugs.webkit.org/show_bug.cgi?id=147271
         return JSValue();
+    }
     
     return tryGetConstantProperty(base, structure.set(), offset);
 }
@@ -1167,17 +1197,9 @@ void Graph::visitChildren(SlotVisitor& visitor)
                 break;
                 
             case MultiGetByOffset:
-                for (unsigned i = node->multiGetByOffsetData().variants.size(); i--;) {
-                    GetByIdVariant& variant = node->multiGetByOffsetData().variants[i];
-                    const StructureSet& set = variant.structureSet();
-                    for (unsigned j = set.size(); j--;)
-                        visitor.appendUnbarrieredReadOnlyPointer(set[j]);
-
-                    // Don't need to mark anything in the structure chain because that would
-                    // have been decomposed into CheckStructure's. Don't need to mark the
-                    // callLinkStatus because we wouldn't use MultiGetByOffset if any of the
-                    // variants did that.
-                    ASSERT(!variant.callLinkStatus());
+                for (const MultiGetByOffsetCase& getCase : node->multiGetByOffsetData().cases) {
+                    for (Structure* structure : getCase.set())
+                        visitor.appendUnbarrieredReadOnlyPointer(structure);
                 }
                 break;
                     
index c6ad495..57b3fff 100644 (file)
@@ -665,6 +665,15 @@ public:
     DesiredIdentifiers& identifiers() { return m_plan.identifiers; }
     DesiredWatchpoints& watchpoints() { return m_plan.watchpoints; }
     
+    // Returns false if the key is already invalid or unwatchable. If this is a Presence condition,
+    // this also makes it cheap to query if the condition holds. Also makes sure that the GC knows
+    // what's going on.
+    bool watchCondition(const ObjectPropertyCondition&);
+
+    // Checks if it's known that loading from the given object at the given offset is fine. This is
+    // computed by tracking which conditions we track with watchCondition().
+    bool isSafeToLoad(JSObject* base, PropertyOffset);
+    
     FullBytecodeLiveness& livenessFor(CodeBlock*);
     FullBytecodeLiveness& livenessFor(InlineCallFrame*);
     
@@ -844,6 +853,7 @@ public:
     Vector<InlineVariableData, 4> m_inlineVariableData;
     HashMap<CodeBlock*, std::unique_ptr<FullBytecodeLiveness>> m_bytecodeLiveness;
     HashMap<CodeBlock*, std::unique_ptr<BytecodeKills>> m_bytecodeKills;
+    HashSet<std::pair<JSObject*, PropertyOffset>> m_safeToLoad;
     Dominators m_dominators;
     PrePostNumbering m_prePostNumbering;
     NaturalLoops m_naturalLoops;
diff --git a/Source/JavaScriptCore/dfg/DFGMultiGetByOffsetData.cpp b/Source/JavaScriptCore/dfg/DFGMultiGetByOffsetData.cpp
new file mode 100644 (file)
index 0000000..d1a111b
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#include "config.h"
+#include "DFGMultiGetByOffsetData.h"
+
+#if ENABLE(DFG_JIT)
+
+#include "DFGFrozenValue.h"
+#include "JSCInlines.h"
+
+namespace JSC { namespace DFG {
+
+void GetByOffsetMethod::dumpInContext(PrintStream& out, DumpContext* context) const
+{
+    out.print(m_kind, ":");
+    switch (m_kind) {
+    case Invalid:
+        out.print("<none>");
+        return;
+    case Constant:
+        out.print(pointerDumpInContext(constant(), context));
+        return;
+    case Load:
+        out.print(offset());
+        return;
+    case LoadFromPrototype:
+        out.print(offset(), "@", pointerDumpInContext(prototype(), context));
+        return;
+    }
+}
+
+void GetByOffsetMethod::dump(PrintStream& out) const
+{
+    dumpInContext(out, nullptr);
+}
+
+void MultiGetByOffsetCase::dumpInContext(PrintStream& out, DumpContext* context) const
+{
+    out.print(inContext(m_set, context), ":", inContext(m_method, context));
+}
+
+void MultiGetByOffsetCase::dump(PrintStream& out) const
+{
+    dumpInContext(out, nullptr);
+}
+
+} } // namespace JSC::DFG
+
+namespace WTF {
+
+using namespace JSC::DFG;
+
+void printInternal(PrintStream& out, GetByOffsetMethod::Kind kind)
+{
+    switch (kind) {
+    case GetByOffsetMethod::Invalid:
+        out.print("Invalid");
+        return;
+    case GetByOffsetMethod::Constant:
+        out.print("Constant");
+        return;
+    case GetByOffsetMethod::Load:
+        out.print("Load");
+        return;
+    case GetByOffsetMethod::LoadFromPrototype:
+        out.print("LoadFromPrototype");
+        return;
+    }
+    
+    RELEASE_ASSERT_NOT_REACHED();
+}
+
+} // namespace WTF
+
+#endif // ENABLE(DFG_JIT)
+
diff --git a/Source/JavaScriptCore/dfg/DFGMultiGetByOffsetData.h b/Source/JavaScriptCore/dfg/DFGMultiGetByOffsetData.h
new file mode 100644 (file)
index 0000000..72680bf
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2015 Apple Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ */
+
+#ifndef DFGMultiGetByOffsetData_h
+#define DFGMultiGetByOffsetData_h
+
+#if ENABLE(DFG_JIT)
+
+#include "DumpContext.h"
+#include "JSObject.h"
+#include "StructureSet.h"
+
+namespace JSC { namespace DFG {
+
+class FrozenValue;
+
+class GetByOffsetMethod {
+public:
+    enum Kind {
+        Invalid,
+        Constant,
+        Load,
+        LoadFromPrototype
+    };
+    
+    GetByOffsetMethod()
+        : m_kind(Invalid)
+    {
+    }
+    
+    static GetByOffsetMethod constant(FrozenValue* value)
+    {
+        GetByOffsetMethod result;
+        result.m_kind = Constant;
+        result.u.constant = value;
+        return result;
+    }
+    
+    static GetByOffsetMethod load(PropertyOffset offset)
+    {
+        GetByOffsetMethod result;
+        result.m_kind = Load;
+        result.u.load.offset = offset;
+        return result;
+    }
+    
+    static GetByOffsetMethod loadFromPrototype(FrozenValue* prototype, PropertyOffset offset)
+    {
+        GetByOffsetMethod result;
+        result.m_kind = LoadFromPrototype;
+        result.u.load.prototype = prototype;
+        result.u.load.offset = offset;
+        return result;
+    }
+    
+    bool operator!() const { return m_kind == Invalid; }
+    
+    Kind kind() const { return m_kind; }
+    
+    FrozenValue* constant() const
+    {
+        ASSERT(kind() == Constant);
+        return u.constant;
+    }
+    
+    FrozenValue* prototype() const
+    {
+        ASSERT(kind() == LoadFromPrototype);
+        return u.load.prototype;
+    }
+    
+    PropertyOffset offset() const
+    {
+        ASSERT(kind() == Load || kind() == LoadFromPrototype);
+        return u.load.offset;
+    }
+    
+    void dumpInContext(PrintStream&, DumpContext*) const;
+    void dump(PrintStream&) const;
+    
+private:
+    union {
+        FrozenValue* constant;
+        struct {
+            FrozenValue* prototype;
+            PropertyOffset offset;
+        } load;
+    } u;
+    Kind m_kind;
+};
+
+class MultiGetByOffsetCase {
+public:
+    MultiGetByOffsetCase()
+    {
+    }
+    
+    MultiGetByOffsetCase(const StructureSet& set, const GetByOffsetMethod& method)
+        : m_set(set)
+        , m_method(method)
+    {
+    }
+    
+    StructureSet& set() { return m_set; }
+    const StructureSet& set() const { return m_set; }
+    const GetByOffsetMethod& method() const { return m_method; }
+    
+    void dumpInContext(PrintStream&, DumpContext*) const;
+    void dump(PrintStream&) const;
+
+private:
+    StructureSet m_set;
+    GetByOffsetMethod m_method;
+};
+
+struct MultiGetByOffsetData {
+    unsigned identifierNumber;
+    Vector<MultiGetByOffsetCase, 2> cases;
+};
+
+} } // namespace JSC::DFG
+
+namespace WTF {
+
+void printInternal(PrintStream&, JSC::DFG::GetByOffsetMethod::Kind);
+
+} // namespace WTF
+
+#endif // ENABLE(DFG_JIT)
+
+#endif // DFGMultiGetByOffsetData_h
+
index df21cde..28cab05 100644 (file)
@@ -37,6 +37,7 @@
 #include "DFGCommon.h"
 #include "DFGEpoch.h"
 #include "DFGLazyJSValue.h"
+#include "DFGMultiGetByOffsetData.h"
 #include "DFGNodeFlags.h"
 #include "DFGNodeOrigin.h"
 #include "DFGNodeType.h"
@@ -65,11 +66,6 @@ struct StorageAccessData {
     unsigned identifierNumber;
 };
 
-struct MultiGetByOffsetData {
-    unsigned identifierNumber;
-    Vector<GetByIdVariant, 2> variants;
-};
-
 struct MultiPutByOffsetData {
     unsigned identifierNumber;
     Vector<PutByIdVariant, 2> variants;
index 0ffa6a8..0c0a277 100644 (file)
@@ -324,10 +324,18 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
     case GetByOffset:
     case GetGetterSetterByOffset:
     case PutByOffset: {
+        PropertyOffset offset = node->storageAccessData().offset;
+
+        if (state.structureClobberState() == StructuresAreWatched) {
+            if (JSObject* knownBase = node->child1()->dynamicCastConstant<JSObject*>()) {
+                if (graph.isSafeToLoad(knownBase, offset))
+                    return true;
+            }
+        }
+        
         StructureAbstractValue& value = state.forNode(node->child1()).m_structure;
         if (value.isInfinite())
             return false;
-        PropertyOffset offset = node->storageAccessData().offset;
         for (unsigned i = value.size(); i--;) {
             if (!value[i]->isValidOffset(offset))
                 return false;
@@ -337,42 +345,28 @@ bool safeToExecute(AbstractStateType& state, Graph& graph, Node* node)
         
     case MultiGetByOffset: {
         // We can't always guarantee that the MultiGetByOffset is safe to execute if it
-        // contains loads from prototypes. We know that it won't load from those prototypes if
-        // we watch the mutability of the properties being loaded. So, here we try to
-        // constant-fold prototype loads, and if that fails, we claim that we cannot hoist. We
-        // also know that a load is safe to execute if we are watching the prototype's
-        // structure.
-        for (const GetByIdVariant& variant : node->multiGetByOffsetData().variants) {
-            if (!variant.alternateBase()) {
-                // It's not a prototype load.
-                continue;
+        // contains loads from prototypes. If the load requires a check in IR, which is rare, then
+        // we currently claim that we don't know if it's safe to execute because finding that
+        // check in the abstract state would be hard. If the load requires watchpoints, we just
+        // check if we're not in a clobbered state (i.e. in between a side effect and an
+        // invalidation point).
+        for (const MultiGetByOffsetCase& getCase : node->multiGetByOffsetData().cases) {
+            GetByOffsetMethod method = getCase.method();
+            switch (method.kind()) {
+            case GetByOffsetMethod::Invalid:
+                RELEASE_ASSERT_NOT_REACHED();
+                break;
+            case GetByOffsetMethod::Constant: // OK because constants are always safe to execute.
+            case GetByOffsetMethod::Load: // OK because the MultiGetByOffset has its own checks for loading from self.
+                break;
+            case GetByOffsetMethod::LoadFromPrototype:
+                // Only OK if the state isn't clobbered. That's almost always the case.
+                if (state.structureClobberState() != StructuresAreWatched)
+                    return false;
+                if (!graph.isSafeToLoad(method.prototype()->cast<JSObject*>(), method.offset()))
+                    return false;
+                break;
             }
-            
-            JSValue base = variant.alternateBase();
-            FrozenValue* frozen = graph.freeze(base);
-            if (state.structureClobberState() == StructuresAreWatched
-                && frozen->structure()->dfgShouldWatch()
-                && frozen->structure()->isValidOffset(variant.offset())) {
-                // We're already watching that it's safe to load from this.
-                continue;
-            }
-            
-            JSValue constantResult = graph.tryGetConstantProperty(
-                variant.alternateBase(), variant.baseStructure(), variant.offset());
-            if (!constantResult) {
-                // Couldn't constant-fold a prototype load. Therefore, we shouldn't hoist
-                // because the safety of the load depends on structure checks on the prototype,
-                // and we're too cheap to verify that here.
-                return false;
-            }
-            
-            // Otherwise, we will either:
-            // - Not load from the prototype because constant folding will succeed in the
-            //   backend, or
-            // - Emit invalid code that fails watchpoint checks. This could happen if the
-            //   property becomes mutable after this point, since we already set the watchpoint
-            //   above. This case is OK since the code will fail watchpoint checks and never
-            //   get installed.
         }
         return true;
     }
index 875e0d3..cf5707f 100644 (file)
@@ -86,15 +86,8 @@ public:
                     break;
                     
                 case MultiGetByOffset:
-                    for (unsigned i = node->multiGetByOffsetData().variants.size(); i--;) {
-                        GetByIdVariant& variant = node->multiGetByOffsetData().variants[i];
-                        registerStructures(variant.structureSet());
-                        // Don't need to watch anything in the structure chain because that would
-                        // have been decomposed into CheckStructure's. Don't need to watch the
-                        // callLinkStatus because we wouldn't use MultiGetByOffset if any of the
-                        // variants did that.
-                        ASSERT(!variant.callLinkStatus());
-                    }
+                    for (const MultiGetByOffsetCase& getCase : node->multiGetByOffsetData().cases)
+                        registerStructures(getCase.set());
                     break;
                     
                 case MultiPutByOffset:
index 52dea5e..5b587dd 100644 (file)
@@ -3837,24 +3837,24 @@ private:
         
         MultiGetByOffsetData& data = m_node->multiGetByOffsetData();
 
-        if (data.variants.isEmpty()) {
+        if (data.cases.isEmpty()) {
             // Protect against creating a Phi function with zero inputs. LLVM doesn't like that.
             terminate(BadCache);
             return;
         }
         
-        Vector<LBasicBlock, 2> blocks(data.variants.size());
-        for (unsigned i = data.variants.size(); i--;)
+        Vector<LBasicBlock, 2> blocks(data.cases.size());
+        for (unsigned i = data.cases.size(); i--;)
             blocks[i] = FTL_NEW_BLOCK(m_out, ("MultiGetByOffset case ", i));
         LBasicBlock exit = FTL_NEW_BLOCK(m_out, ("MultiGetByOffset fail"));
         LBasicBlock continuation = FTL_NEW_BLOCK(m_out, ("MultiGetByOffset continuation"));
         
         Vector<SwitchCase, 2> cases;
         StructureSet baseSet;
-        for (unsigned i = data.variants.size(); i--;) {
-            GetByIdVariant variant = data.variants[i];
-            for (unsigned j = variant.structureSet().size(); j--;) {
-                Structure* structure = variant.structureSet()[j];
+        for (unsigned i = data.cases.size(); i--;) {
+            MultiGetByOffsetCase getCase = data.cases[i];
+            for (unsigned j = getCase.set().size(); j--;) {
+                Structure* structure = getCase.set()[j];
                 baseSet.add(structure);
                 cases.append(SwitchCase(weakStructureID(structure), blocks[i], Weight(1)));
             }
@@ -3865,29 +3865,36 @@ private:
         LBasicBlock lastNext = m_out.m_nextBlock;
         
         Vector<ValueFromBlock, 2> results;
-        for (unsigned i = data.variants.size(); i--;) {
-            m_out.appendTo(blocks[i], i + 1 < data.variants.size() ? blocks[i + 1] : exit);
+        for (unsigned i = data.cases.size(); i--;) {
+            MultiGetByOffsetCase getCase = data.cases[i];
+            GetByOffsetMethod method = getCase.method();
+            
+            m_out.appendTo(blocks[i], i + 1 < data.cases.size() ? blocks[i + 1] : exit);
             
-            GetByIdVariant variant = data.variants[i];
-            baseSet.merge(variant.structureSet());
             LValue result;
-            JSValue constantResult;
-            if (variant.alternateBase()) {
-                constantResult = m_graph.tryGetConstantProperty(
-                    variant.alternateBase(), variant.baseStructure(), variant.offset());
-            }
-            if (constantResult)
-                result = m_out.constInt64(JSValue::encode(constantResult));
-            else {
+            
+            switch (method.kind()) {
+            case GetByOffsetMethod::Invalid:
+                RELEASE_ASSERT_NOT_REACHED();
+                break;
+                
+            case GetByOffsetMethod::Constant:
+                result = m_out.constInt64(JSValue::encode(method.constant()->value()));
+                break;
+                
+            case GetByOffsetMethod::Load:
+            case GetByOffsetMethod::LoadFromPrototype: {
                 LValue propertyBase;
-                if (variant.alternateBase())
-                    propertyBase = weakPointer(variant.alternateBase());
-                else
+                if (method.kind() == GetByOffsetMethod::Load)
                     propertyBase = base;
-                if (!isInlineOffset(variant.offset()))
+                else
+                    propertyBase = weakPointer(method.prototype()->value().asCell());
+                if (!isInlineOffset(method.offset()))
                     propertyBase = m_out.loadPtr(propertyBase, m_heaps.JSObject_butterfly);
-                result = loadProperty(propertyBase, data.identifierNumber, variant.offset());
-            }
+                result = loadProperty(
+                    propertyBase, data.identifierNumber, method.offset());
+                break;
+            } }
             
             results.append(m_out.anchor(result));
             m_out.jump(continuation);
index fe0d061..762f391 100644 (file)
@@ -105,7 +105,7 @@ static void repatchByIdSelfAccess(
     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 @@ static void repatchByIdSelfAccess(
 #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 FunctionPtr customFor(const PutPropertySlot& slot)
 
 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 @@ static bool generateByIdStub(
     }
 
     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 @@ static InlineCacheAction actionForCell(VM& vm, JSCell* cell)
         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 @@ static InlineCacheAction tryBuildGetByIDList(ExecState* exec, JSValue baseValue,
     }
 
     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 @@ static InlineCacheAction tryBuildGetByIDList(ExecState* exec, JSValue baseValue,
     
     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 @@ static InlineCacheAction tryBuildGetByIDList(ExecState* exec, JSValue baseValue,
     
     list->addAccess(GetByIdAccess(
         *vm, codeBlock->ownerExecutable(), accessType, stubRoutine, structure,
-        prototypeChain, count));
+        conditionSet));
     
     patchJumpToGetByIdStub(codeBlock, stubInfo, stubRoutine.get());
     
@@ -991,26 +972,28 @@ static bool emitPutReplaceStub(
     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 @@ static Structure* emitPutTransitionStubAndGetOldStructure(ExecState* exec, VM* v
     // FIXME: We shouldn't skip this! Implement it!
     // https://bugs.webkit.org/show_bug.cgi?id=130914
     if (oldStructure->couldHaveIndexingHeader())
-        return nullptr;
-
-    if (normalizePrototypeChain(exec, structure) == InvalidPrototypeChain)
-        return nullptr;
-
-    StructureChain* prototypeChain = structure->prototypeChain(exec);
+        return false;
 
-    // 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 @@ static Structure* emitPutTransitionStubAndGetOldStructure(ExecState* exec, VM* v
         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 @@ static Structure* emitPutTransitionStubAndGetOldStructure(ExecState* exec, VM* v
     
     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 @@ static Structure* emitPutTransitionStubAndGetOldStructure(ExecState* exec, VM* v
             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 @@ static InlineCacheAction tryCachePutByID(ExecState* exec, JSValue baseValue, Str
     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 @@ static InlineCacheAction tryCachePutByID(ExecState* exec, JSValue baseValue, Str
                 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 @@ static InlineCacheAction tryCachePutByID(ExecState* exec, JSValue baseValue, Str
         && 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 @@ static InlineCacheAction tryCachePutByID(ExecState* exec, JSValue baseValue, Str
         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 @@ static InlineCacheAction tryBuildPutByIdList(ExecState* exec, JSValue baseValue,
             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 @@ static InlineCacheAction tryBuildPutByIdList(ExecState* exec, JSValue baseValue,
     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 @@ static InlineCacheAction tryBuildPutByIdList(ExecState* exec, JSValue baseValue,
         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 @@ static InlineCacheAction tryRepatchIn(
     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 @@ static InlineCacheAction tryRepatchIn(
             return GiveUpOnCache;
     }
     
-    StructureChain* chain = structure->prototypeChain(exec);
     RefPtr<JITStubRoutine> stubRoutine;
     
     {
@@ -1545,18 +1544,8 @@ static InlineCacheAction tryRepatchIn(
         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);
diff --git a/Source/JavaScriptCore/runtime/IntendedStructureChain.cpp b/Source/JavaScriptCore/runtime/IntendedStructureChain.cpp
deleted file mode 100644 (file)
index d73f748..0000000
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Copyright (C) 2013, 2014 Apple Inc. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- *    notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- *    notice, this list of conditions and the following disclaimer in the
- *    documentation and/or other materials provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
- * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
- * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
- * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
- * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
- * OF LIABILITY, WHETHER IN CONTRACT,&n