/*
 * Copyright (c) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "ecmascript/mem/local_cmc/cc_evacuator-inl.h"

#include "ecmascript/js_hclass-inl.h"
#include "ecmascript/mem/tlab_allocator-inl.h"
#include "ecmascript/mem/work_manager-inl.h"
#include "ecmascript/free_object.h"

namespace panda::ecmascript {
TaggedObject *CCEvacuator::CopyObject(TaggedObject *fromObj, const MarkWord &markWord)
{
    JSHClass *klass = markWord.GetJSHClass();
    size_t size = klass->SizeFromJSHClass(fromObj);
    uintptr_t forwardAddress = tlab_->Allocate(size);
    if (UNLIKELY(forwardAddress == 0)) {
        LOG_ECMA_MEM(FATAL) << "EvacuateObject alloc failed: " << " size: " << size;
        UNREACHABLE();
    }
    errno_t res = memcpy_s(ToVoidPtr(forwardAddress), size, fromObj, size);
    if (res != EOK) {
        LOG_FULL(FATAL) << "memcpy_s failed " << res;
    }
    std::atomic_thread_fence(std::memory_order_seq_cst);
    MarkWordType oldValue = markWord.GetValue();
    MarkWordType result = Barriers::AtomicSetPrimitive(fromObj, 0, oldValue,
                                                       MarkWord::FromForwardingAddress(forwardAddress));
    if (result == oldValue) {
        TaggedObject *toObject = reinterpret_cast<TaggedObject*>(forwardAddress);
        heap_->OnMoveEvent(reinterpret_cast<uintptr_t>(fromObj), toObject, size);
        return toObject;
    }
    ASSERT(MarkWord::IsForwardingAddress(result));
    FreeObject::FillFreeObject(heap_, forwardAddress, size);
    return MarkWord::ToForwardingAddress(result);
}

TaggedObject *CCEvacuator::Copy(TaggedObject *fromObj, const MarkWord &markWord)
{
    TaggedObject *toObject = CopyObject(fromObj, markWord);
    if constexpr (G_USE_STICKY_CMS_GC) {
        Region *toRegion = Region::ObjectAddressToRange(toObject);
        toRegion->AtomicMark(toObject);
        toObject->SetObjectState(ObjectState::OLD);
        JSHClass *klass = toObject->GetClass();
        size_t size = klass->SizeFromJSHClass(toObject);
        toRegion->IncreaseGCAliveSize(size);
    }
    return toObject;
}
}  // namespace panda::ecmascript