Ignore:
Timestamp:
Oct 3, 2017, 6:53:18 PM (8 years ago)
Author:
[email protected]
Message:

Implement polymorphic prototypes
https://bugs.webkit.org/show_bug.cgi?id=176391

Reviewed by Filip Pizlo.

JSTests:

  • microbenchmarks/poly-proto-access.js: Added.

(assert):
(foo.C):
(foo.C.prototype.get bar):
(foo):
(bar):

  • microbenchmarks/poly-proto-put-transition-speed.js: Added.

(assert):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(performSet):

  • microbenchmarks/poly-proto-setter-speed.js: Added.

(assert):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo.C.prototype.set p):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(performSet):

  • stress/constructor-with-return.js:

(i.tests.forEach.Constructor):
(i.tests.forEach):
(tests.forEach.Constructor): Deleted.
(tests.forEach): Deleted.

  • stress/dom-jit-with-poly-proto.js: Added.

(assert):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(validate):

  • stress/poly-proto-custom-value-and-accessor.js: Added.

(assert):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(items.forEach):
(set get for):

  • stress/poly-proto-intrinsic-getter-correctness.js: Added.

(assert):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(foo):

  • stress/poly-proto-miss.js: Added.

(makePolyProtoInstanceWithNullPrototype.foo.C):
(makePolyProtoInstanceWithNullPrototype.foo):
(makePolyProtoInstanceWithNullPrototype):
(assert):
(validate):

  • stress/poly-proto-op-in-caching.js: Added.

(assert):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(validate):
(validate2):

  • stress/poly-proto-put-transition.js: Added.

(assert):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(performSet):
(i.obj.proto.set p):

  • stress/poly-proto-set-prototype.js: Added.

(assert):
(let.alternateProto.get x):
(let.alternateProto2.get y):
(let.alternateProto2.get x):
(foo.C):
(foo):
(validate):

  • stress/poly-proto-setter.js: Added.

(assert):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo.C.prototype.set p):
(makePolyProtoObject.foo.C.prototype.get p):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(performSet):

  • stress/poly-proto-using-inheritance.js: Added.

(assert):
(foo.C):
(foo.C.prototype.get baz):
(foo):
(bar.C):
(bar):
(validate):

  • stress/primitive-poly-proto.js: Added.

(makePolyProtoInstance.foo.C):
(makePolyProtoInstance.foo):
(makePolyProtoInstance):
(assert):
(validate):

  • stress/prototype-is-not-js-object.js: Added.

(foo.bar):
(foo):
(assert):
(validate):

  • stress/try-get-by-id-poly-proto.js: Added.

(assert):
(makePolyProtoObject.foo.C):
(makePolyProtoObject.foo):
(makePolyProtoObject):
(tryGetByIdText):
(x.proto.get bar):
(validate):

  • typeProfiler/overflow.js:

Source/JavaScriptCore:

This patch changes JSC's object model with respect to where the prototype
of an object is stored. Previously, it was always stored as
a constant value inside Structure. So an object's structure used to
always tell you what its prototype is. Anytime an object changed
its prototype, it would do a structure transition. This enables
a large class of optimizations: just by doing a structure check,
we know what the prototype is.

However, this design falls down when you have many objects that
have the same shape, but only differ in what their prototype value
is. This arises in many JS programs. A simple, and probably common, example
is when the program has a constructor inside of a function:
`
function foo() {

class C {

constructor() { this.field1 = 42; ...; this.fieldN = 42; }
method1() { doStuffWith(this.field); }
method2() { doStuffWith(this.field); }

}
let c = new C;
do things with c;
}

repeatedly call foo() here.
`

Before this patch, in the above program, each time new C created an
object, it would create an object with a different structure. The
reason for this is that each time foo is called, there is a new
instance of C.prototype. However, each new C that was created
with have identical shape sans its prototype value. This would
cause all ICs that used c to quickly give up on any form of caching
because they would see too many structures and give up and permanently
divert control flow to the slow path.

This patch fixes this issue by expanding the notion of where the prototype
of an object is stored. There are now two notions of where the prototype
is stored. A Structure can now be in two modes:

  1. Mono proto mode. This is the same mode as we used to have. It means

the structure itself has a constant prototype value.

  1. Poly proto mode. This means the structure knows nothing about the

prototype value itself. Objects with this structure store their prototype
in normal object field storage. The structure will tell you the offset of
this prototype inside the object's storage. As of today, we only reserve
inline slots for the prototype field because poly proto only occurs
for JSFinalObject. However, this will be expanded to support out of line
offsets in a future patch when we extend poly proto to work when we inherit
from builtin types like Map and Array.

In this initial patch, we do poly proto style inline caching whenever
we see an object that is poly proto or if an object in its prototype lookup
chain is poly proto. Poly proto ICs work by verifying the lookup chain
at runtime. This essentially boils down to performing structure checks
up the prototype chain. In a future patch, we're going to extend object
property condition set to work with objects that don't have poly proto bases.

Initially, accesses that have poly proto access chains will always turn
into GetById/PutById in the DFG. In a future patch, I'm going to teach
the DFG how to inline certain accesses that have poly proto in the access
chain.

One of most interesting parts about this patch is how we decide when to go
poly proto. This patch uses a profiling based approach. An IC will inform
a watchpoint that it sees an opportunity when two Structure's are structurally
the same, sans the base object's prototype. This means that two structures
have equivalent shapes all the way up the prototype chain. To support fast
structural comparison, we compute a hash for a structure based on the properties
it has. We compute this hash as we add properties to the structure. This
computation is nearly free since we always add UniquedStringImpl*'s which
already have their hashes computed. To compare structural equivalence, we
just compare hash values all the way up the prototype chain. This means we
can get hash conflicts between two structures, but it's extremely rare. First,
it'll be rare for two structures to have the same hash. Secondly, we only
consider structures originating from the same executable.

How we set up this poly proto watchpoint is crucial to its design. When we create_this
an object originating from some executable, that executable will create a Box<InlineWatchpointSet>.
Each structure that originates from this executable will get a copy of that
Box<InlineWatchpointSet>. As that structure transitions to new structures,
they too will get a copy of that Box<InilneWatchpointSet>. Therefore, when
invalidating an arbitrary structure's poly proto watchpoint, we will know
the next time we create_this from that executable that it had been
invalidated, and that we should create an object with a poly proto
structure. We also use the pointer value of this Box<InlineWatchpointSet>
to determine if two structures originated from the same executable. This
pruning will severely limit the chances of getting a hash conflict in practice.

This patch is neutral on my MBP on traditional JS benchmarks like Octane/Kraken/Sunspider.
It may be a 1-2% ARES-6 progression.

This patch is between neutral and a 9x progression on the various tests
I added. Most of the microbenchmarks are progressed by at least 50%.

  • JavaScriptCore.xcodeproj/project.pbxproj:
  • Sources.txt:
  • builtins/BuiltinNames.cpp:
  • builtins/BuiltinNames.h:

(JSC::BuiltinNames::BuiltinNames):
(JSC::BuiltinNames::underscoreProtoPrivateName const):

  • bytecode/AccessCase.cpp:

(JSC::AccessCase::AccessCase):
(JSC::AccessCase::create):
(JSC::AccessCase::commit):
(JSC::AccessCase::guardedByStructureCheck const):
(JSC::AccessCase::canReplace const):
(JSC::AccessCase::dump const):
(JSC::AccessCase::visitWeak const):
(JSC::AccessCase::propagateTransitions const):
(JSC::AccessCase::generateWithGuard):
(JSC::AccessCase::generateImpl):

  • bytecode/AccessCase.h:

(JSC::AccessCase::usesPolyProto const):
(JSC::AccessCase::AccessCase):

  • bytecode/CodeBlock.cpp:

(JSC::CodeBlock::finishCreation):

  • bytecode/GetByIdStatus.cpp:

(JSC::GetByIdStatus::computeForStubInfoWithoutExitSiteFeedback):

  • bytecode/GetterSetterAccessCase.cpp:

(JSC::GetterSetterAccessCase::GetterSetterAccessCase):
(JSC::GetterSetterAccessCase::create):

  • bytecode/GetterSetterAccessCase.h:
  • bytecode/InternalFunctionAllocationProfile.h:

(JSC::InternalFunctionAllocationProfile::createAllocationStructureFromBase):

  • bytecode/IntrinsicGetterAccessCase.cpp:

(JSC::IntrinsicGetterAccessCase::IntrinsicGetterAccessCase):

  • bytecode/IntrinsicGetterAccessCase.h:
  • bytecode/ModuleNamespaceAccessCase.cpp:

(JSC::ModuleNamespaceAccessCase::ModuleNamespaceAccessCase):

  • bytecode/ObjectAllocationProfile.cpp: Added.

(JSC::ObjectAllocationProfile::initializeProfile):
(JSC::ObjectAllocationProfile::possibleDefaultPropertyCount):

  • bytecode/ObjectAllocationProfile.h:

(JSC::ObjectAllocationProfile::clear):
(JSC::ObjectAllocationProfile::initialize): Deleted.
(JSC::ObjectAllocationProfile::possibleDefaultPropertyCount): Deleted.

  • bytecode/ObjectPropertyConditionSet.cpp:
  • bytecode/PolyProtoAccessChain.cpp: Added.

(JSC::PolyProtoAccessChain::create):
(JSC::PolyProtoAccessChain::needImpurePropertyWatchpoint const):
(JSC::PolyProtoAccessChain::operator== const):
(JSC::PolyProtoAccessChain::dump const):

  • bytecode/PolyProtoAccessChain.h: Added.

(JSC::PolyProtoAccessChain::clone):
(JSC::PolyProtoAccessChain:: const):
(JSC::PolyProtoAccessChain::operator!= const):
(JSC::PolyProtoAccessChain::forEach const):

  • bytecode/PolymorphicAccess.cpp:

(JSC::PolymorphicAccess::addCases):
(JSC::PolymorphicAccess::regenerate):
(WTF::printInternal):

  • bytecode/PolymorphicAccess.h:

(JSC::AccessGenerationResult::shouldResetStub const):
(JSC::AccessGenerationState::AccessGenerationState):

  • bytecode/PropertyCondition.cpp:

(JSC::PropertyCondition::isStillValidAssumingImpurePropertyWatchpoint const):

  • bytecode/ProxyableAccessCase.cpp:

(JSC::ProxyableAccessCase::ProxyableAccessCase):
(JSC::ProxyableAccessCase::create):

  • bytecode/ProxyableAccessCase.h:
  • bytecode/PutByIdStatus.cpp:

(JSC::PutByIdStatus::computeForStubInfo):

  • bytecode/StructureStubInfo.cpp:

(JSC::StructureStubInfo::addAccessCase):

  • dfg/DFGByteCodeParser.cpp:

(JSC::DFG::ByteCodeParser::load):
(JSC::DFG::ByteCodeParser::parseBlock):

  • dfg/DFGGraph.cpp:

(JSC::DFG::Graph::canDoFastSpread):

  • dfg/DFGOperations.cpp:
  • dfg/DFGSpeculativeJIT.cpp:

(JSC::DFG::SpeculativeJIT::compileInstanceOfForObject):
(JSC::DFG::SpeculativeJIT::compileInstanceOf):

  • dfg/DFGSpeculativeJIT.h:
  • dfg/DFGSpeculativeJIT64.cpp:

(JSC::DFG::SpeculativeJIT::compile):

  • ftl/FTLLowerDFGToB3.cpp:

(JSC::FTL::DFG::LowerDFGToB3::compileInstanceOf):

  • jit/JITOpcodes.cpp:

(JSC::JIT::emit_op_instanceof):

  • jit/JITOpcodes32_64.cpp:

(JSC::JIT::emit_op_instanceof):

  • jit/Repatch.cpp:

(JSC::tryCacheGetByID):
(JSC::tryCachePutByID):
(JSC::tryRepatchIn):

  • jsc.cpp:

(WTF::DOMJITGetterBaseJSObject::DOMJITGetterBaseJSObject):
(WTF::DOMJITGetterBaseJSObject::createStructure):
(WTF::DOMJITGetterBaseJSObject::create):
(WTF::DOMJITGetterBaseJSObject::DOMJITAttribute::DOMJITAttribute):
(WTF::DOMJITGetterBaseJSObject::DOMJITAttribute::slowCall):
(WTF::DOMJITGetterBaseJSObject::DOMJITAttribute::callDOMGetter):
(WTF::DOMJITGetterBaseJSObject::customGetter):
(WTF::DOMJITGetterBaseJSObject::finishCreation):
(GlobalObject::finishCreation):
(functionCreateDOMJITGetterBaseJSObject):

  • llint/LLIntSlowPaths.cpp:

(JSC::LLInt::LLINT_SLOW_PATH_DECL):

  • runtime/ArrayPrototype.cpp:

(JSC::holesMustForwardToPrototype):
(JSC::fastJoin):
(JSC::arrayProtoFuncReverse):
(JSC::moveElements):

  • runtime/ClonedArguments.cpp:

(JSC::ClonedArguments::createEmpty):
(JSC::ClonedArguments::createWithInlineFrame):
(JSC::ClonedArguments::createWithMachineFrame):
(JSC::ClonedArguments::createByCopyingFrom):

  • runtime/CommonSlowPaths.cpp:

(JSC::SLOW_PATH_DECL):

  • runtime/FunctionExecutable.cpp:

(JSC::FunctionExecutable::visitChildren):

  • runtime/FunctionExecutable.h:
  • runtime/FunctionRareData.cpp:

(JSC::FunctionRareData::initializeObjectAllocationProfile):

  • runtime/FunctionRareData.h:
  • runtime/InternalFunction.cpp:

(JSC::InternalFunction::createSubclassStructureSlow):

  • runtime/JSArray.cpp:

(JSC::JSArray::fastSlice):
(JSC::JSArray::shiftCountWithArrayStorage):
(JSC::JSArray::shiftCountWithAnyIndexingType):
(JSC::JSArray::isIteratorProtocolFastAndNonObservable):

  • runtime/JSArrayInlines.h:

(JSC::JSArray::canFastCopy):

  • runtime/JSCJSValue.cpp:

(JSC::JSValue::dumpInContextAssumingStructure const):

  • runtime/JSFunction.cpp:

(JSC::JSFunction::prototypeForConstruction):
(JSC::JSFunction::allocateAndInitializeRareData):
(JSC::JSFunction::initializeRareData):
(JSC::JSFunction::getOwnPropertySlot):

  • runtime/JSFunction.h:
  • runtime/JSMap.cpp:

(JSC::JSMap::isIteratorProtocolFastAndNonObservable):
(JSC::JSMap::canCloneFastAndNonObservable):

  • runtime/JSObject.cpp:

(JSC::JSObject::putInlineSlow):
(JSC::JSObject::createInitialIndexedStorage):
(JSC::JSObject::createArrayStorage):
(JSC::JSObject::convertUndecidedToArrayStorage):
(JSC::JSObject::convertInt32ToArrayStorage):
(JSC::JSObject::convertDoubleToArrayStorage):
(JSC::JSObject::convertContiguousToArrayStorage):
(JSC::JSObject::ensureInt32Slow):
(JSC::JSObject::ensureDoubleSlow):
(JSC::JSObject::ensureContiguousSlow):
(JSC::JSObject::ensureArrayStorageSlow):
(JSC::JSObject::setPrototypeDirect):
(JSC::JSObject::ordinaryToPrimitive const):
(JSC::JSObject::putByIndexBeyondVectorLength):
(JSC::JSObject::putDirectIndexSlowOrBeyondVectorLength):
(JSC::JSObject::getEnumerableLength):
(JSC::JSObject::anyObjectInChainMayInterceptIndexedAccesses const):
(JSC::JSObject::prototypeChainMayInterceptStoreTo):
(JSC::JSObject::needsSlowPutIndexing const):
(JSC::JSObject::suggestedArrayStorageTransition const):

  • runtime/JSObject.h:

(JSC::JSObject::finishCreation):
(JSC::JSObject::getPrototypeDirect const):
(JSC::JSObject::getPropertySlot):

  • runtime/JSObjectInlines.h:

(JSC::JSObject::getPropertySlot):
(JSC::JSObject::getNonIndexPropertySlot):
(JSC::JSObject::putInlineForJSObject):

  • runtime/JSPropertyNameEnumerator.h:

(JSC::propertyNameEnumerator):

  • runtime/JSSet.cpp:

(JSC::JSSet::isIteratorProtocolFastAndNonObservable):
(JSC::JSSet::canCloneFastAndNonObservable):

  • runtime/LazyClassStructure.h:

(JSC::LazyClassStructure::prototypeConcurrently const): Deleted.

  • runtime/Operations.cpp:

(JSC::normalizePrototypeChain):

  • runtime/Operations.h:
  • runtime/Options.h:
  • runtime/PrototypeMap.cpp:

(JSC::PrototypeMap::createEmptyStructure):
(JSC::PrototypeMap::emptyStructureForPrototypeFromBaseStructure):
(JSC::PrototypeMap::emptyObjectStructureForPrototype):
(JSC::PrototypeMap::clearEmptyObjectStructureForPrototype):

  • runtime/PrototypeMap.h:
  • runtime/Structure.cpp:

(JSC::Structure::Structure):
(JSC::Structure::create):
(JSC::Structure::holesMustForwardToPrototype const):
(JSC::Structure::changePrototypeTransition):
(JSC::Structure::isCheapDuringGC):
(JSC::Structure::toStructureShape):
(JSC::Structure::dump const):
(JSC::Structure::canCachePropertyNameEnumerator const):
(JSC::Structure::anyObjectInChainMayInterceptIndexedAccesses const): Deleted.
(JSC::Structure::needsSlowPutIndexing const): Deleted.
(JSC::Structure::suggestedArrayStorageTransition const): Deleted.
(JSC::Structure::prototypeForLookup const): Deleted.
(JSC::Structure::prototypeChainMayInterceptStoreTo): Deleted.
(JSC::Structure::canUseForAllocationsOf): Deleted.

  • runtime/Structure.h:
  • runtime/StructureChain.h:
  • runtime/StructureInlines.h:

(JSC::Structure::create):
(JSC::Structure::storedPrototypeObject const):
(JSC::Structure::storedPrototypeStructure const):
(JSC::Structure::storedPrototype const):
(JSC::prototypeForLookupPrimitiveImpl):
(JSC::Structure::prototypeForLookup const):
(JSC::Structure::prototypeChain const):
(JSC::Structure::isValid const):
(JSC::Structure::add):
(JSC::Structure::setPropertyTable):
(JSC::Structure::shouldConvertToPolyProto):

  • runtime/StructureRareData.h:
  • runtime/TypeProfilerLog.cpp:

(JSC::TypeProfilerLog::processLogEntries):

  • runtime/TypeSet.cpp:

(JSC::TypeSet::addTypeInformation):

  • runtime/TypeSet.h:
  • runtime/WriteBarrier.h:

(JSC::WriteBarrierBase<Unknown>::isInt32 const):

Source/WTF:

  • wtf/Box.h:

(WTF::Box::operator bool const):
(WTF::Box::operator bool): Deleted.
Make Box movable. Also ensure its operator bool doesn't do an atomic increment.

  • wtf/RefPtr.h:

(WTF::RefPtr::operator bool const):
Add explicit operator bool() for RefPtr.

Tools:

  • Scripts/run-jsc-stress-tests:
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/Source/JavaScriptCore/jit/Repatch.cpp

    r222671 r222827  
    245245        }
    246246
     247        std::unique_ptr<PolyProtoAccessChain> prototypeAccessChain;
     248
    247249        PropertyOffset offset = slot.isUnset() ? invalidOffset : slot.cachedOffset();
    248250
     
    256258                structure->flattenDictionaryStructure(vm, jsCast<JSObject*>(baseCell));
    257259            }
    258            
     260
    259261            if (slot.isUnset() && structure->typeInfo().getOwnPropertySlotIsImpureForPropertyAbsence())
    260262                return GiveUpOnCache;
    261263
    262             if (slot.isUnset()) {
    263                 conditionSet = generateConditionsForPropertyMiss(
    264                     vm, codeBlock, exec, structure, propertyName.impl());
    265             } else {
    266                 conditionSet = generateConditionsForPrototypePropertyHit(
    267                     vm, codeBlock, exec, structure, slot.slotBase(),
    268                     propertyName.impl());
    269             }
    270            
    271             if (!conditionSet.isValid())
     264            bool usesPolyProto;
     265            prototypeAccessChain = PolyProtoAccessChain::create(exec->lexicalGlobalObject(), baseCell, slot, usesPolyProto);
     266            if (!prototypeAccessChain) {
     267                // It's invalid to access this prototype property.
    272268                return GiveUpOnCache;
    273 
    274             offset = slot.isUnset() ? invalidOffset : conditionSet.slotBaseCondition().offset();
     269            }
     270
     271            if (!usesPolyProto) {
     272                // We use ObjectPropertyConditionSet instead for faster accesses.
     273                prototypeAccessChain = nullptr;
     274
     275                if (slot.isUnset()) {
     276                    conditionSet = generateConditionsForPropertyMiss(
     277                        vm, codeBlock, exec, structure, propertyName.impl());
     278                } else {
     279                    conditionSet = generateConditionsForPrototypePropertyHit(
     280                        vm, codeBlock, exec, structure, slot.slotBase(),
     281                        propertyName.impl());
     282                }
     283
     284                if (!conditionSet.isValid())
     285                    return GiveUpOnCache;
     286            }
     287
     288            offset = slot.isUnset() ? invalidOffset : slot.cachedOffset();
    275289        }
    276290
     
    294308                RELEASE_ASSERT_NOT_REACHED();
    295309
    296             newCase = ProxyableAccessCase::create(vm, codeBlock, type, offset, structure, conditionSet, loadTargetFromProxy, slot.watchpointSet());
    297         } else if (!loadTargetFromProxy && getter && IntrinsicGetterAccessCase::canEmitIntrinsicGetter(getter, structure))
     310            newCase = ProxyableAccessCase::create(vm, codeBlock, type, offset, structure, conditionSet, loadTargetFromProxy, slot.watchpointSet(), WTFMove(prototypeAccessChain));
     311        } else if (!loadTargetFromProxy && getter && IntrinsicGetterAccessCase::canEmitIntrinsicGetter(getter, structure) && !prototypeAccessChain) {
     312            // FIXME: We should make this work with poly proto, but for our own sanity, we probably
     313            // want to do a pointer check on the actual getter. A good time to make this work would
     314            // be when we can inherit from builtin types in poly proto fashion:
     315            // https://bugs.webkit.org/show_bug.cgi?id=177318
    298316            newCase = IntrinsicGetterAccessCase::create(vm, codeBlock, slot.cachedOffset(), structure, conditionSet, getter);
    299         else {
     317        } else {
    300318            if (slot.isCacheableValue() || slot.isUnset()) {
    301319                newCase = ProxyableAccessCase::create(vm, codeBlock, slot.isUnset() ? AccessCase::Miss : AccessCase::Load,
    302                     offset, structure, conditionSet, loadTargetFromProxy, slot.watchpointSet());
     320                    offset, structure, conditionSet, loadTargetFromProxy, slot.watchpointSet(), WTFMove(prototypeAccessChain));
    303321            } else {
    304322                AccessCase::AccessType type;
     
    317335                    slot.watchpointSet(), slot.isCacheableCustom() ? slot.customGetter() : nullptr,
    318336                    slot.isCacheableCustom() ? slot.slotBase() : nullptr,
    319                     domAttribute);
     337                    domAttribute, WTFMove(prototypeAccessChain));
    320338            }
    321339        }
     
    387405
    388406    std::unique_ptr<AccessCase> newCase;
     407    JSCell* baseCell = baseValue.asCell();
    389408
    390409    if (slot.base() == baseValue && slot.isCacheablePut()) {
     
    430449            ASSERT(newStructure->isObject());
    431450           
     451            std::unique_ptr<PolyProtoAccessChain> prototypeAccessChain;
    432452            ObjectPropertyConditionSet conditionSet;
    433453            if (putKind == NotDirect) {
    434                 conditionSet =
    435                     generateConditionsForPropertySetterMiss(
    436                         vm, codeBlock, exec, newStructure, ident.impl());
    437                 if (!conditionSet.isValid())
     454                bool usesPolyProto;
     455                prototypeAccessChain = PolyProtoAccessChain::create(exec->lexicalGlobalObject(), baseCell, nullptr, usesPolyProto);
     456                if (!prototypeAccessChain) {
     457                    // It's invalid to access this prototype property.
    438458                    return GiveUpOnCache;
    439             }
    440 
    441             newCase = AccessCase::create(vm, codeBlock, offset, structure, newStructure, conditionSet);
     459                }
     460
     461                if (!usesPolyProto) {
     462                    prototypeAccessChain = nullptr;
     463                    conditionSet =
     464                        generateConditionsForPropertySetterMiss(
     465                            vm, codeBlock, exec, newStructure, ident.impl());
     466                    if (!conditionSet.isValid())
     467                        return GiveUpOnCache;
     468                }
     469
     470            }
     471
     472            newCase = AccessCase::create(vm, codeBlock, offset, structure, newStructure, conditionSet, WTFMove(prototypeAccessChain));
    442473        }
    443474    } else if (slot.isCacheableCustom() || slot.isCacheableSetter()) {
    444475        if (slot.isCacheableCustom()) {
    445476            ObjectPropertyConditionSet conditionSet;
     477            std::unique_ptr<PolyProtoAccessChain> prototypeAccessChain;
    446478
    447479            if (slot.base() != baseValue) {
    448                 conditionSet =
    449                     generateConditionsForPrototypePropertyHit(
    450                         vm, codeBlock, exec, structure, slot.base(), ident.impl());
    451                 if (!conditionSet.isValid())
     480                bool usesPolyProto;
     481                prototypeAccessChain = PolyProtoAccessChain::create(exec->lexicalGlobalObject(), baseCell, slot.base(), usesPolyProto);
     482                if (!prototypeAccessChain) {
     483                    // It's invalid to access this prototype property.
    452484                    return GiveUpOnCache;
     485                }
     486
     487                if (!usesPolyProto) {
     488                    prototypeAccessChain = nullptr;
     489                    conditionSet =
     490                        generateConditionsForPrototypePropertyHit(
     491                            vm, codeBlock, exec, structure, slot.base(), ident.impl());
     492                    if (!conditionSet.isValid())
     493                        return GiveUpOnCache;
     494                }
    453495            }
    454496
    455497            newCase = GetterSetterAccessCase::create(
    456                 vm, codeBlock, slot.isCustomAccessor() ? AccessCase::CustomAccessorSetter : AccessCase::CustomValueSetter, structure, invalidOffset, conditionSet,
    457                 slot.customSetter(), slot.base());
     498                vm, codeBlock, slot.isCustomAccessor() ? AccessCase::CustomAccessorSetter : AccessCase::CustomValueSetter, structure, invalidOffset,
     499                conditionSet, WTFMove(prototypeAccessChain), slot.customSetter(), slot.base());
    458500        } else {
    459501            ObjectPropertyConditionSet conditionSet;
    460             PropertyOffset offset;
     502            std::unique_ptr<PolyProtoAccessChain> prototypeAccessChain;
     503            PropertyOffset offset = slot.cachedOffset();
    461504
    462505            if (slot.base() != baseValue) {
    463                 conditionSet =
    464                     generateConditionsForPrototypePropertyHit(
    465                         vm, codeBlock, exec, structure, slot.base(), ident.impl());
    466                 if (!conditionSet.isValid())
     506                bool usesPolyProto;
     507                prototypeAccessChain = PolyProtoAccessChain::create(exec->lexicalGlobalObject(), baseCell, slot.base(), usesPolyProto);
     508                if (!prototypeAccessChain) {
     509                    // It's invalid to access this prototype property.
    467510                    return GiveUpOnCache;
    468                 offset = conditionSet.slotBaseCondition().offset();
    469             } else
    470                 offset = slot.cachedOffset();
     511                }
     512
     513                if (!usesPolyProto) {
     514                    prototypeAccessChain = nullptr;
     515                    conditionSet =
     516                        generateConditionsForPrototypePropertyHit(
     517                            vm, codeBlock, exec, structure, slot.base(), ident.impl());
     518                    if (!conditionSet.isValid())
     519                        return GiveUpOnCache;
     520
     521                    RELEASE_ASSERT(offset == conditionSet.slotBaseCondition().offset());
     522                }
     523
     524            }
    471525
    472526            newCase = GetterSetterAccessCase::create(
    473                 vm, codeBlock, AccessCase::Setter, structure, offset, conditionSet);
     527                vm, codeBlock, AccessCase::Setter, structure, offset, conditionSet, WTFMove(prototypeAccessChain));
    474528        }
    475529    }
     
    518572    Structure* structure = base->structure(vm);
    519573   
     574    std::unique_ptr<PolyProtoAccessChain> prototypeAccessChain;
    520575    ObjectPropertyConditionSet conditionSet;
    521576    if (wasFound) {
    522577        if (slot.slotBase() != base) {
    523             conditionSet = generateConditionsForPrototypePropertyHit(
    524                 vm, codeBlock, exec, structure, slot.slotBase(), ident.impl());
     578            bool usesPolyProto;
     579            prototypeAccessChain = PolyProtoAccessChain::create(exec->lexicalGlobalObject(), base, slot, usesPolyProto);
     580            if (!prototypeAccessChain) {
     581                // It's invalid to access this prototype property.
     582                return GiveUpOnCache;
     583            }
     584            if (!usesPolyProto) {
     585                prototypeAccessChain = nullptr;
     586                conditionSet = generateConditionsForPrototypePropertyHit(
     587                    vm, codeBlock, exec, structure, slot.slotBase(), ident.impl());
     588            }
    525589        }
    526590    } else {
    527         conditionSet = generateConditionsForPropertyMiss(
    528             vm, codeBlock, exec, structure, ident.impl());
     591        bool usesPolyProto;
     592        prototypeAccessChain = PolyProtoAccessChain::create(exec->lexicalGlobalObject(), base, slot, usesPolyProto);
     593        if (!prototypeAccessChain) {
     594            // It's invalid to access this prototype property.
     595            return GiveUpOnCache;
     596        }
     597
     598        if (!usesPolyProto) {
     599            prototypeAccessChain = nullptr;
     600            conditionSet = generateConditionsForPropertyMiss(
     601                vm, codeBlock, exec, structure, ident.impl());
     602        }
    529603    }
    530604    if (!conditionSet.isValid())
     
    534608
    535609    std::unique_ptr<AccessCase> newCase = AccessCase::create(
    536         vm, codeBlock, wasFound ? AccessCase::InHit : AccessCase::InMiss, invalidOffset, structure, conditionSet);
     610        vm, codeBlock, wasFound ? AccessCase::InHit : AccessCase::InMiss, invalidOffset, structure, conditionSet, WTFMove(prototypeAccessChain));
    537611
    538612    AccessGenerationResult result = stubInfo.addAccessCase(locker, codeBlock, ident, WTFMove(newCase));
Note: See TracChangeset for help on using the changeset viewer.