/*
 * Copyright (C) 2011-2016 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 "Repatch.h"

#if ENABLE(JIT)

#include "BinarySwitch.h"
#include "CCallHelpers.h"
#include "CallFrameInlines.h"
#include "DirectArguments.h"
#include "GCAwareJITStubRoutine.h"
#include "GetterSetter.h"
#include "GetterSetterAccessCase.h"
#include "InlineAccess.h"
#include "InstanceOfAccessCase.h"
#include "IntrinsicGetterAccessCase.h"
#include "JSModuleNamespaceObject.h"
#include "LinkBuffer.h"
#include "ModuleNamespaceAccessCase.h"
#include "ObjectPropertyConditionSet.h"
#include "Operations.h"
#include "PolymorphicAccess.h"
#include "ScopedArguments.h"
#include "ScratchRegisterAllocator.h"
#include "StructureRareDataInlines.h"
#include "StructureStubClearingWatchpoint.h"
#include "ThunkGenerators.h"
#include <wtf/MemoryMonitor.h>
#include <wtf/StringPrintStream.h>

namespace JSC {

// Beware: in this code, it is not safe to assume anything about the following registers
// that would ordinarily have well-known values:
// - callFrameRegister **
//
// The callFrameRegister is a double-FIXME:
// - https://bugs.webkit.org/show_bug.cgi?id=123085
//   We're not yet done getting rid of past uses of callFrameRegister.
// - https://bugs.webkit.org/show_bug.cgi?id=113621
//   Once the calling convention work is done, we won't need the callFrameRegister anymore
//   since we'll just use framePointerRegister.

static FunctionPtr readCallTarget(CodeBlock* codeBlock, CodeLocationCall call)
{
    FunctionPtr result = MacroAssembler::readCallTarget(call);
#if ENABLE(FTL_JIT)
    if (codeBlock->jitType() == JITCode::FTLJIT) {
        return FunctionPtr(codeBlock->vm()->ftlThunks->keyForSlowPathCallThunk(
            MacroAssemblerCodePtr::createFromExecutableAddress(
                result.executableAddress())).callTarget());
    }
#else
    UNUSED_PARAM(codeBlock);
#endif // ENABLE(FTL_JIT)
    return result;
}

void ftlThunkAwareRepatchCall(CodeBlock* codeBlock, CodeLocationCall call, FunctionPtr newCalleeFunction)
{
#if ENABLE(FTL_JIT)
    if (codeBlock->jitType() == JITCode::FTLJIT) {
        VM& vm = *codeBlock->vm();
        FTL::Thunks& thunks = *vm.ftlThunks;
        FTL::SlowPathCallKey key = thunks.keyForSlowPathCallThunk(
            MacroAssemblerCodePtr::createFromExecutableAddress(
                MacroAssembler::readCallTarget(call).executableAddress()));
        key = key.withCallTarget(newCalleeFunction.executableAddress());
        newCalleeFunction = FunctionPtr(
            thunks.getSlowPathCallThunk(vm, key).code().executableAddress());
    }
#else // ENABLE(FTL_JIT)
    UNUSED_PARAM(codeBlock);
#endif // ENABLE(FTL_JIT)
    MacroAssembler::repatchCall(call, newCalleeFunction);
}

enum InlineCacheAction {
    GiveUpOnCache,
    RetryCacheLater,
    AttemptToCache
};

static InlineCacheAction actionForCell(VM& vm, JSCell* cell)
{
    Structure* structure = cell->structure(vm);

    TypeInfo typeInfo = structure->typeInfo();
    if (typeInfo.prohibitsPropertyCaching())
        return GiveUpOnCache;

    if (structure->isUncacheableDictionary()) {
        if (structure->hasBeenFlattenedBefore())
            return GiveUpOnCache;
        // Flattening could have changed the offset, so return early for another try.
        asObject(cell)->flattenDictionaryObject(vm);
        return RetryCacheLater;
    }

    if (!structure->propertyAccessesAreCacheable())
        return GiveUpOnCache;

    return AttemptToCache;
}

static inline FunctionPtr appropriateOptimizingGetByIdFunction(GetByIDKind kind)
{
    switch (kind) {
    case GetByIDKind::Normal:
        return operationGetByIdOptimize;
    case GetByIDKind::WithThis:
        return operationGetByIdWithThisOptimize;
    case GetByIDKind::Try:
        return operationTryGetByIdOptimize;
    case GetByIDKind::Direct:
        return operationGetByIdDirectOptimize;
    case GetByIDKind::PrivateName:
        return operationGetPrivateNameOptimize;
    case GetByIDKind::PrivateNameById:
        return operationGetPrivateNameByIdOptimize;
    }
    ASSERT_NOT_REACHED();
    return operationGetById;
}

static inline FunctionPtr appropriateGetByIdFunction(GetByIDKind kind)
{
    switch (kind) {
    case GetByIDKind::Normal:
        return operationGetById;
    case GetByIDKind::WithThis:
        return operationGetByIdWithThis;
    case GetByIDKind::Try:
        return operationTryGetById;
    case GetByIDKind::Direct:
        return operationGetByIdDirect;
    case GetByIDKind::PrivateName:
        return operationGetPrivateName;
    case GetByIDKind::PrivateNameById:
        return operationGetPrivateNameById;
    }
    ASSERT_NOT_REACHED();
    return operationGetById;
}

static InlineCacheAction tryCacheGetByID(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot, StructureStubInfo& stubInfo, GetByIDKind kind)
{
    // FIXME: Cache property access for immediates.
    if (!baseValue.isCell())
        return GiveUpOnCache;

    if (Options::forceICFailure())
    	return GiveUpOnCache;

	if(ExecutableAllocator::underMemoryPressure() || WTF::isLowMemory() || WTF::isUnderMemoryPressure())
        return GiveUpOnCache;

    CodeBlock* codeBlock = exec->codeBlock();
    ScriptExecutable* owner = codeBlock->ownerScriptExecutable();
    VM& vm = codeBlock->vm();
	JSCell* baseCell = baseValue.asCell();
    const bool isPrivate = kind == GetByIDKind::PrivateName || kind == GetByIDKind::PrivateNameById;

    OwnPtr<AccessCase> newCase;

    if (propertyName == vm.propertyNames->length) {
		if (isJSArray(baseCell)) {
            if (InlineAccess::enabled
            	&& stubInfo.cacheType == CacheType::Unset
                && slot.slotBase() == baseCell
                && InlineAccess::isCacheableArrayLength(stubInfo, jsCast<JSArray*>(baseCell))) {

                bool generatedCodeInline = InlineAccess::generateArrayLength(vm, stubInfo, jsCast<JSArray*>(baseCell));
                if (generatedCodeInline) {
                	ftlThunkAwareRepatchCall(codeBlock, stubInfo.slowPathCallLocation(), appropriateOptimizingGetByIdFunction(kind));
                    stubInfo.initArrayLength();
                    return RetryCacheLater;
                }
            }

			newCase = AccessCase::create(vm, owner, AccessCase::ArrayLength);
		} else if (isJSString(baseCell)) {
            if (InlineAccess::enabled
				&& stubInfo.cacheType == CacheType::Unset
				&& InlineAccess::isCacheableStringLength(stubInfo)) {

                bool generatedCodeInline = InlineAccess::generateStringLength(vm, stubInfo);
                if (generatedCodeInline) {
                	ftlThunkAwareRepatchCall(codeBlock, stubInfo.slowPathCallLocation(), appropriateOptimizingGetByIdFunction(kind));
                    stubInfo.initStringLength();
                    return RetryCacheLater;
                }
            }

			newCase = AccessCase::create(vm, owner, AccessCase::StringLength);
		} else if (DirectArguments* arguments = jsDynamicCast<DirectArguments*>(baseCell)) {
			// If there were overrides, then we can handle this as a normal property load! Guarding
			// this with such a check enables us to add an IC case for that load if needed.
			if (!arguments->overrodeThings())
				newCase = AccessCase::create(vm, owner, AccessCase::DirectArgumentsLength);
		} else if (ScopedArguments* arguments = jsDynamicCast<ScopedArguments*>(baseCell)) {
			// Ditto.
			if (!arguments->overrodeThings())
				newCase = AccessCase::create(vm, owner, AccessCase::ScopedArgumentsLength);
		}
    }

    if (!propertyName.isSymbol() && isJSModuleNamespaceObject(baseCell) && !slot.isUnset()) {
        if (auto moduleNamespaceSlot = slot.moduleNamespaceSlot())
            newCase = ModuleNamespaceAccessCase::create(vm, owner, jsCast<JSModuleNamespaceObject*>(baseCell), moduleNamespaceSlot->environment, ScopeOffset(moduleNamespaceSlot->scopeOffset));
    }

	if (!newCase) {
		if (!slot.isCacheable() && !slot.isUnset())
			return GiveUpOnCache;

		Structure* structure = baseCell->structure(vm);

		bool loadTargetFromProxy = false;
		if (baseCell->structure()->typeInfo().type() == PureForwardingProxyType) {
            if (isPrivate)
                return GiveUpOnCache;
			baseValue = jsCast<JSProxy*>(baseCell)->target();
			baseCell = baseValue.asCell();
			structure = baseCell->structure(vm);
			loadTargetFromProxy = true;
		}

		InlineCacheAction action = actionForCell(vm, baseCell);
		if (action != AttemptToCache)
			return action;

		// Optimize self access.
		if (InlineAccess::enabled
			&& stubInfo.cacheType == CacheType::Unset
			&& slot.isCacheableValue()
			&& slot.slotBase() == baseCell
			&& !slot.watchpointSet()
			&& !structure->needImpurePropertyWatchpoint()
            && !loadTargetFromProxy) {

            bool generatedCodeInline = InlineAccess::generateSelfPropertyAccess(vm, stubInfo, structure, slot.cachedOffset());
            if (generatedCodeInline) {
				structure->startWatchingPropertyForReplacements(vm, slot.cachedOffset());
				ftlThunkAwareRepatchCall(codeBlock, stubInfo.slowPathCallLocation(), appropriateOptimizingGetByIdFunction(kind));
				stubInfo.initGetByIdSelf(codeBlock, structure, slot.cachedOffset());
				return RetryCacheLater;
            }
		}

		PropertyOffset offset = slot.isUnset() ? invalidOffset : slot.cachedOffset();

		if (slot.isCustom() && slot.slotBase() == baseCell) {
			// To cache self customs, we must disallow dictionaries because we
			// need to be informed if the custom goes away since we cache the
			// constant function pointer.

			if (!prepareChainForCaching(codeBlock->globalObject(), slot.slotBase(), slot.slotBase()))
				return GiveUpOnCache;
		}

        ObjectPropertyConditionSet conditionSet;
		if (slot.isUnset() || slot.slotBase() != baseCell) {
			if (structure->typeInfo().prohibitsPropertyCaching())
				return GiveUpOnCache;

			if (structure->isDictionary()) {
				if (structure->hasBeenFlattenedBefore())
					return GiveUpOnCache;
				structure->flattenDictionaryStructure(vm, jsCast<JSObject*>(baseCell));
                return RetryCacheLater; // We may have changed property offsets.
			}

			if (slot.isUnset() && structure->typeInfo().getOwnPropertySlotIsImpureForPropertyAbsence())
				return GiveUpOnCache;

            // If a kind is GetByIDKind::Direct or GetByIDKind::PrivateName, we do not need to investigate prototype chains further.
            // Cacheability just depends on the head structure.
            if (kind != GetByIDKind::Direct && !isPrivate) {
				auto cacheStatus = prepareChainForCaching(codeBlock->globalObject(), baseCell, slot);
				if (!cacheStatus)
					return GiveUpOnCache;

				if (cacheStatus->flattenedDictionary) {
					// Property offsets may have changed due to flattening. We'll cache later.
					return RetryCacheLater;
				}

				// We use ObjectPropertyConditionSet instead for faster accesses.

				// FIXME: Maybe this `if` should be inside generateConditionsForPropertyBlah.
				// https://bugs.webkit.org/show_bug.cgi?id=185215
				if (slot.isUnset()) {
					conditionSet = generateConditionsForPropertyMiss(
						vm, owner, exec, structure, propertyName.impl());
				} else if (!slot.isCacheableCustom()) {
                    conditionSet = generateConditionsForPrototypePropertyHit(
						vm, owner, exec, structure, slot.slotBase(),
						propertyName.uid());
                    RELEASE_ASSERT(!conditionSet.isValid() || conditionSet.slotBaseCondition().offset() == offset);
				} else {
					conditionSet = generateConditionsForPrototypePropertyHitCustom(
						vm, owner, exec, structure, slot.slotBase(),
						propertyName.impl(), slot.attributes());
				}

				if (!conditionSet.isValid())
					return GiveUpOnCache;
            }
		}

        JSFunction* getter = nullptr;
        if (slot.isCacheableGetter())
            getter = jsDynamicCast<JSFunction*>(slot.getterSetter()->getter());

        if (kind == GetByIDKind::Try) {
            AccessCase::AccessType type;
            if (slot.isCacheableValue())
                type = AccessCase::Load;
            else if (slot.isUnset())
                type = AccessCase::Miss;
            else if (slot.isCacheableGetter())
                type = AccessCase::GetGetter;
            else
                RELEASE_ASSERT_NOT_REACHED();

            newCase = ProxyableAccessCase::create(vm, owner, type, offset, structure, conditionSet, loadTargetFromProxy, slot.watchpointSet());
        } else if (!loadTargetFromProxy && getter && IntrinsicGetterAccessCase::canEmitIntrinsicGetter(stubInfo, getter, structure))
            newCase = IntrinsicGetterAccessCase::create(vm, owner, slot.cachedOffset(), structure, conditionSet, getter);
        else {
            if (isPrivate) {
                RELEASE_ASSERT(!slot.isUnset());
                constexpr bool isProxy = false;
                if (!slot.isCacheable())
                    return GiveUpOnCache;
                newCase = ProxyableAccessCase::create(vm, owner, AccessCase::Load, offset, structure,
                    conditionSet, isProxy, slot.watchpointSet());
            } else if (slot.isCacheableValue() || slot.isUnset()) {
                newCase = ProxyableAccessCase::create(vm, owner, slot.isUnset() ? AccessCase::Miss : AccessCase::Load,
                    offset, structure, conditionSet, loadTargetFromProxy, slot.watchpointSet());
            } else {
				AccessCase::AccessType type;
				if (slot.isCacheableGetter()) {
					type = AccessCase::Getter;
				} else if (slot.attributes() & CustomAccessor)
					type = AccessCase::CustomAccessorGetter;
				else
					type = AccessCase::CustomValueGetter;

#if 0
                // we don't emit IC for DOMJIT when op is get_by_id_with_this
                if (Options::useDOMJIT() && kind == GetByIDKind::WithThis && type == AccessCase::CustomAccessorGetter && domJIT)
                    return GiveUpOnCache;
#endif

				newCase = GetterSetterAccessCase::create(
					vm, owner, type, offset, structure, conditionSet, loadTargetFromProxy,
					slot.watchpointSet(), slot.isCacheableCustom() ? FunctionPtr(slot.customGetter()) : FunctionPtr(operationCallGetter),
					slot.isCacheableCustom() && slot.slotBase() != baseCell ? slot.slotBase() : nullptr);
            }
        }
	}

    DeferGC deferGC(vm.heap);
	AccessGenerationResult result = stubInfo.addAccessCase(codeBlock, ECMAMode::strict(), propertyName, newCase.release());

    if (result.generatedSomeCode()) {
    	RELEASE_ASSERT(result.code());
        InlineAccess::rewireStubAsJump(vm, stubInfo, CodeLocationLabel(result.code()));
    }

    return result.shouldGiveUpNow() ? GiveUpOnCache : RetryCacheLater;
}

void repatchGetByID(ExecState* exec, JSValue baseValue, const Identifier& propertyName, const PropertySlot& slot, StructureStubInfo& stubInfo, GetByIDKind kind)
{
    //SuperSamplerScope superSamplerScope(false);
    // ConcurrentJITLocker locker(exec->codeBlock()->m_lock);

    if(tryCacheGetByID(exec, baseValue, propertyName, slot, stubInfo, kind) == GiveUpOnCache)
    	ftlThunkAwareRepatchCall(exec->codeBlock(), stubInfo.slowPathCallLocation(), appropriateGetByIdFunction(kind));
}

#if 0
static InlineCacheAction tryCacheArrayGetByVal(JSGlobalObject* globalObject, CodeBlock* codeBlock, JSValue baseValue, JSValue index, StructureStubInfo& stubInfo)
{
    if (!baseValue.isCell())
        return GiveUpOnCache;

    if (!index.isInt32())
        return RetryCacheLater;

    VM& vm = globalObject->vm();
    AccessGenerationResult result;

    {
        GCSafeConcurrentJSLocker locker(codeBlock->m_lock, globalObject->vm().heap);

        JSCell* base = baseValue.asCell();

        AccessCase::AccessType accessType;
        if (base->type() == DirectArgumentsType)
            accessType = AccessCase::IndexedDirectArgumentsLoad;
        else if (base->type() == ScopedArgumentsType)
            accessType = AccessCase::IndexedScopedArgumentsLoad;
        else if (base->type() == StringType)
            accessType = AccessCase::IndexedStringLoad;
        else if (isTypedView(base->classInfo(vm)->typedArrayStorageType)) {
            switch (base->classInfo(vm)->typedArrayStorageType) {
            case TypeInt8:
                accessType = AccessCase::IndexedTypedArrayInt8Load;
                break;
            case TypeUint8:
                accessType = AccessCase::IndexedTypedArrayUint8Load;
                break;
            case TypeUint8Clamped:
                accessType = AccessCase::IndexedTypedArrayUint8ClampedLoad;
                break;
            case TypeInt16:
                accessType = AccessCase::IndexedTypedArrayInt16Load;
                break;
            case TypeUint16:
                accessType = AccessCase::IndexedTypedArrayUint16Load;
                break;
            case TypeInt32:
                accessType = AccessCase::IndexedTypedArrayInt32Load;
                break;
            case TypeUint32:
                accessType = AccessCase::IndexedTypedArrayUint32Load;
                break;
            case TypeFloat32:
                accessType = AccessCase::IndexedTypedArrayFloat32Load;
                break;
            case TypeFloat64:
                accessType = AccessCase::IndexedTypedArrayFloat64Load;
                break;
            // FIXME: Optimize BigInt64Array / BigUint64Array in IC
            // https://bugs.webkit.org/show_bug.cgi?id=221183
            case TypeBigInt64:
            case TypeBigUint64:
                return GiveUpOnCache;
            default:
                RELEASE_ASSERT_NOT_REACHED();
            }
        } else {
            IndexingType indexingShape = base->indexingType() & IndexingShapeMask;
            switch (indexingShape) {
            case Int32Shape:
                accessType = AccessCase::IndexedInt32Load;
                break;
            case DoubleShape:
                accessType = AccessCase::IndexedDoubleLoad;
                break;
            case ContiguousShape:
                accessType = AccessCase::IndexedContiguousLoad;
                break;
            case ArrayStorageShape:
                accessType = AccessCase::IndexedArrayStorageLoad;
                break;
            default:
                return GiveUpOnCache;
            }
        }

        result = stubInfo.addAccessCase(locker, globalObject, codeBlock, ECMAMode::strict(), nullptr, AccessCase::create(vm, codeBlock, accessType, nullptr));

        if (result.generatedSomeCode()) {
            LOG_IC((ICEvent::GetByReplaceWithJump, baseValue.classInfoOrNull(vm), Identifier()));

            RELEASE_ASSERT(result.code());
            InlineAccess::rewireStubAsJump(stubInfo, CodeLocationLabel<JITStubRoutinePtrTag>(result.code()));
        }
    }

    fireWatchpointsAndClearStubIfNeeded(vm, stubInfo, codeBlock, result);
    return result.shouldGiveUpNow() ? GiveUpOnCache : RetryCacheLater;
}

void repatchArrayGetByVal(JSGlobalObject* globalObject, CodeBlock* codeBlock, JSValue base, JSValue index, StructureStubInfo& stubInfo)
{
    if (tryCacheArrayGetByVal(globalObject, codeBlock, base, index, stubInfo) == GiveUpOnCache)
        ftlThunkAwareRepatchCall(codeBlock, stubInfo.slowPathCallLocation, operationGetByValGeneric);
}
#endif

static InlineCacheAction tryCachePutByID(ExecState* exec, JSValue baseValue, Structure* oldStructure, const Identifier& ident, const PutPropertySlot& slot, StructureStubInfo& stubInfo, PutKind putKind)
{
    if (Options::forceICFailure())
    	return GiveUpOnCache;

    if (!Options::maxPutByIdICSize())
    	return GiveUpOnCache;

	if(ExecutableAllocator::underMemoryPressure() || WTF::isLowMemory() || WTF::isUnderMemoryPressure())
        return GiveUpOnCache;

    CodeBlock* codeBlock = exec->codeBlock();
    ScriptExecutable* owner = codeBlock->ownerScriptExecutable();
    VM& vm = exec->vm();

    if (!baseValue.isCell())
        return GiveUpOnCache;

    if (!slot.isCacheablePut() && !slot.isCacheableCustom())
        return GiveUpOnCache;

    if (!oldStructure->propertyAccessesAreCacheable())
        return GiveUpOnCache;

    JSCell* baseCell = baseValue.asCell();

    bool isProxy = false;
    if (baseCell->structure()->typeInfo().type() == PureForwardingProxyType) {
        baseCell = jsCast<JSProxy*>(baseCell)->target();
        baseValue = baseCell;
        isProxy = true;

        // We currently only cache Replace and JS/Custom Setters on JSProxy. We don't
        // cache transitions because global objects will never share the same structure
        // in our current implementation.
        bool isCacheableProxy = (slot.isCacheablePut() && slot.type() == PutPropertySlot::ExistingProperty)
            || slot.isCacheableSetter()
            || slot.isCacheableCustom();
        if (!isCacheableProxy)
            return GiveUpOnCache;
    }

    if (isProxy && (putKind == PutKind::DirectPrivateFieldSet || putKind == PutKind::DirectPrivateFieldDefine))
        return GiveUpOnCache;

    OwnPtr<AccessCase> newCase;

    // Optimize self access.
    if (slot.base() == baseValue && slot.isCacheablePut()) {
        if (slot.type() == PutPropertySlot::ExistingProperty) {
            // This assert helps catch bugs if we accidentally forget to disable caching
            // when we transition then store to an existing property. This is common among
            // paths that reify lazy properties. If we reify a lazy property and forget
            // to disable caching, we may come down this path. The Replace IC does not
            // know how to model these types of structure transitions (or any structure
            // transition for that matter).
            RELEASE_ASSERT(baseValue.asCell()->structure(vm) == structure);

        	oldStructure->didCachePropertyReplacement(vm, slot.cachedOffset());

            if (InlineAccess::enabled
				&& stubInfo.cacheType == CacheType::Unset
				&& InlineAccess::canGenerateSelfPropertyReplace(stubInfo, slot.cachedOffset())
				&& !oldStructure->needImpurePropertyWatchpoint()
                && !isProxy) {

                bool generatedCodeInline = InlineAccess::generateSelfPropertyReplace(vm, stubInfo, oldStructure, slot.cachedOffset());
                if (generatedCodeInline) {
                    //LOG_IC((ICEvent::PutByIdSelfPatch, structure->classInfo(), ident));
                	ftlThunkAwareRepatchCall(codeBlock, stubInfo.slowPathCallLocation(), appropriateOptimizingPutByIdFunction(slot, putKind));
                    stubInfo.initPutByIdReplace(codeBlock, oldStructure, slot.cachedOffset());
                    return RetryCacheLater;
                }
            }

            newCase = ProxyableAccessCase::create(vm, owner, AccessCase::Replace, slot.cachedOffset(), oldStructure, ObjectPropertyConditionSet(), isProxy);
        }
        else {
            ASSERT(!isProxy);
            ASSERT(slot.type() == PutPropertySlot::NewProperty);

            if (!oldStructure->isObject())
                return GiveUpOnCache;

            // If the old structure is dictionary, it means that this is one-on-one between an object and a structure.
            // If this is NewProperty operation, generating IC for this does not offer any benefit because this transition never happens again.
            if (oldStructure->isDictionary())
                return RetryCacheLater;

            PropertyOffset offset;
            Structure* newStructure = Structure::addPropertyTransitionToExistingStructure(oldStructure, ident.impl(), None, offset);
            if (!newStructure || !newStructure->propertyAccessesAreCacheable())
                return GiveUpOnCache;

            // If JSObject::put is overridden by UserObject, UserObject::put performs side-effect on JSObject::put, and it neglects to mark the PutPropertySlot as non-cachaeble,
            // then arbitrary structure transitions can happen during the put operation, and this generates wrong transition information here as if oldStructure -> newStructure.
            // In reality, the transition is oldStructure -> something unknown structures -> baseValue's structure.
            // To guard against the embedder's potentially incorrect UserObject::put implementation, we should check for this condition and if found, and give up on caching the put.
            ASSERT(baseValue.asCell()->structure(vm) == newStructure);
            if (baseValue.asCell()->structure(vm) != newStructure)
                return GiveUpOnCache;

            ASSERT(newStructure->previousID() == oldStructure);
            ASSERT(!newStructure->isDictionary());
            ASSERT(newStructure->isObject());

            ObjectPropertyConditionSet conditionSet;
            if (putKind == PutKind::NotDirect) {
                auto cacheStatus = prepareChainForCaching(exec->lexicalGlobalObject(), baseCell, 0);
                if (!cacheStatus)
                    return GiveUpOnCache;

                conditionSet = generateConditionsForPropertySetterMiss(
                        vm, owner, exec, newStructure, ident.impl());
                if (!conditionSet.isValid())
                    return GiveUpOnCache;
            }

            if (putKind == PutKind::DirectPrivateFieldDefine) {
                ASSERT(ident.isPrivateName());
                conditionSet = generateConditionsForPropertyMiss(vm, owner, exec, newStructure, ident.impl());
                if (!conditionSet.isValid())
                    return GiveUpOnCache;
            }

            newCase = AccessCase::create(vm, owner, offset, oldStructure, newStructure, conditionSet);
        }
    } else if (slot.isCacheableCustom() || slot.isCacheableSetter()) {
        if (slot.isCacheableCustom()) {
            ObjectPropertyConditionSet conditionSet;

            // We need to do this even if we're a self custom, since we must disallow dictionaries
            // because we need to be informed if the custom goes away since we cache the constant
            // function pointer.
            auto cacheStatus = prepareChainForCaching(codeBlock->globalObject(), baseCell, slot.base());
            if (!cacheStatus)
                return GiveUpOnCache;

            if (slot.base() != baseValue) {
                conditionSet = generateConditionsForPrototypePropertyHitCustom(
                        vm, owner, exec, oldStructure, slot.base(), ident.impl(), None);
                if (!conditionSet.isValid())
                    return GiveUpOnCache;
            }

            newCase = GetterSetterAccessCase::create(
                vm, owner, slot.isCustomAccessor() ? AccessCase::CustomAccessorSetter : AccessCase::CustomValueSetter, oldStructure, invalidOffset, conditionSet,
				isProxy, FunctionPtr(slot.customSetter()), slot.base() != baseValue ? slot.base() : nullptr);
        } else {
        	// do not support yet.
#if 0
            ASSERT(slot.isCacheableSetter());
            ObjectPropertyConditionSet conditionSet;
            PropertyOffset offset;

            if (slot.base() != baseValue) {
                auto cacheStatus = prepareChainForCaching(exec->lexicalGlobalObject(), baseCell, slot.base());
                if (!cacheStatus)
                    return GiveUpOnCache;
                if (cacheStatus->flattenedDictionary)
                    return RetryCacheLater;

                conditionSet = generateConditionsForPrototypePropertyHit(
                        vm, owner, exec, oldStructure, slot.base(), ident.impl());
                if (!conditionSet.isValid())
                    return GiveUpOnCache;
                offset = conditionSet.slotBaseCondition().offset();
            } else
                offset = slot.cachedOffset();

            newCase = GetterSetterAccessCase::create(
                vm, owner, AccessCase::Setter, oldStructure, offset, conditionSet, isProxy);
#endif
        }
    }

    DeferGC deferGC(vm.heap);
    AccessGenerationResult result = stubInfo.addAccessCase(codeBlock, slot.isStrictMode() ? ECMAMode::strict() : ECMAMode::sloppy(), ident, newCase.release());

    if (result.generatedSomeCode()) {
		RELEASE_ASSERT(result.code());
		InlineAccess::rewireStubAsJump(vm, stubInfo, CodeLocationLabel(result.code()));
	}

    return result.shouldGiveUpNow() ? GiveUpOnCache : RetryCacheLater;
}

void repatchPutByID(ExecState* exec, JSValue baseValue, Structure* oldStructure, const Identifier& propertyName, const PutPropertySlot& slot, StructureStubInfo& stubInfo, PutKind putKind)
{
    //SuperSamplerScope superSamplerScope(false);
    //GCSafeConcurrentJITLocker locker(exec->codeBlock()->m_lock, exec->vm().heap);

    if(tryCachePutByID(exec, baseValue, oldStructure, propertyName, slot, stubInfo, putKind) == GiveUpOnCache)
    	ftlThunkAwareRepatchCall(exec->codeBlock(), stubInfo.slowPathCallLocation(), appropriatePutByIdFunction(slot, putKind));
}

static InlineCacheAction tryRepatchIn(
    ExecState* exec, JSCell* base, const Identifier& ident, bool wasFound,
    const PropertySlot& slot, StructureStubInfo& stubInfo)
{
    if (Options::forceICFailure())
    	return GiveUpOnCache;

	if (ExecutableAllocator::underMemoryPressure() || WTF::isLowMemory() || WTF::isUnderMemoryPressure())
        return GiveUpOnCache;

    if (!base->structure()->propertyAccessesAreCacheable()|| (!wasFound && !base->structure()->propertyAccessesAreCacheableForAbsence()))
        return GiveUpOnCache;

    if (wasFound) {
        if (!slot.isCacheable())
            return GiveUpOnCache;
    }

    CodeBlock* codeBlock = exec->codeBlock();
    ScriptExecutable* owner = codeBlock->ownerScriptExecutable();
    VM& vm = exec->vm();
    Structure* structure = base->structure(vm);

    TypeInfo typeInfo = structure->typeInfo();
    if (typeInfo.getOwnPropertySlotIsImpure() && !typeInfo.newImpurePropertyFiresWatchpoints())
        return GiveUpOnCache;

    ObjectPropertyConditionSet conditionSet;
    if (wasFound) {
        if (slot.slotBase() != base) {
            auto cacheStatus = prepareChainForCaching(exec->lexicalGlobalObject(), base, slot);
            if (!cacheStatus)
                return GiveUpOnCache;
            if (cacheStatus->flattenedDictionary)
                return RetryCacheLater;

            conditionSet = generateConditionsForPrototypePropertyHit(
                vm, codeBlock->ownerExecutable(), exec, structure, slot.slotBase(), ident.impl());
            if (!conditionSet.isValid())
				return GiveUpOnCache;
			RELEASE_ASSERT(slot.isCacheableCustom() || conditionSet.slotBaseCondition().offset() == slot.cachedOffset());
		}
    } else {
        auto cacheStatus = prepareChainForCaching(exec->lexicalGlobalObject(), base, 0);
        if (!cacheStatus)
            return GiveUpOnCache;

        conditionSet = generateConditionsForPropertyMiss(
            vm, codeBlock->ownerExecutable(), exec, structure, ident.impl());
        if (!conditionSet.isValid())
            return GiveUpOnCache;
    }

    OwnPtr<AccessCase> newCase = AccessCase::create(
        vm, owner, wasFound ? AccessCase::InHit : AccessCase::InMiss, invalidOffset, structure, conditionSet);

    DeferGC deferGC(vm.heap);
    AccessGenerationResult result = stubInfo.addAccessCase(codeBlock, ECMAMode::strict(), ident, newCase.release());

    if (result.generatedSomeCode()) {
		RELEASE_ASSERT(result.code());

		MacroAssembler::repatchJump(
			stubInfo.patchableJump(),
			CodeLocationLabel(result.code()));
    }

    return result.shouldGiveUpNow() ? GiveUpOnCache : RetryCacheLater;
}

void repatchIn(
    ExecState* exec, JSCell* base, const Identifier& propertyName, bool wasFound,
    const PropertySlot& slot, StructureStubInfo& stubInfo)
{
    //SuperSamplerScope superSamplerScope(false);
    if (tryRepatchIn(exec, base, propertyName, wasFound, slot, stubInfo) == GiveUpOnCache)
    	ftlThunkAwareRepatchCall(exec->codeBlock(), stubInfo.slowPathCallLocation(), operationIn);
}

static InlineCacheAction tryCacheInstanceOf(
    ExecState* exec, JSValue valueValue, JSValue prototypeValue, StructureStubInfo& stubInfo,
    bool wasFound)
{
	if (ExecutableAllocator::underMemoryPressure() || WTF::isLowMemory() || WTF::isUnderMemoryPressure())
        return GiveUpOnCache;

	if (!Options::enableInstanceOfIC())
    	return GiveUpOnCache;

    VM& vm = exec->vm();
    CodeBlock* codeBlock = exec->codeBlock();
    AccessGenerationResult result;

    RELEASE_ASSERT(valueValue.isCell()); // shouldConsiderCaching rejects non-cells.

    if(Options::forceICFailure())
    	return GiveUpOnCache;

    {
        //GCSafeConcurrentJSLocker locker(codeBlock->m_lock, vm.heap);

        JSCell* value = valueValue.asCell();
        Structure* structure = value->structure(vm);
        OwnPtr<AccessCase> newCase;
        JSObject* prototype = jsDynamicCast<JSObject*>(prototypeValue);
        if (prototype) {
            if (!jsDynamicCast<JSObject*>(value)) {
                newCase = InstanceOfAccessCase::create(
                    vm, codeBlock->ownerExecutable(), AccessCase::InstanceOfMiss, structure, ObjectPropertyConditionSet(),
                    prototype);
            } else if (structure->prototypeQueriesAreCacheable()) {
                // FIXME: Teach this to do poly proto.
                // https://bugs.webkit.org/show_bug.cgi?id=185663
            	prepareChainForCaching(exec->lexicalGlobalObject(), value, wasFound ? prototype : 0);
                ObjectPropertyConditionSet conditionSet = generateConditionsForInstanceOf(
                    vm, codeBlock->ownerExecutable(), exec, structure, prototype, wasFound);

                if (conditionSet.isValid()) {
                    newCase = InstanceOfAccessCase::create(
                        vm, codeBlock->ownerExecutable(),
                        wasFound ? AccessCase::InstanceOfHit : AccessCase::InstanceOfMiss,
                        structure, conditionSet, prototype);
                }
            }
        }

        if (!newCase)
            newCase = AccessCase::create(vm, codeBlock->ownerExecutable(), AccessCase::InstanceOfGeneric);

        //LOG_IC((ICEvent::InstanceOfAddAccessCase, structure->classInfo(), Identifier()));

        result = stubInfo.addAccessCase(codeBlock, ECMAMode::strict(), Identifier(), newCase.release());

        if (result.generatedSomeCode()) {
            //LOG_IC((ICEvent::InstanceOfReplaceWithJump, structure->classInfo(), Identifier()));

            RELEASE_ASSERT(result.code());

            MacroAssembler::repatchJump(
                stubInfo.patchableJump(),
                CodeLocationLabel(result.code()));
        }
    }

    //fireWatchpointsAndClearStubIfNeeded(vm, stubInfo, codeBlock, result);

    return result.shouldGiveUpNow() ? GiveUpOnCache : RetryCacheLater;
}

void repatchInstanceOf(
    ExecState* exec, JSValue valueValue, JSValue prototypeValue, StructureStubInfo& stubInfo,
    bool wasFound)
{
    //SuperSamplerScope superSamplerScope(false);
    if (tryCacheInstanceOf(exec, valueValue, prototypeValue, stubInfo, wasFound) == GiveUpOnCache)
    	ftlThunkAwareRepatchCall(exec->codeBlock(), stubInfo.slowPathCallLocation(), operationInstanceOfGeneric);
}

static void linkSlowFor(VM&, CallLinkInfo& callLinkInfo, MacroAssemblerCodeRef codeRef)
{
    MacroAssembler::repatchNearCall(callLinkInfo.callReturnLocation(), CodeLocationLabel(codeRef.code()));
}

static void linkSlowFor(VM& vm, CallLinkInfo& callLinkInfo, ThunkGenerator generator)
{
    linkSlowFor(vm, callLinkInfo, vm.getCTIStub(generator));
}

static void linkSlowFor(VM& vm, CallLinkInfo& callLinkInfo)
{
    MacroAssemblerCodeRef virtualThunk = virtualThunkFor(vm, callLinkInfo);
    linkSlowFor(vm, callLinkInfo, virtualThunk);
    callLinkInfo.setSlowStub(GCAwareJITStubRoutine::create(virtualThunk, vm));
}

void linkFor(
    ExecState* exec, CallLinkInfo& callLinkInfo, CodeBlock* calleeCodeBlock,
    JSFunction* callee, MacroAssemblerCodePtr codePtr)
{
    ASSERT(!callLinkInfo.stub());

    CodeBlock* callerCodeBlock = exec->callerFrame()->codeBlock();

    VM& vm = callerCodeBlock->vm();

    ASSERT(!callLinkInfo.isLinked());
    callLinkInfo.setCallee(vm, callLinkInfo.hotPathBegin(), callerCodeBlock->ownerExecutable(), callee);
    MacroAssembler::repatchPointer(callLinkInfo.hotPathBegin(), callee);
    callLinkInfo.setLastSeenCallee(vm, callerCodeBlock->ownerExecutable(), callee);
    MacroAssembler::repatchNearCall(callLinkInfo.hotPathOther(), CodeLocationLabel(codePtr));

    if (calleeCodeBlock)
        calleeCodeBlock->linkIncomingCall(exec->callerFrame(), &callLinkInfo);
    
    if (Options::enablePolymorphicCallInlining() && callLinkInfo.specializationKind() == CodeForCall && callLinkInfo.allowStubs()) {
        linkSlowFor(vm, callLinkInfo, linkPolymorphicCallThunkGenerator);
        return;
    }

    linkSlowFor(vm, callLinkInfo);
}

void linkSlowFor(
    ExecState* exec, CallLinkInfo& callLinkInfo)
{
	CodeBlock* callerCodeBlock = exec->callerFrame()->codeBlock();
	VM& vm = callerCodeBlock->vm();

	linkSlowFor(vm, callLinkInfo);
}

static void revertCall(VM& vm, CallLinkInfo& callLinkInfo, MacroAssemblerCodeRef codeRef)
{
    if (!callLinkInfo.clearedByJettison()) {
		MacroAssembler::revertJumpReplacementToBranchPtrWithPatch(
			MacroAssembler::startOfBranchPtrWithPatchOnRegister(callLinkInfo.hotPathBegin()),
			static_cast<MacroAssembler::RegisterID>(callLinkInfo.calleeGPR()), 0);
		linkSlowFor(vm, callLinkInfo, codeRef);
		MacroAssembler::repatchPointer(callLinkInfo.hotPathBegin(), reinterpret_cast<void*>(0));
	}
    callLinkInfo.clearCallee();
    callLinkInfo.clearSeen();
    callLinkInfo.clearStub();
    callLinkInfo.clearSlowStub();
    if (callLinkInfo.isOnList())
        callLinkInfo.remove();
}

void unlinkFor(VM& vm, CallLinkInfo& callLinkInfo)
{
    //if (Options::showDisassembly())
    //    dataLog("Unlinking call from ", callLinkInfo.callReturnLocation, " in request from ", pointerDump(repatchBuffer.codeBlock()), "\n");

    revertCall(vm, callLinkInfo, vm.getCTIStub(linkCallThunkGenerator));
}

void linkVirtualFor(
    ExecState* exec, CallLinkInfo& callLinkInfo)
{
    CodeBlock* callerCodeBlock = exec->callerFrame()->codeBlock();
    VM& vm = callerCodeBlock->vm();

    //if (shouldShowDisassemblyFor(callerCodeBlock))
    //    dataLog("Linking virtual call at ", *callerCodeBlock, " ", exec->callerFrame()->codeOrigin(), "\n");

    MacroAssemblerCodeRef virtualThunk = virtualThunkFor(vm, callLinkInfo);
    revertCall(vm, callLinkInfo, virtualThunk);
    callLinkInfo.setSlowStub(GCAwareJITStubRoutine::create(virtualThunk, vm));
}

namespace {
struct CallToCodePtr {
    CCallHelpers::Call call;
    MacroAssemblerCodePtr codePtr;
};
} // annonymous namespace

void linkPolymorphicCall(
    ExecState* exec, CallLinkInfo& callLinkInfo, CallVariant newVariant)
{
    CallFrame* callerFrame = exec->callerFrame();
    VM& vm = callerFrame->vm();

    // During execution of linkPolymorphicCall, we strongly assume that we never do GC.
    // GC jettisons CodeBlocks, changes CallLinkInfo etc. and breaks assumption done before and after this call.
    DeferGCForAWhile deferGCForAWhile(vm.heap);

	if (ExecutableAllocator::underMemoryPressure() || WTF::isLowMemory() || WTF::isUnderMemoryPressure()) {
        linkVirtualFor(exec, callLinkInfo);
        return;
	}

    // Currently we can't do anything for non-function callees.
    // https://bugs.webkit.org/show_bug.cgi?id=140685
    if (!newVariant || !newVariant.executable()) {
        linkVirtualFor(exec, callLinkInfo);
        return;
    }

    RELEASE_ASSERT(callLinkInfo.allowStubs());

	ASSERT(!callLinkInfo.stub());

	CodeBlock* callerCodeBlock = callerFrame->codeBlock();

    CallVariantList list;
    if (PolymorphicCallStubRoutine* stub = callLinkInfo.stub())
        list = stub->variants();
    else if (JSFunction* oldCallee = callLinkInfo.callee())
        list.append(CallVariant(oldCallee));

    list = variantListWithVariant(list, newVariant);

    // If there are any closure calls then it makes sense to treat all of them as closure calls.
    // This makes switching on callee cheaper. It also produces profiling that's easier on the DFG;
    // the DFG doesn't really want to deal with a combination of closure and non-closure callees.
    bool isClosureCall = false;
    for(auto it = list.begin(); it != list.end(); ++it) {
    	CallVariant variant = *it;
        if (variant.isClosureCall()) {
            list = despecifiedVariantList(list);
            isClosureCall = true;
            break;
        }
    }

    if (isClosureCall)
        callLinkInfo.setHasSeenClosure();

    Vector<PolymorphicCallCase> callCases;
    Vector<int64_t> caseValues;

    // Figure out what our cases are.
    for (CallVariant variant : list) {
        CodeBlock* codeBlock = nullptr;
        if (variant.executable() && !variant.executable()->isHostFunction()) {
            ExecutableBase* executable = variant.executable();
            codeBlock = jsCast<FunctionExecutable*>(executable)->generatedBytecodeFor(CodeForCall);
            // If we cannot handle a callee, because we don't have a CodeBlock,
            // assume that it's better for this whole thing to be a virtual call.
            if (!codeBlock) {
            	linkVirtualFor(exec, callLinkInfo);
                return;
            }
        }

        int64_t newCaseValue;
        if (isClosureCall)
        	newCaseValue = bitwise_cast<intptr_t>(variant.executable());
        else
        	newCaseValue = bitwise_cast<intptr_t>(variant.function());

        callCases.append(PolymorphicCallCase(variant, codeBlock));
        caseValues.append(newCaseValue);
    }
    ASSERT(callCases.size() == caseValues.size());

    // If we are over the limit, just use a normal virtual call.
    unsigned maxPolymorphicCallVariantListSize;
    if (callerCodeBlock->jitType() == JITCode::topTierJIT())
        maxPolymorphicCallVariantListSize = Options::maxPolymorphicCallVariantListSizeForTopTier();
    else
        maxPolymorphicCallVariantListSize = Options::maxPolymorphicCallVariantListSize();

    // We use list.size() instead of callCases.size() because we respect CallVariant size for now.
	if (list.size() > maxPolymorphicCallVariantListSize) {
        linkVirtualFor(exec, callLinkInfo);
        return;
    }

    Vector<CallToCodePtr> calls(callCases.size());
    uint32_t* fastCounts = 0;

    if (Options::useDFGJIT() && callerCodeBlock->jitType() != JITCode::topTierJIT()) {
        fastCounts = reinterpret_cast<uint32_t*>(fastMalloc(sizeof(uint32_t) * callCases.size()));
        memset(fastCounts, 0, callCases.size() * sizeof(uint32_t));
    }

	GPRReg calleeGPR = static_cast<GPRReg>(callLinkInfo.calleeGPR());

	CCallHelpers stubJit(&vm, callerCodeBlock);

	CCallHelpers::JumpList slowPath;

    GPRReg scratch = AssemblyHelpers::selectScratchGPR(calleeGPR);
    GPRReg comparisonValueGPR;

    if (isClosureCall) {
        // Verify that we have a function and stash the executable in scratch.

#if USE(JSVALUE64)
        slowPath.append(stubJit.branchTest64(CCallHelpers::NonZero, calleeGPR, GPRInfo::tagMaskRegister));
#else
		// We would have already checked that the callee is a cell.
#endif

		stubJit.loadPtr(calleeGPR, JSCell::structureOffset(), scratch);
		slowPath.append(
			stubJit.branch8(
				CCallHelpers::NotEqual,
				CCallHelpers::Address(scratch, Structure::typeInfoTypeOffset()),
				CCallHelpers::TrustedImm32(JSFunctionType)));

		stubJit.loadPtr(
			calleeGPR, JSFunction::offsetOfExecutable(),
			scratch);

		comparisonValueGPR = scratch;
	} else
		comparisonValueGPR = calleeGPR;

#if USE(JSVALUE64)
	stubJit.store64(
		AssemblyHelpers::TrustedImm64(ValueUndefined),
		CCallHelpers::Address(GPRInfo::callFrameRegister, static_cast<ptrdiff_t>(sizeof(Register) * JSStack::ScopeChain)));
#else
	stubJit.storePtr(
		AssemblyHelpers::TrustedImmPtr(0),
		CCallHelpers::Address(GPRInfo::callFrameRegister, static_cast<ptrdiff_t>(sizeof(Register) * JSStack::ScopeChain) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.payload)));
	stubJit.store32(
		CCallHelpers::TrustedImm32(JSValue::UndefinedTag),
		CCallHelpers::Address(GPRInfo::callFrameRegister, static_cast<ptrdiff_t>(sizeof(Register) * JSStack::ScopeChain) + OBJECT_OFFSETOF(EncodedValueDescriptor, asBits.tag)));
#endif

    GPRReg fastCountsBaseGPR = InvalidGPRReg;

    if (fastCounts) {
    	fastCountsBaseGPR = AssemblyHelpers::selectScratchGPR(calleeGPR, comparisonValueGPR, GPRInfo::regT3);
        stubJit.move(CCallHelpers::TrustedImmPtr(fastCounts), fastCountsBaseGPR);
    }

    BinarySwitch binarySwitch(comparisonValueGPR, caseValues, BinarySwitch::IntPtr);
    CCallHelpers::JumpList done;
    while (binarySwitch.advance(stubJit)) {
        size_t caseIndex = binarySwitch.caseIndex();

        PolymorphicCallCase& callCase = callCases[caseIndex];
        CallVariant variant = callCase.variant();

        MacroAssemblerCodePtr codePtr;
        if (auto* executable = variant.executable()) {
			ASSERT(variant.executable()->hasJITCodeForCall());

            ArityCheckMode arityCheck = ArityCheckNotRequired;
            if (auto* codeBlock = callCase.codeBlock()) {
                ASSERT(!variant.executable()->isHostFunction());
                if ((exec->argumentCountIncludingThis() < static_cast<size_t>(callCase.codeBlock()->numParameters()) || callLinkInfo.isVarargs()))
                    arityCheck = MustCheckArity;

            }
			codePtr = executable->generatedJITCodeForCall()->addressForCall(vm, executable, arityCheck, callLinkInfo.registerPreservationMode());
        }

        if (fastCounts) {
            stubJit.add32(
                CCallHelpers::TrustedImm32(1),
                CCallHelpers::Address(fastCountsBaseGPR, caseIndex * sizeof(uint32_t)));
        }
        calls[caseIndex].call = stubJit.nearCall();
        calls[caseIndex].codePtr = codePtr;
        done.append(stubJit.jump());
    }

	slowPath.link(&stubJit);
    binarySwitch.fallThrough().link(&stubJit);
	stubJit.move(calleeGPR, GPRInfo::regT0);
#if USE(JSVALUE32_64)
	stubJit.move(CCallHelpers::TrustedImm32(JSValue::CellTag), GPRInfo::regT1);
#endif
    stubJit.move(CCallHelpers::TrustedImmPtr(&callLinkInfo), GPRInfo::regT2);
    stubJit.move(CCallHelpers::TrustedImmPtr(callLinkInfo.callReturnLocation().executableAddress()), GPRInfo::regT4);

    stubJit.restoreReturnAddressBeforeReturn(GPRInfo::regT4);
	AssemblyHelpers::Jump slow = stubJit.jump();

	LinkBuffer patchBuffer(stubJit, callerCodeBlock, JITCompilationCanFail, ExecutableAllocator::CodeType::Special);
    if (patchBuffer.didFailToAllocate()) {
        linkVirtualFor(exec, callLinkInfo);
        return;
    }

    RELEASE_ASSERT(callCases.size() == calls.size());
    for(int i = 0; i < calls.size(); ++i) {
    	CallToCodePtr callToCodePtr = calls[i];
        patchBuffer.link(
            callToCodePtr.call, FunctionPtr(callToCodePtr.codePtr.executableAddress()));
    }
	patchBuffer.link(done, callLinkInfo.callReturnLocation().labelAtOffset(0));
	patchBuffer.link(slow, CodeLocationLabel(vm.getCTIStub(linkPolymorphicCallThunkGenerator).code()));

	RefPtr<PolymorphicCallStubRoutine> stubRoutine = adoptRef(new PolymorphicCallStubRoutine(
		FINALIZE_DFG_CODE(
			patchBuffer,
			("Polymorphic call stub for %s, return point %p, targets %s",
				toCString(*callerCodeBlock).data(), callLinkInfo.callReturnLocation().labelAtOffset(0).executableAddress(),
				""/*toCString(listDump(callCases)).data()*/)),
		vm, callerCodeBlock->ownerExecutable(), exec->callerFrame(), callLinkInfo, callCases,
        fastCounts));

    MacroAssembler::replaceWithJump(
        MacroAssembler::startOfBranchPtrWithPatchOnRegister(callLinkInfo.hotPathBegin()),
		CodeLocationLabel(stubRoutine->code().code()));
    // The original slow path is unreachable on 64-bits, but still
    // reachable on 32-bits since a non-cell callee will always
    // trigger the slow path
    linkSlowFor(vm, callLinkInfo);

    // If there had been a previous stub routine, that one will die as soon as the GC runs and sees
    // that it's no longer on stack.
	callLinkInfo.setStub(stubRoutine.release());

    // The call link info no longer has a call cache apart from the jump to the polymorphic call
    // stub.
    if (callLinkInfo.isOnList())
        callLinkInfo.remove();
}

void resetGetByID(CodeBlock* codeBlock, StructureStubInfo& stubInfo, GetByIDKind kind)
{
	ftlThunkAwareRepatchCall(codeBlock, stubInfo.slowPathCallLocation(), appropriateOptimizingGetByIdFunction(kind));
    InlineAccess::rewireStubAsJump(codeBlock->vm(), stubInfo, stubInfo.slowPathStartLocation());
}

void resetPutByID(CodeBlock* codeBlock, StructureStubInfo& stubInfo)
{
	V_JITOperation_ESsiJJI unoptimizedFunction = bitwise_cast<V_JITOperation_ESsiJJI>(readCallTarget(codeBlock, stubInfo.slowPathCallLocation()).executableAddress());
	V_JITOperation_ESsiJJI optimizedFunction;
    if (unoptimizedFunction == operationPutByIdStrict || unoptimizedFunction == operationPutByIdStrictOptimize)
        optimizedFunction = operationPutByIdStrictOptimize;
    else if (unoptimizedFunction == operationPutByIdNonStrict || unoptimizedFunction == operationPutByIdNonStrictOptimize)
        optimizedFunction = operationPutByIdNonStrictOptimize;
    else if (unoptimizedFunction == operationPutByIdDirectStrict || unoptimizedFunction == operationPutByIdDirectStrictOptimize)
        optimizedFunction = operationPutByIdDirectStrictOptimize;
    else if (unoptimizedFunction == operationPutByIdSetPrivateFieldStrict || unoptimizedFunction == operationPutByIdSetPrivateFieldStrictOptimize)
        optimizedFunction = operationPutByIdSetPrivateFieldStrictOptimize;
	else if (unoptimizedFunction == operationPutByIdDefinePrivateFieldStrict || unoptimizedFunction == operationPutByIdDefinePrivateFieldStrictOptimize)
		optimizedFunction = operationPutByIdDefinePrivateFieldStrictOptimize;
	else {
        ASSERT(unoptimizedFunction == operationPutByIdDirectNonStrict || unoptimizedFunction == operationPutByIdDirectNonStrictOptimize);
        optimizedFunction = operationPutByIdDirectNonStrictOptimize;
    }

    ftlThunkAwareRepatchCall(codeBlock, stubInfo.slowPathCallLocation(), optimizedFunction);
    InlineAccess::rewireStubAsJump(codeBlock->vm(), stubInfo, stubInfo.slowPathStartLocation());
}

static void resetPatchableJump(StructureStubInfo& stubInfo)
{
    MacroAssembler::repatchJump(stubInfo.patchableJump(), stubInfo.slowPathStartLocation());
}

void resetIn(StructureStubInfo& stubInfo)
{
    resetPatchableJump(stubInfo);
}

void resetInstanceOf(StructureStubInfo& stubInfo)
{
    resetPatchableJump(stubInfo);
}

} // namespace JSC

#endif
