+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
--- /dev/null
+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
+
--- /dev/null
+<!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>
--- /dev/null
+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
+
--- /dev/null
+<!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>
--- /dev/null
+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;
+}
--- /dev/null
+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;
+}
}
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.
}
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.
}
var result = baz();
if (result != 2)
- throw "Bad result: " + result;
+ throw new Error("Bad result: " + result);
bytecode/CodeOrigin.cpp
bytecode/CodeType.cpp
bytecode/ComplexGetStatus.cpp
- bytecode/ConstantStructureCheck.cpp
bytecode/DFGExitProfile.cpp
bytecode/DeferredCompilationCallback.cpp
bytecode/DeferredSourceDump.cpp
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
dfg/DFGAbstractHeap.cpp
dfg/DFGAbstractValue.cpp
+ dfg/DFGAdaptiveInferredPropertyValueWatchpoint.cpp
+ dfg/DFGAdaptiveStructureWatchpoint.cpp
dfg/DFGArgumentsEliminationPhase.cpp
dfg/DFGArgumentsUtilities.cpp
dfg/DFGArithMode.cpp
dfg/DFGMinifiedGraph.cpp
dfg/DFGMinifiedNode.cpp
dfg/DFGMovHintRemovalPhase.cpp
+ dfg/DFGMultiGetByOffsetData.cpp
dfg/DFGNaiveDominators.cpp
dfg/DFGNaturalLoops.cpp
dfg/DFGNode.cpp
runtime/IndexingType.cpp
runtime/InferredValue.cpp
runtime/InitializeThreading.cpp
- runtime/IntendedStructureChain.cpp
runtime/InternalFunction.cpp
runtime/IntlCollator.cpp
runtime/IntlCollatorConstructor.cpp
+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)
-<?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" />
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 */,
Structure* baseStructure = 0;
Structure* prototypeStructure = 0;
- StructureChain* chain = 0;
PolymorphicGetByIdList* list = 0;
switch (stubInfo.accessType) {
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) {
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(")");
}
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 = [");
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");
/*
- * 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
dataLog("Firing watchpoint ", RawPointer(this), " on ", *m_codeBlock, "\n");
m_codeBlock->jettison(Profiler::JettisonDueToUnprofiledWatchpoint, CountReoptimization, &detail);
-
- if (isOnList())
- remove();
}
} // namespace JSC
/*
- * 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
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
// 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();
/*
- * 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
#ifndef ComplexGetStatus_h
#define ComplexGetStatus_h
-#include "IntendedStructureChain.h"
#include "JSCJSValue.h"
+#include "ObjectPropertyConditionSet.h"
#include "PropertyOffset.h"
namespace JSC {
ComplexGetStatus()
: m_kind(ShouldSkip)
, m_offset(invalidOffset)
- , m_attributes(UINT_MAX)
{
}
}
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
/*
- * 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
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:
}
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);
/*
- * 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
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)
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);
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;
}
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);
/*
- * 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
#define GetByIdVariant_h
#include "CallLinkStatus.h"
-#include "ConstantStructureCheck.h"
-#include "IntendedStructureChain.h"
#include "JSCJSValue.h"
+#include "ObjectPropertyConditionSet.h"
#include "PropertyOffset.h"
#include "StructureSet.h"
public:
GetByIdVariant(
const StructureSet& structureSet = StructureSet(), PropertyOffset offset = invalidOffset,
- const IntendedStructureChain* chain = nullptr,
+ const ObjectPropertyConditionSet& = ObjectPropertyConditionSet(),
std::unique_ptr<CallLinkStatus> callLinkStatus = nullptr);
~GetByIdVariant();
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(); }
friend class GetByIdStatus;
StructureSet m_structureSet;
- ConstantStructureCheckVector m_constantChecks;
- JSObject* m_alternateBase;
+ ObjectPropertyConditionSet m_conditionSet;
PropertyOffset m_offset;
std::unique_ptr<CallLinkStatus> m_callLinkStatus;
};
--- /dev/null
+/*
+ * 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
+
--- /dev/null
+/*
+ * 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
+
--- /dev/null
+/*
+ * 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
+
--- /dev/null
+/*
+ * 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
+
/*
- * 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
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()
{
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;
/*
- * 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
#include "CodeOrigin.h"
#include "MacroAssembler.h"
+#include "ObjectPropertyConditionSet.h"
#include "Opcode.h"
#include "Structure.h"
#include <wtf/Vector.h>
GetByIdAccess()
: m_type(Invalid)
- , m_chainCount(0)
{
}
GetByIdAccess(
VM&, JSCell* owner, AccessType, PassRefPtr<JITStubRoutine>, Structure*,
- StructureChain* = 0, unsigned chainCount = 0);
+ const ObjectPropertyConditionSet& = ObjectPropertyConditionSet());
~GetByIdAccess();
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
{
friend class CodeBlock;
AccessType m_type;
- unsigned m_chainCount;
WriteBarrier<Structure> m_structure;
- WriteBarrier<StructureChain> m_chain;
+ ObjectPropertyConditionSet m_conditionSet;
RefPtr<JITStubRoutine> m_stubRoutine;
};
/*
- * 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
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;
bool PutByIdAccess::visitWeak(RepatchBuffer& repatchBuffer) const
{
+ if (!m_conditionSet.areStillLive())
+ return false;
+
switch (m_type) {
case Replace:
if (!Heap::isMarked(m_oldStructure.get()))
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();
/*
- * 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
#include "CodeOrigin.h"
#include "MacroAssembler.h"
+#include "ObjectPropertyConditionSet.h"
#include "Opcode.h"
#include "PutKind.h"
#include "PutPropertySlot.h"
PutByIdAccess()
: m_type(Invalid)
- , m_chainCount(UINT_MAX)
{
}
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;
JSCell* owner,
AccessType accessType,
Structure* structure,
- StructureChain* chain,
- unsigned chainCount,
+ const ObjectPropertyConditionSet& conditionSet,
PutPropertySlot::PutValueFunc customSetter,
PassRefPtr<JITStubRoutine> stubRoutine)
{
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;
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
{
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;
};
--- /dev/null
+/*
+ * 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
--- /dev/null
+/*
+ * 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
+
/*
- * 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
|| 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)
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);
}
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;
}
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:
locker, profiledBlock, *stub->m_callLinkInfo, callExitSiteData));
variant = PutByIdVariant::setter(
- structure, complexGetStatus.offset(), complexGetStatus.chain(),
+ structure, complexGetStatus.offset(), complexGetStatus.conditionSet(),
WTF::move(callLinkStatus));
} }
break;
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);
}
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);
}
/*
- * 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
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);
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;
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)
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;
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
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(">");
/*
- * 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
#ifndef PutByIdVariant_h
#define PutByIdVariant_h
-#include "IntendedStructureChain.h"
+#include "ObjectPropertyConditionSet.h"
#include "PropertyOffset.h"
#include "StructureSet.h"
PutByIdVariant()
: m_kind(NotSet)
, m_newStructure(nullptr)
- , m_alternateBase(nullptr)
, m_offset(invalidOffset)
{
}
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; }
return m_oldStructure;
}
+ const StructureSet& structureSet() const
+ {
+ return structure();
+ }
+
const StructureSet& oldStructure() const
{
ASSERT(kind() == Transition || kind() == Replace || kind() == Setter);
bool reallocatesStorage() const;
bool makesCalls() const;
- const ConstantStructureCheckVector& constantChecks() const
- {
- return m_constantChecks;
- }
+ const ObjectPropertyConditionSet& conditionSet() const { return m_conditionSet; }
PropertyOffset offset() const
{
return m_offset;
}
- JSObject* alternateBase() const
- {
- ASSERT(kind() == Setter);
- return m_alternateBase;
- }
-
- StructureSet baseStructure() const;
-
CallLinkStatus* callLinkStatus() const
{
ASSERT(kind() == Setter);
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;
};
/*
- * 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
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));
ASSERT(holderRef->m_stubInfo == stubInfo);
}
- return holderRef->addWatchpoint();
+ return holderRef->addWatchpoint(key);
}
} // namespace JSC
/*
- * 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
#ifndef StructureStubClearingWatchpoint_h
#define StructureStubClearingWatchpoint_h
+#include "ObjectPropertyCondition.h"
#include "Watchpoint.h"
#if ENABLE(JIT)
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))
{
}
virtual ~StructureStubClearingWatchpoint();
static StructureStubClearingWatchpoint* push(
+ const ObjectPropertyCondition& key,
WatchpointsOnStructureStubInfo& holder,
std::unique_ptr<StructureStubClearingWatchpoint>& head);
virtual void fireInternal(const FireDetail&) override;
private:
+ ObjectPropertyCondition m_key;
WatchpointsOnStructureStubInfo& m_holder;
std::unique_ptr<StructureStubClearingWatchpoint> m_next;
};
~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; }
/*
- * 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
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
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:
#include "Instruction.h"
#include "JITStubRoutine.h"
#include "MacroAssembler.h"
+#include "ObjectPropertyConditionSet.h"
#include "Opcode.h"
#include "PolymorphicAccessStructureList.h"
#include "RegisterSet.h"
// 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;
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)
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;
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;
// 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) {
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);
}
--- /dev/null
+/*
+ * 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)
+
/*
- * 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
/*
- * 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)
--- /dev/null
+/*
+ * 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
+
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&);
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();
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;
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;
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;
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(
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));
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;
// 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),
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;
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;
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());
}
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);
VirtualRegister(registerOffset)).toLocal());
int nextRegister = registerOffset + JSStack::CallFrameHeaderSize;
- set(VirtualRegister(nextRegister++), originalBase, ImmediateNakedSet);
+ set(VirtualRegister(nextRegister++), base, ImmediateNakedSet);
set(VirtualRegister(nextRegister++), value, ImmediateNakedSet);
handleCall(
case GlobalProperty:
case GlobalPropertyWithVarInjectionChecks: {
SpeculatedType prediction = getPrediction();
+
GetByIdStatus status = GetByIdStatus::computeFor(structure, uid);
if (status.state() != GetByIdStatus::Simple
|| status.numVariants() != 1
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:
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;
trackedReferences.check(inlineCallFrame->calleeRecovery.constant());
}
}
+
+ for (AdaptiveStructureWatchpoint* watchpoint : adaptiveStructureWatchpoints)
+ watchpoint->key().validateReferences(trackedReferences);
}
} } // namespace JSC::DFG
#if ENABLE(DFG_JIT)
#include "CodeBlockJettisoningWatchpoint.h"
+#include "DFGAdaptiveInferredPropertyValueWatchpoint.h"
+#include "DFGAdaptiveStructureWatchpoint.h"
#include "DFGJumpReplacement.h"
#include "InlineCallFrameSet.h"
#include "JSCell.h"
Vector<WriteBarrier<JSCell>> weakReferences;
Vector<WriteBarrier<Structure>> weakStructureReferences;
Bag<CodeBlockJettisoningWatchpoint> watchpoints;
+ Bag<AdaptiveStructureWatchpoint> adaptiveStructureWatchpoints;
+ Bag<AdaptiveInferredPropertyValueWatchpoint> adaptiveInferredPropertyValueWatchpoints;
Vector<JumpReplacement> jumpReplacements;
RefPtr<Profiler::Compilation> compilation;
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;
}
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;
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;
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);
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);
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)
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);
}
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() { }
m_bufferViews.addLazily(view);
}
+void DesiredWatchpoints::addLazily(const ObjectPropertyCondition& key)
+{
+ m_adaptiveStructureSets.addLazily(key);
+}
+
bool DesiredWatchpoints::consider(Structure* structure)
{
if (!structure->dfgShouldWatch())
m_inlineSets.reallyAdd(codeBlock, commonData);
m_inferredValues.reallyAdd(codeBlock, commonData);
m_bufferViews.reallyAdd(codeBlock, commonData);
+ m_adaptiveStructureSets.reallyAdd(codeBlock, commonData);
}
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
#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 {
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;
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;
}
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;
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&);
{
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
/*
- * 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
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)
/*
- * 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
class CodeBlock;
class JSCell;
+class JSValue;
class SlotVisitor;
class VM;
~DesiredWeakReferences();
void addLazily(JSCell*);
+ void addLazily(JSValue);
bool contains(JSCell*);
void reallyAdd(VM&, CommonData*);
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();
if (value->pointsToHeap())
out.print(" ", inContext(*value, &myContext), "\n");
}
+
+ out.print(inContext(watchpoints(), &myContext));
if (!myContext.isEmpty()) {
myContext.dump(out);
}
}
+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);
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);
}
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;
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*);
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;
--- /dev/null
+/*
+ * 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)
+
--- /dev/null
+/*
+ * 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
+
#include "DFGCommon.h"
#include "DFGEpoch.h"
#include "DFGLazyJSValue.h"
+#include "DFGMultiGetByOffsetData.h"
#include "DFGNodeFlags.h"
#include "DFGNodeOrigin.h"
#include "DFGNodeType.h"
unsigned identifierNumber;
};
-struct MultiGetByOffsetData {
- unsigned identifierNumber;
- Vector<GetByIdVariant, 2> variants;
-};
-
struct MultiPutByOffsetData {
unsigned identifierNumber;
Vector<PutByIdVariant, 2> variants;
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;
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;
}
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:
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)));
}
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);
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);
#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)
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(
}
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
asObject(cell)->flattenDictionaryObject(vm);
return RetryCacheLater;
}
- ASSERT(!structure->isUncacheableDictionary());
- if (typeInfo.hasImpureGetOwnPropertySlot() && !typeInfo.newImpurePropertyFiresWatchpoints())
+ if (!structure->propertyAccessesAreCacheable())
return GiveUpOnCache;
return AttemptToCache;
}
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);
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);
list->addAccess(GetByIdAccess(
*vm, codeBlock->ownerExecutable(), accessType, stubRoutine, structure,
- prototypeChain, count));
+ conditionSet));
patchJumpToGetByIdStub(codeBlock, stubInfo, stubRoutine.get());
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
// 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;
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;
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())
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)
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(
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;
}
&& 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),
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()));
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 {
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()),
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()));
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;
return GiveUpOnCache;
}
- StructureChain* chain = structure->prototypeChain(exec);
RefPtr<JITStubRoutine> stubRoutine;
{
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);
+++ /dev/null
-/*
- * 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