Skip to content

Instantly share code, notes, and snippets.

@oberrich
Created July 21, 2020 23:27
Show Gist options
  • Save oberrich/a58786ae50db4cc97e5b70f19fa6dc9e to your computer and use it in GitHub Desktop.
Save oberrich/a58786ae50db4cc97e5b70f19fa6dc9e to your computer and use it in GitHub Desktop.
#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