Created
July 21, 2020 23:27
-
-
Save oberrich/a58786ae50db4cc97e5b70f19fa6dc9e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <bit> | |
#include "stackext.hpp" | |
#ifdef RECLASS_PROXY_SINGLE_THREAD | |
# define RECLASS_PROXY_STORAGE_DUR | |
#else | |
# define RECLASS_PROXY_STORAGE_DUR thread_local | |
#endif | |
#define RECLASS_AUTO_INDIRECTION : public auto_indirection_tag | |
extern "C" bool reclass_proxy_read(std::uintptr_t addr, void *buffer, std::size_t size); | |
extern "C" bool reclass_proxy_write(std::uintptr_t addr, void const *buffer, std::size_t size); | |
namespace detail { | |
// 1MB of shared memory (per thread unless RECLASS_PROXY_SINGLE_THREAD is defined) between class_proxy instances | |
static RECLASS_PROXY_STORAGE_DUR stackext::stack_allocator class_proxy_stack{ 1024 * 1024 }; | |
template <typename T> | |
inline constexpr bool is_ptr_reflectable_v = std::is_pointer_v<T> && refl::trait::is_reflectable_v<std::remove_pointer_t<T>>; | |
template <typename T, bool = std::is_member_object_pointer_v<T>> | |
struct member_object_pointer_traits { | |
}; | |
template <typename T, typename U> | |
struct member_object_pointer_traits<T U::*, true> { | |
using class_type = U; | |
using value_type = T; | |
}; | |
template <typename T> | |
inline static constexpr auto members() noexcept { | |
return refl::util::filter(get_members(refl::reflect<T>()), [](auto const member) constexpr { | |
if constexpr (is_field(member)) | |
return (is_writable(member) || is_readable(member)) && !is_static(member); | |
return false; | |
}); | |
} | |
template <typename T> | |
constexpr auto make_mem_ptr_tpl(T members) noexcept { | |
if constexpr (T::size != 1) { | |
return std::tuple{}; | |
} else { | |
using Head = refl::trait::get_t<0, T>; | |
if constexpr (detail::is_ptr_reflectable_v<typename Head::value_type>) { | |
return std::tuple_cat(std::make_tuple(Head::pointer), detail::make_mem_ptr_tpl(detail::members<std::remove_pointer_t<typename Head::value_type>>())); | |
} else { | |
return std::tuple{}; | |
} | |
} | |
}; | |
template <typename T> | |
inline static constexpr auto bit_cast_to_uint(T&& offset) noexcept { | |
return __builtin_bit_cast( | |
std::conditional_t<sizeof(T) == 4, std::uint32_t, | |
std::conditional_t<sizeof(T) == 8, std::uint64_t, | |
void>>, | |
offset); | |
} | |
}; | |
struct auto_indirection_tag {}; | |
template <typename T> | |
class reclass_proxy : | |
public refl::runtime::proxy<reclass_proxy<T>, T> | |
{ | |
friend struct refl::runtime::proxy<reclass_proxy<T>, T>; | |
public: | |
inline constexpr reclass_proxy(std::uintptr_t instance, bool valid = true) | |
: m_allocator{detail::class_proxy_stack} | |
, m_virtual_data{detail::bit_cast_to_uint(m_allocator.allocate<alignof(T)>(virtual_size()))} | |
, m_instance{instance} | |
, m_valid{valid} | |
{ | |
if (m_valid) { | |
m_valid = sync(); | |
} | |
} | |
inline bool sync() const noexcept { | |
return reclass_proxy_read(m_instance + virtual_start(), reinterpret_cast<void *>(m_virtual_data), virtual_size()); | |
} | |
inline explicit operator bool() const noexcept { | |
return is_valid(); | |
} | |
inline bool is_valid() const noexcept { | |
return m_valid; | |
} | |
private: | |
inline constexpr T const& target() const noexcept { | |
return *(reinterpret_cast<T *>(m_virtual_data - virtual_start())); | |
} | |
template <typename U, typename... Us> | |
inline static bool resolve_indirections_impl(std::uintptr_t &instance, U&& first, Us&&... args) noexcept { | |
if (reclass_proxy_read(instance + std::forward<U>(first), &instance, sizeof(std::uintptr_t)) && instance) { | |
if constexpr (sizeof...(Us) > 0) { | |
return reclass_proxy::resolve_indirections_impl(instance, std::forward<Us>(args)...); | |
} | |
return true; | |
} | |
return false; | |
} | |
inline static bool resolve_indirections_impl(std::uintptr_t &instance) noexcept { | |
return instance != 0; | |
} | |
template <typename Tuple> | |
inline static bool resolve_indirections(std::uintptr_t &instance, Tuple &&t) noexcept { | |
return std::apply([&instance](auto&&...xs) { | |
return reclass_proxy::resolve_indirections_impl(instance, xs...); | |
}, std::forward<Tuple>(t)); | |
} | |
template <typename Member, typename Self, typename... Args> | |
inline static constexpr decltype(auto) invoke_impl(Self&& self, Args&&... args) noexcept { | |
constexpr Member member{}; | |
decltype(auto) data = const_cast<T &>(self.target()); | |
if constexpr (constexpr auto is_static = !std::is_member_object_pointer_v<decltype(Member::pointer)>; is_static || !is_field(member)) { | |
if constexpr (is_static) { | |
return member(std::forward<Args>(args)...); | |
} else { | |
return member(data, std::forward<Args>(args)...); | |
} | |
} else { | |
static_assert(sizeof...(Args) <= 1, "Invalid number of arguments provided for property!"); | |
if constexpr (sizeof...(Args) == 1) { | |
static_assert(is_writable(member)); | |
member(data) = refl::util::identity(std::forward<Args>(args)...); | |
self.m_valid = reclass_proxy_write(self.m_instance + detail::bit_cast_to_uint(Member::pointer), &member(data), sizeof(typename Member::value_type)); | |
return refl::util::make_const(member(data)); | |
} else { | |
static_assert(is_readable(member)); | |
using ValueType = std::remove_pointer_t<typename Member::value_type>; | |
if constexpr (detail::is_ptr_reflectable_v<typename Member::value_type>) { | |
if constexpr (std::is_base_of_v<auto_indirection_tag, ValueType>) { | |
static_assert(sizeof(typename Member::value_type) == sizeof(std::uintptr_t)); | |
auto bit_cast_to_uint_tpl = [](auto &&...ptrs) noexcept { | |
return std::make_tuple(detail::bit_cast_to_uint(std::forward<decltype(ptrs)>(ptrs))...); | |
}; | |
auto mem_ptrs = detail::make_mem_ptr_tpl(detail::members<ValueType>()); | |
auto raw_ptrs = std::apply(bit_cast_to_uint_tpl, mem_ptrs); | |
constexpr std::size_t mem_ptrs_size = std::tuple_size_v<decltype(mem_ptrs)>; | |
using TailValueType = std::conditional_t< | |
mem_ptrs_size != 0 | |
, typename std::remove_pointer_t<typename detail::member_object_pointer_traits<std::remove_cv_t<std::tuple_element_t<mem_ptrs_size - 1, decltype(mem_ptrs)>>>::value_type> | |
, ValueType | |
>; | |
std::uintptr_t instance = detail::bit_cast_to_uint(member(data)); | |
auto valid = instance != 0 && resolve_indirections(instance, raw_ptrs); | |
return reclass_proxy<TailValueType>{instance, valid}; | |
} else { | |
auto instance = detail::bit_cast_to_uint(member(data)); | |
return reclass_proxy<ValueType>{instance, instance != 0}; | |
} | |
} else { | |
return refl::util::make_const(member(data)); | |
} | |
} | |
} | |
__builtin_unreachable(); | |
} | |
using Members = decltype(detail::members<T>()); | |
inline constexpr auto virtual_start() const noexcept { | |
using Head = typename refl::trait::get_t<0, Members>; | |
return detail::bit_cast_to_uint(Head::pointer); | |
} | |
inline constexpr auto virtual_size() const noexcept { | |
using Tail = typename refl::trait::get_t<Members::size - 1, Members>; | |
constexpr auto tail_size = std::max<std::size_t>(alignof(T), sizeof(typename Tail::value_type)); | |
return detail::bit_cast_to_uint(Tail::pointer) + tail_size - virtual_start(); | |
} | |
stackext::scoped_allocator m_allocator; | |
std::uintptr_t m_virtual_data; | |
std::uintptr_t m_instance; | |
bool m_valid; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment