#pragma once
#include "Luau/CodeAllocationData.h"
#include "Luau/CodeGen.h"
#include "Luau/Common.h"
#include "Luau/NativeProtoExecData.h"
#include <array>
#include <atomic>
#include <memory>
#include <mutex>
#include <optional>
#include <stdint.h>
#include <unordered_map>
#include <vector>
namespace Luau
{
namespace CodeGen
{
struct CodeAllocator;
class NativeModule;
class NativeModuleRef;
class SharedCodeAllocator;
class NativeModule
{
public:
NativeModule(
SharedCodeAllocator* allocator,
const std::optional<ModuleId>& moduleId,
const uint8_t* moduleBaseAddress,
std::vector<NativeProtoExecDataPtr> nativeProtos
) noexcept;
NativeModule(
SharedCodeAllocator* allocator,
const std::optional<ModuleId>& moduleId,
CodeAllocationData codeAllocationData,
std::vector<NativeProtoExecDataPtr> nativeProtos
) noexcept;
NativeModule(const NativeModule&) = delete;
NativeModule(NativeModule&&) = delete;
NativeModule& operator=(const NativeModule&) = delete;
NativeModule& operator=(NativeModule&&) = delete;
~NativeModule() noexcept;
size_t addRef() const noexcept;
size_t addRefs(size_t count) const noexcept;
size_t release() const noexcept;
[[nodiscard]] size_t getRefcount() const noexcept;
[[nodiscard]] const std::optional<ModuleId>& getModuleId() const noexcept;
[[nodiscard]] const uint8_t* getModuleBaseAddress() const noexcept;
[[nodiscard]] CodeAllocationData getCodeAllocationData() const noexcept;
[[nodiscard]] const uint32_t* tryGetNativeProto(uint32_t bytecodeId) const noexcept;
[[nodiscard]] const std::vector<NativeProtoExecDataPtr>& getNativeProtos() const noexcept;
private:
mutable std::atomic<size_t> refcount = 0;
SharedCodeAllocator* allocator = nullptr;
std::optional<ModuleId> moduleId = {};
const uint8_t* moduleBaseAddress_DEPRECATED = nullptr;
CodeAllocationData codeAllocationData;
std::vector<NativeProtoExecDataPtr> nativeProtos = {};
};
class NativeModuleRef
{
public:
NativeModuleRef() noexcept = default;
NativeModuleRef(const NativeModule* nativeModule) noexcept;
NativeModuleRef(const NativeModuleRef& other) noexcept;
NativeModuleRef(NativeModuleRef&& other) noexcept;
NativeModuleRef& operator=(NativeModuleRef other) noexcept;
~NativeModuleRef() noexcept;
void reset() noexcept;
void swap(NativeModuleRef& other) noexcept;
[[nodiscard]] bool empty() const noexcept;
explicit operator bool() const noexcept;
[[nodiscard]] const NativeModule* get() const noexcept;
[[nodiscard]] const NativeModule* operator->() const noexcept;
[[nodiscard]] const NativeModule& operator*() const noexcept;
private:
const NativeModule* nativeModule = nullptr;
};
class SharedCodeAllocator
{
public:
SharedCodeAllocator(CodeAllocator* codeAllocator) noexcept;
SharedCodeAllocator(const SharedCodeAllocator&) = delete;
SharedCodeAllocator(SharedCodeAllocator&&) = delete;
SharedCodeAllocator& operator=(const SharedCodeAllocator&) = delete;
SharedCodeAllocator& operator=(SharedCodeAllocator&&) = delete;
~SharedCodeAllocator() noexcept;
[[nodiscard]] NativeModuleRef tryGetNativeModule(const ModuleId& moduleId) const noexcept;
std::pair<NativeModuleRef, bool> getOrInsertNativeModule(
const ModuleId& moduleId,
std::vector<NativeProtoExecDataPtr> nativeProtos,
const uint8_t* data,
size_t dataSize,
const uint8_t* code,
size_t codeSize
);
NativeModuleRef insertAnonymousNativeModule(
std::vector<NativeProtoExecDataPtr> nativeProtos,
const uint8_t* data,
size_t dataSize,
const uint8_t* code,
size_t codeSize
);
void eraseNativeModuleIfUnreferenced(const NativeModule& nativeModule);
private:
struct ModuleIdHash
{
[[nodiscard]] size_t operator()(const ModuleId& moduleId) const noexcept;
};
[[nodiscard]] NativeModuleRef tryGetNativeModuleWithLockHeld(const ModuleId& moduleId) const noexcept;
mutable std::mutex mutex;
std::unordered_map<ModuleId, std::unique_ptr<NativeModule>, ModuleIdHash, std::equal_to<>> identifiedModules;
std::atomic<size_t> anonymousModuleCount = 0;
CodeAllocator* codeAllocator = nullptr;
};
} }