Add logging to identify when the Page suspends scripted animations
[WebKit-https.git] / Source / JavaScriptCore / bytecode / PolymorphicAccess.cpp
1 /*
2  * Copyright (C) 2014-2017 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
24  */
25
26 #include "config.h"
27 #include "PolymorphicAccess.h"
28
29 #if ENABLE(JIT)
30
31 #include "BinarySwitch.h"
32 #include "CCallHelpers.h"
33 #include "CodeBlock.h"
34 #include "FullCodeOrigin.h"
35 #include "Heap.h"
36 #include "JITOperations.h"
37 #include "JSCInlines.h"
38 #include "LinkBuffer.h"
39 #include "StructureStubClearingWatchpoint.h"
40 #include "StructureStubInfo.h"
41 #include <wtf/CommaPrinter.h>
42 #include <wtf/ListDump.h>
43
44 namespace JSC {
45
46 static const bool verbose = false;
47
48 void AccessGenerationResult::dump(PrintStream& out) const
49 {
50     out.print(m_kind);
51     if (m_code)
52         out.print(":", m_code);
53 }
54
55 Watchpoint* AccessGenerationState::addWatchpoint(const ObjectPropertyCondition& condition)
56 {
57     return WatchpointsOnStructureStubInfo::ensureReferenceAndAddWatchpoint(
58         watchpoints, jit->codeBlock(), stubInfo, condition);
59 }
60
61 void AccessGenerationState::restoreScratch()
62 {
63     allocator->restoreReusedRegistersByPopping(*jit, preservedReusedRegisterState);
64 }
65
66 void AccessGenerationState::succeed()
67 {
68     restoreScratch();
69     success.append(jit->jump());
70 }
71
72 const RegisterSet& AccessGenerationState::liveRegistersForCall()
73 {
74     if (!m_calculatedRegistersForCallAndExceptionHandling)
75         calculateLiveRegistersForCallAndExceptionHandling();
76     return m_liveRegistersForCall;
77 }
78
79 const RegisterSet& AccessGenerationState::liveRegistersToPreserveAtExceptionHandlingCallSite()
80 {
81     if (!m_calculatedRegistersForCallAndExceptionHandling)
82         calculateLiveRegistersForCallAndExceptionHandling();
83     return m_liveRegistersToPreserveAtExceptionHandlingCallSite;
84 }
85
86 static RegisterSet calleeSaveRegisters()
87 {
88     RegisterSet result = RegisterSet::registersToNotSaveForJSCall();
89     result.filter(RegisterSet::registersToNotSaveForCCall());
90     return result;
91 }
92
93 const RegisterSet& AccessGenerationState::calculateLiveRegistersForCallAndExceptionHandling()
94 {
95     if (!m_calculatedRegistersForCallAndExceptionHandling) {
96         m_calculatedRegistersForCallAndExceptionHandling = true;
97
98         m_liveRegistersToPreserveAtExceptionHandlingCallSite = jit->codeBlock()->jitCode()->liveRegistersToPreserveAtExceptionHandlingCallSite(jit->codeBlock(), stubInfo->callSiteIndex);
99         m_needsToRestoreRegistersIfException = m_liveRegistersToPreserveAtExceptionHandlingCallSite.numberOfSetRegisters() > 0;
100         if (m_needsToRestoreRegistersIfException)
101             RELEASE_ASSERT(JITCode::isOptimizingJIT(jit->codeBlock()->jitType()));
102
103         m_liveRegistersForCall = RegisterSet(m_liveRegistersToPreserveAtExceptionHandlingCallSite, allocator->usedRegisters());
104         m_liveRegistersForCall.exclude(calleeSaveRegisters());
105     }
106     return m_liveRegistersForCall;
107 }
108
109 auto AccessGenerationState::preserveLiveRegistersToStackForCall(const RegisterSet& extra) -> SpillState
110 {
111     RegisterSet liveRegisters = liveRegistersForCall();
112     liveRegisters.merge(extra);
113     
114     unsigned extraStackPadding = 0;
115     unsigned numberOfStackBytesUsedForRegisterPreservation = ScratchRegisterAllocator::preserveRegistersToStackForCall(*jit, liveRegisters, extraStackPadding);
116     return SpillState {
117         WTFMove(liveRegisters),
118         numberOfStackBytesUsedForRegisterPreservation
119     };
120 }
121
122 void AccessGenerationState::restoreLiveRegistersFromStackForCallWithThrownException(const SpillState& spillState)
123 {
124     // Even if we're a getter, we don't want to ignore the result value like we normally do
125     // because the getter threw, and therefore, didn't return a value that means anything.
126     // Instead, we want to restore that register to what it was upon entering the getter
127     // inline cache. The subtlety here is if the base and the result are the same register,
128     // and the getter threw, we want OSR exit to see the original base value, not the result
129     // of the getter call.
130     RegisterSet dontRestore = spillState.spilledRegisters;
131     // As an optimization here, we only need to restore what is live for exception handling.
132     // We can construct the dontRestore set to accomplish this goal by having it contain only
133     // what is live for call but not live for exception handling. By ignoring things that are
134     // only live at the call but not the exception handler, we will only restore things live
135     // at the exception handler.
136     dontRestore.exclude(liveRegistersToPreserveAtExceptionHandlingCallSite());
137     restoreLiveRegistersFromStackForCall(spillState, dontRestore);
138 }
139
140 void AccessGenerationState::restoreLiveRegistersFromStackForCall(const SpillState& spillState, const RegisterSet& dontRestore)
141 {
142     unsigned extraStackPadding = 0;
143     ScratchRegisterAllocator::restoreRegistersFromStackForCall(*jit, spillState.spilledRegisters, dontRestore, spillState.numberOfStackBytesUsedForRegisterPreservation, extraStackPadding);
144 }
145
146 CallSiteIndex AccessGenerationState::callSiteIndexForExceptionHandlingOrOriginal()
147 {
148     if (!m_calculatedRegistersForCallAndExceptionHandling)
149         calculateLiveRegistersForCallAndExceptionHandling();
150
151     if (!m_calculatedCallSiteIndex) {
152         m_calculatedCallSiteIndex = true;
153
154         if (m_needsToRestoreRegistersIfException)
155             m_callSiteIndex = jit->codeBlock()->newExceptionHandlingCallSiteIndex(stubInfo->callSiteIndex);
156         else
157             m_callSiteIndex = originalCallSiteIndex();
158     }
159
160     return m_callSiteIndex;
161 }
162
163 const HandlerInfo& AccessGenerationState::originalExceptionHandler()
164 {
165     if (!m_calculatedRegistersForCallAndExceptionHandling)
166         calculateLiveRegistersForCallAndExceptionHandling();
167
168     RELEASE_ASSERT(m_needsToRestoreRegistersIfException);
169     HandlerInfo* exceptionHandler = jit->codeBlock()->handlerForIndex(stubInfo->callSiteIndex.bits());
170     RELEASE_ASSERT(exceptionHandler);
171     return *exceptionHandler;
172 }
173
174 CallSiteIndex AccessGenerationState::originalCallSiteIndex() const { return stubInfo->callSiteIndex; }
175
176 void AccessGenerationState::emitExplicitExceptionHandler()
177 {
178     restoreScratch();
179     jit->copyCalleeSavesToVMEntryFrameCalleeSavesBuffer(m_vm);
180     if (needsToRestoreRegistersIfException()) {
181         // To the JIT that produces the original exception handling
182         // call site, they will expect the OSR exit to be arrived
183         // at from genericUnwind. Therefore we must model what genericUnwind
184         // does here. I.e, set callFrameForCatch and copy callee saves.
185
186         jit->storePtr(GPRInfo::callFrameRegister, m_vm.addressOfCallFrameForCatch());
187         CCallHelpers::Jump jumpToOSRExitExceptionHandler = jit->jump();
188
189         // We don't need to insert a new exception handler in the table
190         // because we're doing a manual exception check here. i.e, we'll
191         // never arrive here from genericUnwind().
192         HandlerInfo originalHandler = originalExceptionHandler();
193         jit->addLinkTask(
194             [=] (LinkBuffer& linkBuffer) {
195                 linkBuffer.link(jumpToOSRExitExceptionHandler, originalHandler.nativeCode);
196             });
197     } else {
198         jit->setupArguments(CCallHelpers::TrustedImmPtr(&m_vm), GPRInfo::callFrameRegister);
199         CCallHelpers::Call lookupExceptionHandlerCall = jit->call();
200         jit->addLinkTask(
201             [=] (LinkBuffer& linkBuffer) {
202                 linkBuffer.link(lookupExceptionHandlerCall, lookupExceptionHandler);
203             });
204         jit->jumpToExceptionHandler(m_vm);
205     }
206 }
207
208
209 PolymorphicAccess::PolymorphicAccess() { }
210 PolymorphicAccess::~PolymorphicAccess() { }
211
212 AccessGenerationResult PolymorphicAccess::addCases(
213     VM& vm, CodeBlock* codeBlock, StructureStubInfo& stubInfo, const Identifier& ident,
214     Vector<std::unique_ptr<AccessCase>, 2> originalCasesToAdd)
215 {
216     SuperSamplerScope superSamplerScope(false);
217     
218     // This method will add the originalCasesToAdd to the list one at a time while preserving the
219     // invariants:
220     // - If a newly added case canReplace() any existing case, then the existing case is removed before
221     //   the new case is added. Removal doesn't change order of the list. Any number of existing cases
222     //   can be removed via the canReplace() rule.
223     // - Cases in the list always appear in ascending order of time of addition. Therefore, if you
224     //   cascade through the cases in reverse order, you will get the most recent cases first.
225     // - If this method fails (returns null, doesn't add the cases), then both the previous case list
226     //   and the previous stub are kept intact and the new cases are destroyed. It's OK to attempt to
227     //   add more things after failure.
228     
229     // First ensure that the originalCasesToAdd doesn't contain duplicates.
230     Vector<std::unique_ptr<AccessCase>> casesToAdd;
231     for (unsigned i = 0; i < originalCasesToAdd.size(); ++i) {
232         std::unique_ptr<AccessCase> myCase = WTFMove(originalCasesToAdd[i]);
233
234         // Add it only if it is not replaced by the subsequent cases in the list.
235         bool found = false;
236         for (unsigned j = i + 1; j < originalCasesToAdd.size(); ++j) {
237             if (originalCasesToAdd[j]->canReplace(*myCase)) {
238                 found = true;
239                 break;
240             }
241         }
242
243         if (found)
244             continue;
245         
246         casesToAdd.append(WTFMove(myCase));
247     }
248
249     if (verbose)
250         dataLog("casesToAdd: ", listDump(casesToAdd), "\n");
251
252     // If there aren't any cases to add, then fail on the grounds that there's no point to generating a
253     // new stub that will be identical to the old one. Returning null should tell the caller to just
254     // keep doing what they were doing before.
255     if (casesToAdd.isEmpty())
256         return AccessGenerationResult::MadeNoChanges;
257
258     // Now add things to the new list. Note that at this point, we will still have old cases that
259     // may be replaced by the new ones. That's fine. We will sort that out when we regenerate.
260     for (auto& caseToAdd : casesToAdd) {
261         commit(vm, m_watchpoints, codeBlock, stubInfo, ident, *caseToAdd);
262         m_list.append(WTFMove(caseToAdd));
263     }
264     
265     if (verbose)
266         dataLog("After addCases: m_list: ", listDump(m_list), "\n");
267
268     return AccessGenerationResult::Buffered;
269 }
270
271 AccessGenerationResult PolymorphicAccess::addCase(
272     VM& vm, CodeBlock* codeBlock, StructureStubInfo& stubInfo, const Identifier& ident,
273     std::unique_ptr<AccessCase> newAccess)
274 {
275     Vector<std::unique_ptr<AccessCase>, 2> newAccesses;
276     newAccesses.append(WTFMove(newAccess));
277     return addCases(vm, codeBlock, stubInfo, ident, WTFMove(newAccesses));
278 }
279
280 bool PolymorphicAccess::visitWeak(VM& vm) const
281 {
282     for (unsigned i = 0; i < size(); ++i) {
283         if (!at(i).visitWeak(vm))
284             return false;
285     }
286     if (Vector<WriteBarrier<JSCell>>* weakReferences = m_weakReferences.get()) {
287         for (WriteBarrier<JSCell>& weakReference : *weakReferences) {
288             if (!Heap::isMarked(weakReference.get()))
289                 return false;
290         }
291     }
292     return true;
293 }
294
295 bool PolymorphicAccess::propagateTransitions(SlotVisitor& visitor) const
296 {
297     bool result = true;
298     for (unsigned i = 0; i < size(); ++i)
299         result &= at(i).propagateTransitions(visitor);
300     return result;
301 }
302
303 void PolymorphicAccess::dump(PrintStream& out) const
304 {
305     out.print(RawPointer(this), ":[");
306     CommaPrinter comma;
307     for (auto& entry : m_list)
308         out.print(comma, *entry);
309     out.print("]");
310 }
311
312 void PolymorphicAccess::commit(
313     VM& vm, std::unique_ptr<WatchpointsOnStructureStubInfo>& watchpoints, CodeBlock* codeBlock,
314     StructureStubInfo& stubInfo, const Identifier& ident, AccessCase& accessCase)
315 {
316     // NOTE: We currently assume that this is relatively rare. It mainly arises for accesses to
317     // properties on DOM nodes. For sure we cache many DOM node accesses, but even in
318     // Real Pages (TM), we appear to spend most of our time caching accesses to properties on
319     // vanilla objects or exotic objects from within JSC (like Arguments, those are super popular).
320     // Those common kinds of JSC object accesses don't hit this case.
321     
322     for (WatchpointSet* set : accessCase.commit(vm, ident)) {
323         Watchpoint* watchpoint =
324             WatchpointsOnStructureStubInfo::ensureReferenceAndAddWatchpoint(
325                 watchpoints, codeBlock, &stubInfo, ObjectPropertyCondition());
326         
327         set->add(watchpoint);
328     }
329 }
330
331 AccessGenerationResult PolymorphicAccess::regenerate(
332     VM& vm, CodeBlock* codeBlock, StructureStubInfo& stubInfo, const Identifier& ident)
333 {
334     SuperSamplerScope superSamplerScope(false);
335     
336     if (verbose)
337         dataLog("Regenerate with m_list: ", listDump(m_list), "\n");
338     
339     AccessGenerationState state(vm);
340
341     state.access = this;
342     state.stubInfo = &stubInfo;
343     state.ident = &ident;
344     
345     state.baseGPR = static_cast<GPRReg>(stubInfo.patch.baseGPR);
346     state.thisGPR = static_cast<GPRReg>(stubInfo.patch.thisGPR);
347     state.valueRegs = stubInfo.valueRegs();
348
349     ScratchRegisterAllocator allocator(stubInfo.patch.usedRegisters);
350     state.allocator = &allocator;
351     allocator.lock(state.baseGPR);
352     if (state.thisGPR != InvalidGPRReg)
353         allocator.lock(state.thisGPR);
354     allocator.lock(state.valueRegs);
355 #if USE(JSVALUE32_64)
356     allocator.lock(static_cast<GPRReg>(stubInfo.patch.baseTagGPR));
357 #endif
358
359     state.scratchGPR = allocator.allocateScratchGPR();
360     
361     CCallHelpers jit(codeBlock);
362     state.jit = &jit;
363
364     state.preservedReusedRegisterState =
365         allocator.preserveReusedRegistersByPushing(jit, ScratchRegisterAllocator::ExtraStackSpace::NoExtraSpace);
366
367     // Regenerating is our opportunity to figure out what our list of cases should look like. We
368     // do this here. The newly produced 'cases' list may be smaller than m_list. We don't edit
369     // m_list in-place because we may still fail, in which case we want the PolymorphicAccess object
370     // to be unmutated. For sure, we want it to hang onto any data structures that may be referenced
371     // from the code of the current stub (aka previous).
372     ListType cases;
373     unsigned srcIndex = 0;
374     unsigned dstIndex = 0;
375     while (srcIndex < m_list.size()) {
376         std::unique_ptr<AccessCase> someCase = WTFMove(m_list[srcIndex++]);
377         
378         // If the case had been generated, then we have to keep the original in m_list in case we
379         // fail to regenerate. That case may have data structures that are used by the code that it
380         // had generated. If the case had not been generated, then we want to remove it from m_list.
381         bool isGenerated = someCase->state() == AccessCase::Generated;
382         
383         [&] () {
384             if (!someCase->couldStillSucceed())
385                 return;
386
387             // Figure out if this is replaced by any later case.
388             for (unsigned j = srcIndex; j < m_list.size(); ++j) {
389                 if (m_list[j]->canReplace(*someCase))
390                     return;
391             }
392             
393             if (isGenerated)
394                 cases.append(someCase->clone());
395             else
396                 cases.append(WTFMove(someCase));
397         }();
398         
399         if (isGenerated)
400             m_list[dstIndex++] = WTFMove(someCase);
401     }
402     m_list.resize(dstIndex);
403     
404     if (verbose)
405         dataLog("Optimized cases: ", listDump(cases), "\n");
406     
407     // At this point we're convinced that 'cases' contains the cases that we want to JIT now and we
408     // won't change that set anymore.
409     
410     bool allGuardedByStructureCheck = true;
411     bool hasJSGetterSetterCall = false;
412     for (auto& newCase : cases) {
413         commit(vm, state.watchpoints, codeBlock, stubInfo, ident, *newCase);
414         allGuardedByStructureCheck &= newCase->guardedByStructureCheck();
415         if (newCase->type() == AccessCase::Getter || newCase->type() == AccessCase::Setter)
416             hasJSGetterSetterCall = true;
417     }
418
419     if (cases.isEmpty()) {
420         // This is super unlikely, but we make it legal anyway.
421         state.failAndRepatch.append(jit.jump());
422     } else if (!allGuardedByStructureCheck || cases.size() == 1) {
423         // If there are any proxies in the list, we cannot just use a binary switch over the structure.
424         // We need to resort to a cascade. A cascade also happens to be optimal if we only have just
425         // one case.
426         CCallHelpers::JumpList fallThrough;
427
428         // Cascade through the list, preferring newer entries.
429         for (unsigned i = cases.size(); i--;) {
430             fallThrough.link(&jit);
431             fallThrough.clear();
432             cases[i]->generateWithGuard(state, fallThrough);
433         }
434         state.failAndRepatch.append(fallThrough);
435     } else {
436         jit.load32(
437             CCallHelpers::Address(state.baseGPR, JSCell::structureIDOffset()),
438             state.scratchGPR);
439         
440         Vector<int64_t> caseValues(cases.size());
441         for (unsigned i = 0; i < cases.size(); ++i)
442             caseValues[i] = bitwise_cast<int32_t>(cases[i]->structure()->id());
443         
444         BinarySwitch binarySwitch(state.scratchGPR, caseValues, BinarySwitch::Int32);
445         while (binarySwitch.advance(jit))
446             cases[binarySwitch.caseIndex()]->generate(state);
447         state.failAndRepatch.append(binarySwitch.fallThrough());
448     }
449
450     if (!state.failAndIgnore.empty()) {
451         state.failAndIgnore.link(&jit);
452         
453         // Make sure that the inline cache optimization code knows that we are taking slow path because
454         // of something that isn't patchable. The slow path will decrement "countdown" and will only
455         // patch things if the countdown reaches zero. We increment the slow path count here to ensure
456         // that the slow path does not try to patch.
457 #if CPU(X86) || CPU(X86_64)
458         jit.move(CCallHelpers::TrustedImmPtr(&stubInfo.countdown), state.scratchGPR);
459         jit.add8(CCallHelpers::TrustedImm32(1), CCallHelpers::Address(state.scratchGPR));
460 #else
461         jit.load8(&stubInfo.countdown, state.scratchGPR);
462         jit.add32(CCallHelpers::TrustedImm32(1), state.scratchGPR);
463         jit.store8(state.scratchGPR, &stubInfo.countdown);
464 #endif
465     }
466
467     CCallHelpers::JumpList failure;
468     if (allocator.didReuseRegisters()) {
469         state.failAndRepatch.link(&jit);
470         state.restoreScratch();
471     } else
472         failure = state.failAndRepatch;
473     failure.append(jit.jump());
474
475     CodeBlock* codeBlockThatOwnsExceptionHandlers = nullptr;
476     CallSiteIndex callSiteIndexForExceptionHandling;
477     if (state.needsToRestoreRegistersIfException() && hasJSGetterSetterCall) {
478         // Emit the exception handler.
479         // Note that this code is only reachable when doing genericUnwind from a pure JS getter/setter .
480         // Note also that this is not reachable from custom getter/setter. Custom getter/setters will have 
481         // their own exception handling logic that doesn't go through genericUnwind.
482         MacroAssembler::Label makeshiftCatchHandler = jit.label();
483
484         int stackPointerOffset = codeBlock->stackPointerOffset() * sizeof(EncodedJSValue);
485         AccessGenerationState::SpillState spillStateForJSGetterSetter = state.spillStateForJSGetterSetter();
486         ASSERT(!spillStateForJSGetterSetter.isEmpty());
487         stackPointerOffset -= state.preservedReusedRegisterState.numberOfBytesPreserved;
488         stackPointerOffset -= spillStateForJSGetterSetter.numberOfStackBytesUsedForRegisterPreservation;
489
490         jit.loadPtr(vm.addressOfCallFrameForCatch(), GPRInfo::callFrameRegister);
491         jit.addPtr(CCallHelpers::TrustedImm32(stackPointerOffset), GPRInfo::callFrameRegister, CCallHelpers::stackPointerRegister);
492
493         state.restoreLiveRegistersFromStackForCallWithThrownException(spillStateForJSGetterSetter);
494         state.restoreScratch();
495         CCallHelpers::Jump jumpToOSRExitExceptionHandler = jit.jump();
496
497         HandlerInfo oldHandler = state.originalExceptionHandler();
498         CallSiteIndex newExceptionHandlingCallSite = state.callSiteIndexForExceptionHandling();
499         jit.addLinkTask(
500             [=] (LinkBuffer& linkBuffer) {
501                 linkBuffer.link(jumpToOSRExitExceptionHandler, oldHandler.nativeCode);
502
503                 HandlerInfo handlerToRegister = oldHandler;
504                 handlerToRegister.nativeCode = linkBuffer.locationOf(makeshiftCatchHandler);
505                 handlerToRegister.start = newExceptionHandlingCallSite.bits();
506                 handlerToRegister.end = newExceptionHandlingCallSite.bits() + 1;
507                 codeBlock->appendExceptionHandler(handlerToRegister);
508             });
509
510         // We set these to indicate to the stub to remove itself from the CodeBlock's
511         // exception handler table when it is deallocated.
512         codeBlockThatOwnsExceptionHandlers = codeBlock;
513         ASSERT(JITCode::isOptimizingJIT(codeBlockThatOwnsExceptionHandlers->jitType()));
514         callSiteIndexForExceptionHandling = state.callSiteIndexForExceptionHandling();
515     }
516
517     LinkBuffer linkBuffer(jit, codeBlock, JITCompilationCanFail);
518     if (linkBuffer.didFailToAllocate()) {
519         if (verbose)
520             dataLog("Did fail to allocate.\n");
521         return AccessGenerationResult::GaveUp;
522     }
523
524     CodeLocationLabel successLabel = stubInfo.doneLocation();
525         
526     linkBuffer.link(state.success, successLabel);
527
528     linkBuffer.link(failure, stubInfo.slowPathStartLocation());
529     
530     if (verbose)
531         dataLog(FullCodeOrigin(codeBlock, stubInfo.codeOrigin), ": Generating polymorphic access stub for ", listDump(cases), "\n");
532
533     MacroAssemblerCodeRef code = FINALIZE_CODE_FOR(
534         codeBlock, linkBuffer,
535         ("%s", toCString("Access stub for ", *codeBlock, " ", stubInfo.codeOrigin, " with return point ", successLabel, ": ", listDump(cases)).data()));
536
537     bool doesCalls = false;
538     Vector<JSCell*> cellsToMark;
539     for (auto& entry : cases)
540         doesCalls |= entry->doesCalls(&cellsToMark);
541     
542     m_stubRoutine = createJITStubRoutine(code, vm, codeBlock, doesCalls, cellsToMark, codeBlockThatOwnsExceptionHandlers, callSiteIndexForExceptionHandling);
543     m_watchpoints = WTFMove(state.watchpoints);
544     if (!state.weakReferences.isEmpty())
545         m_weakReferences = std::make_unique<Vector<WriteBarrier<JSCell>>>(WTFMove(state.weakReferences));
546     if (verbose)
547         dataLog("Returning: ", code.code(), "\n");
548     
549     m_list = WTFMove(cases);
550     
551     AccessGenerationResult::Kind resultKind;
552     if (m_list.size() >= Options::maxAccessVariantListSize())
553         resultKind = AccessGenerationResult::GeneratedFinalCode;
554     else
555         resultKind = AccessGenerationResult::GeneratedNewCode;
556     
557     return AccessGenerationResult(resultKind, code.code());
558 }
559
560 void PolymorphicAccess::aboutToDie()
561 {
562     if (m_stubRoutine)
563         m_stubRoutine->aboutToDie();
564 }
565
566 } // namespace JSC
567
568 namespace WTF {
569
570 using namespace JSC;
571
572 void printInternal(PrintStream& out, AccessGenerationResult::Kind kind)
573 {
574     switch (kind) {
575     case AccessGenerationResult::MadeNoChanges:
576         out.print("MadeNoChanges");
577         return;
578     case AccessGenerationResult::GaveUp:
579         out.print("GaveUp");
580         return;
581     case AccessGenerationResult::Buffered:
582         out.print("Buffered");
583         return;
584     case AccessGenerationResult::GeneratedNewCode:
585         out.print("GeneratedNewCode");
586         return;
587     case AccessGenerationResult::GeneratedFinalCode:
588         out.print("GeneratedFinalCode");
589         return;
590     }
591     
592     RELEASE_ASSERT_NOT_REACHED();
593 }
594
595 void printInternal(PrintStream& out, AccessCase::AccessType type)
596 {
597     switch (type) {
598     case AccessCase::Load:
599         out.print("Load");
600         return;
601     case AccessCase::Transition:
602         out.print("Transition");
603         return;
604     case AccessCase::Replace:
605         out.print("Replace");
606         return;
607     case AccessCase::Miss:
608         out.print("Miss");
609         return;
610     case AccessCase::GetGetter:
611         out.print("GetGetter");
612         return;
613     case AccessCase::Getter:
614         out.print("Getter");
615         return;
616     case AccessCase::Setter:
617         out.print("Setter");
618         return;
619     case AccessCase::CustomValueGetter:
620         out.print("CustomValueGetter");
621         return;
622     case AccessCase::CustomAccessorGetter:
623         out.print("CustomAccessorGetter");
624         return;
625     case AccessCase::CustomValueSetter:
626         out.print("CustomValueSetter");
627         return;
628     case AccessCase::CustomAccessorSetter:
629         out.print("CustomAccessorSetter");
630         return;
631     case AccessCase::IntrinsicGetter:
632         out.print("IntrinsicGetter");
633         return;
634     case AccessCase::InHit:
635         out.print("InHit");
636         return;
637     case AccessCase::InMiss:
638         out.print("InMiss");
639         return;
640     case AccessCase::ArrayLength:
641         out.print("ArrayLength");
642         return;
643     case AccessCase::StringLength:
644         out.print("StringLength");
645         return;
646     case AccessCase::DirectArgumentsLength:
647         out.print("DirectArgumentsLength");
648         return;
649     case AccessCase::ScopedArgumentsLength:
650         out.print("ScopedArgumentsLength");
651         return;
652     case AccessCase::ModuleNamespaceLoad:
653         out.print("ModuleNamespaceLoad");
654         return;
655     }
656
657     RELEASE_ASSERT_NOT_REACHED();
658 }
659
660 void printInternal(PrintStream& out, AccessCase::State state)
661 {
662     switch (state) {
663     case AccessCase::Primordial:
664         out.print("Primordial");
665         return;
666     case AccessCase::Committed:
667         out.print("Committed");
668         return;
669     case AccessCase::Generated:
670         out.print("Generated");
671         return;
672     }
673
674     RELEASE_ASSERT_NOT_REACHED();
675 }
676
677 } // namespace WTF
678
679 #endif // ENABLE(JIT)
680
681