/*
 * Copyright (C) 2008 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. 
 */

#pragma once

#include "ArityCheckMode.h"
#include "CallFrame.h"
#include "Disassembler.h"
#include "JITStubs.h"
#include "JSValue.h"
#include "MacroAssemblerCodeRef.h"
#include "RegisterPreservationMode.h"
#include "Profiler.h"
#include <wtf/PassOwnPtr.h>

#include <limits>

namespace JSC {

namespace DFG {
class CommonData;
class JITCode;
}
namespace FTL {
class JITCode;
}

#if ENABLE(JIT)
class JSGlobalData;
class JSStack;
#endif

class JITCode : public RefCounted<JITCode> {
public:
	typedef MacroAssemblerCodeRef CodeRef;
	typedef MacroAssemblerCodePtr CodePtr;

    enum JITType {
        None,
        HostCallThunk,
        InterpreterThunk,
        BaselineJIT,
        DFGJIT,
        FTLJIT
    };

	static JITType bottomTierJIT()
	{
		return BaselineJIT;
	}

	static JITType topTierJIT()
	{
        return FTLJIT;
	}

    static JITType nextTierJIT(JITType jitType)
    {
        switch (jitType) {
        case BaselineJIT:
            return DFGJIT;
        case DFGJIT:
            return FTLJIT;
        default:
            RELEASE_ASSERT_NOT_REACHED();
        }
        return DFGJIT;
    }

    static bool isJIT(JITType jitType)
    {
        switch (jitType) {
        case BaselineJIT:
        case DFGJIT:
        case FTLJIT:
            return true;
        default:
            return false;
        }
    }

    static double timeToLive(JITType jitType)
    {
        switch (jitType) {
        case InterpreterThunk:
            return 5.0;
        case BaselineJIT:
            // Effectively 10 additional seconds, since BaselineJIT and
            // InterpreterThunk share a CodeBlock.
            return 15.0;
        case DFGJIT:
            return 20.0;
        case FTLJIT:
            return 60.0;
        default:
            return std::numeric_limits<double>::max();
        }
    }

    static bool isLowerTier(JITType expectedLower, JITType expectedHigher)
    {
        RELEASE_ASSERT(isJIT(expectedLower));
        RELEASE_ASSERT(isJIT(expectedHigher));
        return expectedLower < expectedHigher;
    }

    static bool isHigherTier(JITType expectedHigher, JITType expectedLower)
    {
        return isLowerTier(expectedLower, expectedHigher);
    }

    static bool isLowerOrSameTier(JITType expectedLower, JITType expectedHigher)
    {
        return !isHigherTier(expectedLower, expectedHigher);
    }

    static bool isHigherOrSameTier(JITType expectedHigher, JITType expectedLower)
    {
        return isLowerOrSameTier(expectedLower, expectedHigher);
    }

    static bool isOptimizingJIT(JITType jitType)
    {
        return jitType == DFGJIT || jitType == FTLJIT;
    }

	static bool couldBeInterpreted(JITType jitType)
	{
		switch (jitType) {
		case InterpreterThunk:
		case BaselineJIT:
			return true;
		default:
			return false;
		}
	}

	static bool isBaselineCode(JITType jitType)
	{
		return jitType == InterpreterThunk || jitType == BaselineJIT;
	}

protected:
	JITCode(JITType jitType)
	    : m_jitType(jitType)
	{
	}

public:
    virtual ~JITCode() = default;

    JITType jitType() const
    {
        return m_jitType;
    }

    template<typename PointerType>
    static ALWAYS_INLINE JITType jitTypeFor(const PointerType& jitCode)
    {
        if (UNLIKELY(!jitCode))
            return None;
        return jitCode->jitType();
    }

    virtual CodePtr addressForCall(JSGlobalData&, ExecutableBase*, ArityCheckMode, RegisterPreservationMode) = 0;
    virtual void* executableAddressAtOffset(size_t offset) = 0;
    void* executableAddress() { return executableAddressAtOffset(0); }
    virtual void* dataAddressAtOffset(size_t offset) = 0;
    virtual unsigned offsetOf(void* pointerIntoCode) = 0;

    virtual DFG::CommonData* dfgCommon();
    virtual DFG::JITCode* dfg();
    virtual FTL::JITCode* ftl();

    JSValue execute(JSStack*, CallFrame*, VM*);

    void* start() { return dataAddressAtOffset(0); }
    virtual size_t size() = 0;
    void* end() { return reinterpret_cast<void*>(reinterpret_cast<uintptr_t>(start()) + size()); }

    virtual bool contains(void*) = 0;

private:
    JITType m_jitType;
};

class JITCodeWithCodeRef : public JITCode {
protected:
    JITCodeWithCodeRef(JITType);
    JITCodeWithCodeRef(CodeRef, JITType);

public:
    virtual ~JITCodeWithCodeRef();

    void* executableAddressAtOffset(size_t offset);
    void* dataAddressAtOffset(size_t offset);
    unsigned offsetOf(void* pointerIntoCode);
    size_t size();
    bool contains(void*);

protected:
    CodeRef m_ref;
};

class DirectJITCode : public JITCodeWithCodeRef {
public:
    DirectJITCode(JITType);
    DirectJITCode(CodeRef, CodePtr withArityCheck, JITType);
    virtual ~DirectJITCode();

    void initializeCodeRef(CodeRef ref, CodePtr withArityCheck);

    virtual CodePtr addressForCall(JSGlobalData&, ExecutableBase*, ArityCheckMode, RegisterPreservationMode) OVERRIDE;

private:
    struct RegisterPreservationWrappers {
        CodeRef withoutArityCheck;
        CodeRef withArityCheck;
    };

#if ENABLE(FTL_JIT)
    RegisterPreservationWrappers* ensureWrappers();
#endif

    CodePtr m_withArityCheck;

#if ENABLE(FTL_JIT)
    OwnPtr<RegisterPreservationWrappers> m_wrappers;
#endif
};

class NativeJITCode final : public JITCodeWithCodeRef {
public:
    NativeJITCode(JITType);
    NativeJITCode(CodeRef, JITType);
    virtual ~NativeJITCode();

    void initializeCodeRef(CodeRef ref);

    virtual CodePtr addressForCall(JSGlobalData&, ExecutableBase*, ArityCheckMode, RegisterPreservationMode) OVERRIDE;
};

};
