// Copyright (C) 2016 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#ifndef QV4WRITEBARRIER_P_H
#define QV4WRITEBARRIER_P_H

//
//  W A R N I N G
//  -------------
//
// This file is not part of the Qt API.  It exists purely as an
// implementation detail.  This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//

#include <private/qv4global_p.h>
#include <private/qv4enginebase_p.h>

QT_BEGIN_NAMESPACE

namespace QV4 {
struct EngineBase;
typedef quint64 ReturnedValue;

struct WriteBarrier {

    static constexpr bool isInsertionBarrier = true;

    Q_ALWAYS_INLINE static void write(EngineBase *engine, Heap::Base *base, ReturnedValue *slot, ReturnedValue value)
    {
        if (engine->isGCOngoing)
            write_slowpath(engine, base, slot, value);
        *slot = value;
    }
    Q_QML_EXPORT Q_NEVER_INLINE static void write_slowpath(
            EngineBase *engine, Heap::Base *base,
            ReturnedValue *slot, ReturnedValue value);

    Q_ALWAYS_INLINE static void write(EngineBase *engine, Heap::Base *base, Heap::Base **slot, Heap::Base *value)
    {
        if (engine->isGCOngoing)
            write_slowpath(engine, base, slot, value);
        *slot = value;
    }
    Q_QML_EXPORT Q_NEVER_INLINE static void write_slowpath(
            EngineBase *engine, Heap::Base *base,
            Heap::Base **slot, Heap::Base *value);

    // MemoryManager isn't a complete type here, so make Engine a template argument
    // so that we can still call engine->memoryManager->markStack()
    template<typename F, typename Engine = EngineBase>
    static void markCustom(Engine *engine, F &&markFunction) {
        if (engine->isGCOngoing)
            (std::forward<F>(markFunction))(engine->memoryManager->markStack());
    }

    // HeapObjectWrapper(Base) are helper classes to ensure that
    // we always use a WriteBarrier when setting heap-objects
    // they are also trivial; if triviality is not required, use Pointer instead
    struct HeapObjectWrapperBase
    {
        // enum class avoids accidental construction via brace-init
        enum class PointerWrapper : quintptr {};
        PointerWrapper wrapped;

        void clear() { wrapped = PointerWrapper(quintptr(0)); }
    };

    template<typename HeapType>
    struct HeapObjectWrapperCommon : HeapObjectWrapperBase
    {
        HeapType *get() const { return reinterpret_cast<HeapType *>(wrapped); }
        operator HeapType *() const { return get(); }
        HeapType * operator->() const { return get(); }

        template <typename ConvertibleToHeapType>
        void set(QV4::EngineBase *engine, ConvertibleToHeapType *heapObject)
        {
            WriteBarrier::markCustom(engine, [heapObject](QV4::MarkStack *ms){
                if (heapObject)
                    heapObject->mark(ms);
            });
            wrapped = static_cast<HeapObjectWrapperBase::PointerWrapper>(quintptr(heapObject));
        }
    };

    // all types are trivial; we however want to block copies bypassing the write barrier
    // therefore, all members use a PhantomTag to reduce the likelihood
    template<typename HeapType, int PhantomTag>
    struct HeapObjectWrapper : HeapObjectWrapperCommon<HeapType> {};

    /* similar Heap::Pointer, but without the Base conversion (and its inUse assert)
       and for storing references in engine classes stored on the native heap
       Stores a "non-owning" reference to a heap-item (in the C++ sense), but should
       generally mark the heap-item; therefore set goes through a write-barrier
    */
    template<typename T>
    struct Pointer
    {
        Pointer() = default;
        ~Pointer() = default;
        Q_DISABLE_COPY_MOVE(Pointer)
        T* operator->() const { return get(); }
        operator T* () const { return get(); }

        void set(EngineBase *e, T *newVal) {
            WriteBarrier::markCustom(e, [newVal](QV4::MarkStack *ms) {
                if (newVal)
                    newVal->mark(ms);
            });
            ptr = newVal;
        }

        T* get() const { return ptr; }



    private:
        T *ptr = nullptr;
    };
};

       // ### this needs to be filled with a real memory fence once marking is concurrent
Q_ALWAYS_INLINE void fence() {}

}

QT_END_NAMESPACE

#endif
