//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------

#include "Backend.h"

#if ENABLE_OOP_NATIVE_CODEGEN
#include "JITServer/JITServer.h"

ServerThreadContext::ServerThreadContext(ThreadContextDataIDL* data, ProcessContext* processContext) :
    m_threadContextData(*data),
    m_refCount(0),
    m_numericPropertyBV(nullptr),
    m_preReservedSectionAllocator(processContext->processHandle),
    m_sectionAllocator(processContext->processHandle),
    m_codePageAllocators(nullptr, ALLOC_XDATA, &m_sectionAllocator, &m_preReservedSectionAllocator, processContext->processHandle),
    m_thunkPageAllocators(nullptr, /* allocXData */ false, &m_sectionAllocator, nullptr, processContext->processHandle),
#if defined(_CONTROL_FLOW_GUARD) && !defined(_M_ARM)
    m_jitThunkEmitter(this, &m_sectionAllocator, processContext->processHandle),
#endif
    m_pageAlloc(nullptr, Js::Configuration::Global.flags, PageAllocatorType_BGJIT,
        AutoSystemInfo::Data.IsLowMemoryProcess() ?
        PageAllocator::DefaultLowMaxFreePageCount :
        PageAllocator::DefaultMaxFreePageCount
    ),
    processContext(processContext),
    m_canCreatePreReservedSegment(data->allowPrereserveAlloc != FALSE)
{
    m_pid = GetProcessId(processContext->processHandle);

    m_numericPropertyBV = HeapNew(BVSparse<HeapAllocator>, &HeapAllocator::Instance);
}

ServerThreadContext::~ServerThreadContext()
{
    processContext->Release();
    if (this->m_numericPropertyBV != nullptr)
    {
        HeapDelete(m_numericPropertyBV);
        this->m_numericPropertyBV = nullptr;
    }

}

PreReservedSectionAllocWrapper *
ServerThreadContext::GetPreReservedSectionAllocator()
{
    return &m_preReservedSectionAllocator;
}

intptr_t
ServerThreadContext::GetBailOutRegisterSaveSpaceAddr() const
{
    return static_cast<intptr_t>(m_threadContextData.bailOutRegisterSaveSpaceAddr);
}

ptrdiff_t
ServerThreadContext::GetChakraBaseAddressDifference() const
{
    return GetRuntimeChakraBaseAddress() - (intptr_t)AutoSystemInfo::Data.GetChakraBaseAddr();
}

ptrdiff_t
ServerThreadContext::GetCRTBaseAddressDifference() const
{
    return GetRuntimeCRTBaseAddress() - GetJITCRTBaseAddress();
}

intptr_t
ServerThreadContext::GetDisableImplicitFlagsAddr() const
{
    return static_cast<intptr_t>(m_threadContextData.disableImplicitFlagsAddr);
}

intptr_t
ServerThreadContext::GetImplicitCallFlagsAddr() const
{
    return static_cast<intptr_t>(m_threadContextData.implicitCallFlagsAddr);
}

#ifdef ENABLE_WASM_SIMD
intptr_t
ServerThreadContext::GetSimdTempAreaAddr(uint8 tempIndex) const
{
    Assert(tempIndex < SIMD_TEMP_SIZE);
    return m_threadContextData.simdTempAreaBaseAddr + tempIndex * sizeof(_x86_SIMDValue);
}
#endif

intptr_t
ServerThreadContext::GetThreadStackLimitAddr() const
{
    return static_cast<intptr_t>(m_threadContextData.threadStackLimitAddr);
}

size_t
ServerThreadContext::GetScriptStackLimit() const
{
    return static_cast<size_t>(m_threadContextData.scriptStackLimit);
}

bool
ServerThreadContext::IsThreadBound() const
{
    return m_threadContextData.isThreadBound != FALSE;
}

HANDLE
ServerThreadContext::GetProcessHandle() const
{
    return this->processContext->processHandle;
}

CustomHeap::OOPCodePageAllocators *
ServerThreadContext::GetThunkPageAllocators()
{
    return &m_thunkPageAllocators;
}

SectionAllocWrapper *
ServerThreadContext::GetSectionAllocator()
{
    return &m_sectionAllocator;
}

CustomHeap::OOPCodePageAllocators *
ServerThreadContext::GetCodePageAllocators()
{
    return &m_codePageAllocators;
}

#if defined(_CONTROL_FLOW_GUARD) && !defined(_M_ARM)
OOPJITThunkEmitter *
ServerThreadContext::GetJITThunkEmitter()
{
    return &m_jitThunkEmitter;
}
#endif

intptr_t
ServerThreadContext::GetRuntimeChakraBaseAddress() const
{
    return this->processContext->chakraBaseAddress;
}

intptr_t
ServerThreadContext::GetRuntimeCRTBaseAddress() const
{
    return this->processContext->crtBaseAddress;
}

/* static */
intptr_t
ServerThreadContext::GetJITCRTBaseAddress()
{
    return (intptr_t)AutoSystemInfo::Data.GetCRTHandle();
}

PageAllocator *
ServerThreadContext::GetForegroundPageAllocator()
{
    return &m_pageAlloc;
}

bool
ServerThreadContext::CanCreatePreReservedSegment() const
{
    return m_canCreatePreReservedSegment;
}

void
ServerThreadContext::SetCanCreatePreReservedSegment(bool value)
{
    m_canCreatePreReservedSegment = value;
}

bool
ServerThreadContext::IsNumericProperty(Js::PropertyId propertyId)
{
    if (propertyId >= 0 && Js::IsInternalPropertyId(propertyId))
    {
        return Js::InternalPropertyRecords::GetInternalPropertyName(propertyId)->IsNumeric();
    }

    bool found = false;
    {
        AutoCriticalSection lock(&m_cs);
        found = m_numericPropertyBV->Test(propertyId) != FALSE;
    }

    return found;
}

void
ServerThreadContext::UpdateNumericPropertyBV(BVSparseNode * newProps)
{
    AutoCriticalSection lock(&m_cs);
    m_numericPropertyBV->CopyFromNode(newProps);
}

void ServerThreadContext::AddRef()
{
    InterlockedExchangeAdd(&m_refCount, (uint)1);
}
void ServerThreadContext::Release()
{
    InterlockedExchangeSubtract(&m_refCount, (uint)1);
    if (m_isClosed && m_refCount == 0)
    {
        HeapDelete(this);
    }
}
void ServerThreadContext::Close()
{
    this->m_isClosed = true;
#ifdef STACK_BACK_TRACE
    ServerContextManager::RecordCloseContext(this);
#endif
}

ProcessContext::ProcessContext(HANDLE processHandle, intptr_t chakraBaseAddress, intptr_t crtBaseAddress) :
    processHandle(processHandle),
    chakraBaseAddress(chakraBaseAddress),
    crtBaseAddress(crtBaseAddress),
    refCount(0)
{
}
ProcessContext::~ProcessContext()
{
    CloseHandle(processHandle);
}

void ProcessContext::AddRef()
{
    InterlockedExchangeAdd(&this->refCount, 1);
}
void ProcessContext::Release()
{
    InterlockedExchangeSubtract(&this->refCount, 1);
}

bool ProcessContext::HasRef()
{
    return this->refCount != 0;
}

#endif
