* Copyright (c) 2025-2026 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.
*/
#ifndef ES2PANDA_IMPORT_CACHE_H
#define ES2PANDA_IMPORT_CACHE_H
#include <memory>
#include <mutex>
#include <shared_mutex>
#include <string>
#include <unordered_map>
#include "util/es2pandaMacros.h"
namespace ark::es2panda::parser {
enum class CacheType : uint8_t { SOURCES, METADATA };
}
namespace ark::es2panda::util {
enum class ModuleKind : uint8_t {
UNKNOWN,
PACKAGE,
MODULE,
SOURCE_DECL,
ETSCACHE_DECL,
DECLLESS_DYNAMIC,
SIMULT_MAIN,
METADATA_DECL
};
class ImportInfo;
}
namespace ark::es2panda::parser {
using SourceDeclType = std::string &&;
using SourceDeclCacheType = std::string_view;
using MetadataType = std::vector<uint8_t>;
using MetadataCacheType = std::vector<uint8_t> *;
using AnyCacheType = std::variant<SourceDeclCacheType, MetadataCacheType>;
template <CacheType TYPE, bool IS_INITIAL_TYPE = false>
using SelectCacheDataType =
std::conditional_t<TYPE == CacheType::METADATA,
std::conditional_t<IS_INITIAL_TYPE, MetadataType, MetadataCacheType>,
std::conditional_t<IS_INITIAL_TYPE, SourceDeclType, SourceDeclCacheType>>;
constexpr std::int64_t LOCK_OFF = 0;
constexpr std::int64_t LOCK_SET = std::numeric_limits<std::int64_t>::min();
class UniqueSpinMutex {
public:
UniqueSpinMutex() = default;
virtual ~UniqueSpinMutex();
NO_COPY_SEMANTIC(UniqueSpinMutex);
NO_MOVE_SEMANTIC(UniqueSpinMutex);
void lock();
void unlock();
bool try_lock();
protected:
std::atomic_int64_t spin_ {LOCK_OFF};
};
class ReadWriteSpinMutex final : public UniqueSpinMutex {
public:
ReadWriteSpinMutex() = default;
~ReadWriteSpinMutex() override = default;
NO_COPY_SEMANTIC(ReadWriteSpinMutex);
NO_MOVE_SEMANTIC(ReadWriteSpinMutex);
void lock_shared();
void unlock_shared();
bool try_lock_shared();
};
template <CacheType TYPE>
class ImportCache;
template <typename T = AnyCacheType>
struct CacheReference {
public:
T Data() const
{
static_assert(!std::is_same_v<T, AnyCacheType>,
"Cache type is unknown at compile-time, use `DataFor` to pick data for required cache type");
return data_;
}
template <CacheType TYPE>
SelectCacheDataType<TYPE> DataFor() const
{
if constexpr (std::is_same_v<T, AnyCacheType>) {
return std::get<SelectCacheDataType<TYPE>>(data_);
} else {
static_assert(std::is_same_v<T, SelectCacheDataType<TYPE>>);
return data_;
}
}
std::string_view TextSource() const
{
return textSource_;
}
std::string_view Key() const
{
return key_;
}
auto Kind() const
{
return kind_;
}
void ClearKind()
{
kind_ = util::ModuleKind::UNKNOWN;
}
protected:
template <typename T1>
void Set(CacheReference<T1> ref)
{
static_assert(std::is_same_v<T, AnyCacheType>);
static_assert(std::is_same_v<T1, SourceDeclCacheType> || std::is_same_v<T1, MetadataCacheType>);
ES2PANDA_ASSERT(Key() == ref.Key());
kind_ = ref.Kind();
textSource_ = ref.TextSource();
data_ = std::variant<SourceDeclCacheType, MetadataCacheType>(ref.Data());
}
void SetKey(std::string_view key)
{
key_ = key;
}
bool CanBePromoted() const;
bool Absent() const;
private:
std::string_view key_ {};
util::ModuleKind kind_ {};
T data_ {};
std::string_view textSource_ {};
friend ImportCache<CacheType::SOURCES>;
friend ImportCache<CacheType::METADATA>;
};
template <CacheType TYPE>
class ImportCache final {
private:
ImportCache();
public:
~ImportCache();
NO_COPY_SEMANTIC(ImportCache);
NO_MOVE_SEMANTIC(ImportCache);
static void ActivateCache();
static bool IsCacheActivated() noexcept;
static void ClearAll() noexcept;
static void GetFromCache(CacheReference<> *importInfo) noexcept;
template <util::ModuleKind KIND>
static constexpr bool AssertCacheType()
{
return (KIND >= util::ModuleKind::PACKAGE && KIND <= util::ModuleKind::SIMULT_MAIN &&
TYPE == CacheType::SOURCES) ||
(KIND == util::ModuleKind::METADATA_DECL && TYPE == CacheType::METADATA);
}
template <util::ModuleKind KIND, bool SHOULD_CACHE>
static CacheReference<SelectCacheDataType<TYPE>> StoreContents(const CacheReference<> &importInfo,
std::string textSource,
SelectCacheDataType<TYPE, true> data)
{
static_assert(AssertCacheType<KIND>());
auto cache = Instance();
ES2PANDA_ASSERT(cache != nullptr);
return cache->template Add<KIND, SHOULD_CACHE>(importInfo, textSource, std::move(data));
}
static SelectCacheDataType<TYPE> PromoteExistingEntryToLowdeclaration(const CacheReference<> &importInfo,
SelectCacheDataType<TYPE, true> data);
static void UpdateOnFileModification(std::string_view key, std::string textSource, std::string &&text,
util::ModuleKind kind) noexcept;
private:
static ImportCache *Instance() noexcept;
void Clear() noexcept;
void Get(CacheReference<> *importInfo) const noexcept;
void UpdateModifyfile(std::string_view key, std::string textSource, std::string &&text, util::ModuleKind kind);
template <util::ModuleKind KIND, bool SHOULD_CACHE>
CacheReference<SelectCacheDataType<TYPE>> Add(const CacheReference<> &importInfo, const std::string &textSource,
SelectCacheDataType<TYPE, true> data)
{
std::scoped_lock lock(dataGuard_);
const auto &newElem = dataStorage_.emplace_back(
std::make_unique<NamedData>(std::string(importInfo.Key()), textSource, std::move(data)));
CacheReference<SelectCacheDataType<TYPE>> ref {};
if constexpr (TYPE == CacheType::METADATA) {
ref.key_ = std::get<0>(*newElem);
ref.textSource_ = std::get<1>(*newElem);
constexpr auto dataIndex = 2;
ref.data_ = &(std::get<dataIndex>(*newElem));
} else {
std::tie(ref.key_, ref.textSource_, ref.data_) = *newElem;
}
ref.kind_ = KIND;
if constexpr (SHOULD_CACHE) {
if (auto it = cache_.find(std::string(importInfo.Key())); it != cache_.end()) {
if (!it->second.CanBePromoted() || ref.CanBePromoted()) {
return ref;
}
if (ref.TextSource().empty()) {
ref.textSource_ = it->second.textSource_;
}
}
cache_[std::string(ref.Key())] = ref;
}
return ref;
}
static std::shared_mutex globalGuard_;
static ImportCache *globalCache_;
mutable std::shared_mutex dataGuard_ {};
using NamedData = std::tuple<std::string, std::string, std::decay_t<SelectCacheDataType<TYPE, true>>>;
std::vector<std::unique_ptr<NamedData>> dataStorage_ {};
std::unordered_map<std::string, CacheReference<SelectCacheDataType<TYPE>>> cache_ {};
};
}
#endif