Skip to content

Commit

Permalink
.NET: Implement TryGetMember, equality, various improvements
Browse files Browse the repository at this point in the history
  • Loading branch information
praydog committed Mar 19, 2024
1 parent ca86001 commit 148b081
Show file tree
Hide file tree
Showing 10 changed files with 342 additions and 38 deletions.
2 changes: 1 addition & 1 deletion csharp-api/REFrameworkNET/Field.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ public ref class Field {

// I have no idea if this will work correctly
template<typename T>
T GetData(uintptr_t obj, bool isValueType) {
T& GetData(uintptr_t obj, bool isValueType) {
return m_field->get_data<T>((void*)obj, isValueType);
}

Expand Down
8 changes: 4 additions & 4 deletions csharp-api/REFrameworkNET/InvokeRet.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,9 @@ public ref struct InvokeRet {
Byte = ret.byte;
Word = ret.word;
DWord = ret.dword;
F = ret.f;
Float = ret.f;
QWord = ret.qword;
D = ret.d;
Double = ret.d;
Ptr = gcnew System::UIntPtr(ret.ptr);
ExceptionThrown = ret.exception_thrown;
}
Expand All @@ -26,9 +26,9 @@ public ref struct InvokeRet {
property uint8_t Byte;
property uint16_t Word;
property uint32_t DWord;
property float F;
property float Float;
property uint64_t QWord;
property double D;
property double Double;
property System::Object^ Ptr;
property bool ExceptionThrown;
};
Expand Down
167 changes: 167 additions & 0 deletions csharp-api/REFrameworkNET/ManagedObject.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
#include <string_view>

#include "TypeDefinition.hpp"
#include "TypeInfo.hpp"
#include "InvokeRet.hpp"
Expand All @@ -7,6 +9,8 @@

#include "API.hpp"

#include "Utility.hpp"

namespace REFrameworkNET {
TypeDefinition^ ManagedObject::GetTypeDefinition() {
auto result = m_object->get_type_definition();
Expand Down Expand Up @@ -66,4 +70,167 @@ namespace REFrameworkNET {
result = nullptr;
return false;
}

bool ManagedObject::TryGetMember(System::Dynamic::GetMemberBinder^ binder, System::Object^% result)
{
auto memberName = binder->Name;
auto t = this->GetTypeDefinition();

if (t == nullptr) {
return false;
}

auto field = t->FindField(memberName);

if (field != nullptr)
{
const auto field_type = field->GetType();

if (field_type == nullptr) {
return false;
}

const auto raw_ft = (reframework::API::TypeDefinition*)field_type;
const uintptr_t addr = field->IsStatic() ? 0 : this->GetAddress();
const auto vm_obj_type = field_type->GetVMObjType();

#define MAKE_TYPE_HANDLER(X, Y) \
case ##X##_fnv: \
result = gcnew Y(field->GetData<##Y##>(addr, field_type->IsValueType())); \
break;

switch (REFrameworkNET::hash(raw_ft->get_full_name())) {
MAKE_TYPE_HANDLER("System.Boolean", bool)
MAKE_TYPE_HANDLER("System.Byte", uint8_t)
MAKE_TYPE_HANDLER("System.SByte", int8_t)
MAKE_TYPE_HANDLER("System.Int16", int16_t)
MAKE_TYPE_HANDLER("System.UInt16", uint16_t)
MAKE_TYPE_HANDLER("System.Int32", int32_t)
MAKE_TYPE_HANDLER("System.UInt32", uint32_t)
MAKE_TYPE_HANDLER("System.Int64", int64_t)
MAKE_TYPE_HANDLER("System.UInt64", uint64_t)
MAKE_TYPE_HANDLER("System.Single", float)
MAKE_TYPE_HANDLER("System.Double", double)
MAKE_TYPE_HANDLER("System.Char", wchar_t)
MAKE_TYPE_HANDLER("System.IntPtr", intptr_t)
MAKE_TYPE_HANDLER("System.UIntPtr", uintptr_t)
case "System.String"_fnv:
{
if (field->IsLiteral()) {
result = gcnew System::String((const char*)field->GetInitDataPtr());
break;
}

// TODO: Check if this half of it works
auto strObject = field->GetData<reframework::API::ManagedObject*>(addr, field_type->IsValueType());

if (strObject == nullptr) {
result = nullptr;
break;
}

const auto offset = field_type->IsValueType() ? field_type->GetField("_firstChar")->GetOffsetFromFieldPtr() : field_type->GetField("_firstChar")->GetOffsetFromBase();

wchar_t* chars = (wchar_t*)((uintptr_t)strObject + offset);
result = gcnew System::String(chars);
break;
}
default:
if (vm_obj_type > VMObjType::NULL_ && vm_obj_type < VMObjType::ValType) {
switch (vm_obj_type) {
case VMObjType::Array:
//return sol::make_object(l, *(::sdk::SystemArray**)data);
result = nullptr;
break; // TODO: Implement array
default: {
//const auto td = utility::re_managed_object::get_type_definition(*(::REManagedObject**)data);
auto& obj = field->GetData<reframework::API::ManagedObject*>(addr, field_type->IsValueType());

if (obj == nullptr) {
result = nullptr;
break;
}

auto td = gcnew REFrameworkNET::TypeDefinition(obj->get_type_definition());

// another fallback incase the method returns an object which is an array
if (td != nullptr && td->GetVMObjType() == VMObjType::Array) {
//return sol::make_object(l, *(::sdk::SystemArray**)data);
result = nullptr;
break;
}

result = gcnew ManagedObject(obj);
break;
}
}
} else {
switch (field_type->GetSize()) {
case 8:
result = gcnew System::UInt64(field->GetData<uint64_t>(addr, field_type->IsValueType()));
break;
case 4:
result = gcnew System::UInt32(field->GetData<uint32_t>(addr, field_type->IsValueType()));
break;
case 2:
result = gcnew System::UInt16(field->GetData<uint16_t>(addr, field_type->IsValueType()));
break;
case 1:
result = gcnew System::Byte(field->GetData<uint8_t>(addr, field_type->IsValueType()));
break;
default:
result = nullptr;
break;
}

break;
}
};

return true;
}

/*auto property = t->FindProperty(memberName);
if (property != nullptr)
{
result = property->GetValue(this);
return true;
}*/

REFrameworkNET::API::LogInfo("Member not found: " + memberName);

result = nullptr;
return false;
}

bool ManagedObject::TrySetMember(System::Dynamic::SetMemberBinder^ binder, System::Object^ value)
{
/*auto memberName = binder->Name;
auto t = this->GetTypeDefinition();
if (t == nullptr) {
return false;
}
auto field = t->FindField(memberName);
if (field != nullptr)
{
field->SetValue(this, value);
return true;
}
auto property = t->FindProperty(memberName);
if (property != nullptr)
{
property->SetValue(this, value);
return true;
}
REFrameworkNET::API::LogInfo("Member not found: " + memberName);*/

return false;
}
}
51 changes: 50 additions & 1 deletion csharp-api/REFrameworkNET/ManagedObject.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,10 @@ namespace REFrameworkNET {
ref class TypeDefinition;
ref class TypeInfo;
ref class InvokeRet;
ref class ManagedObject;

public ref class ManagedObject : public System::Dynamic::DynamicObject {
public ref class ManagedObject : public System::Dynamic::DynamicObject, public System::IEquatable<ManagedObject^>
{
public:
ManagedObject(reframework::API::ManagedObject* obj) : m_object(obj) {
AddRef();
Expand Down Expand Up @@ -40,6 +42,51 @@ public ref class ManagedObject : public System::Dynamic::DynamicObject {
return (uintptr_t)m_object;
}

virtual bool Equals(System::Object^ other) override {
if (System::Object::ReferenceEquals(this, other)) {
return true;
}

if (System::Object::ReferenceEquals(other, nullptr)) {
return false;
}

if (other->GetType() != ManagedObject::typeid) {
return false;
}

return Ptr() == safe_cast<ManagedObject^>(other)->Ptr();
}

// Override equality operator
virtual bool Equals(ManagedObject^ other) {
if (System::Object::ReferenceEquals(this, other)) {
return true;
}

if (System::Object::ReferenceEquals(other, nullptr)) {
return false;
}

return Ptr() == other->Ptr();
}

static bool operator ==(ManagedObject^ left, ManagedObject^ right) {
if (System::Object::ReferenceEquals(left, right)) {
return true;
}

if (System::Object::ReferenceEquals(left, nullptr) || System::Object::ReferenceEquals(right, nullptr)) {
return false;
}

return left->Ptr() == right->Ptr();
}

static bool operator !=(ManagedObject^ left, ManagedObject^ right) {
return !(left == right);
}

static bool IsManagedObject(uintptr_t ptr) {
static auto fn = reframework::API::get()->param()->sdk->managed_object->is_managed_object;
return fn((void*)ptr);
Expand All @@ -50,6 +97,8 @@ public ref class ManagedObject : public System::Dynamic::DynamicObject {

REFrameworkNET::InvokeRet^ Invoke(System::String^ methodName, array<System::Object^>^ args);
virtual bool TryInvokeMember(System::Dynamic::InvokeMemberBinder^ binder, array<System::Object^>^ args, System::Object^% result) override;
virtual bool TryGetMember(System::Dynamic::GetMemberBinder^ binder, System::Object^% result) override;
virtual bool TrySetMember(System::Dynamic::SetMemberBinder^ binder, System::Object^ value) override;

// TODO methods:
/*public Void* GetReflectionProperties() {
Expand Down
59 changes: 47 additions & 12 deletions csharp-api/REFrameworkNET/Method.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@

#include "Method.hpp"

#include "Utility.hpp"

namespace REFrameworkNET {
REFrameworkNET::InvokeRet^ Method::Invoke(System::Object^ obj, array<System::Object^>^ args) {
// We need to convert the managed objects to 8 byte representations
Expand Down Expand Up @@ -96,24 +98,57 @@ bool Method::HandleInvokeMember_Internal(System::Object^ obj, System::Dynamic::I
auto tempResult = this->Invoke(obj, args);

if (tempResult != nullptr && tempResult->QWord != 0) {
// Check if the result is a managed object
if (REFrameworkNET::ManagedObject::IsManagedObject((uintptr_t)tempResult->QWord)) {
result = gcnew REFrameworkNET::ManagedObject((::REFrameworkManagedObjectHandle)tempResult->QWord);
} else {
// Check the return type of the method and return it as a NativeObject if possible
auto returnType = this->GetReturnType();
auto returnType = this->GetReturnType();

if (returnType == nullptr) {
result = tempResult;
return true;
}
if (returnType == nullptr) {
result = tempResult;
return true;
}

// Check the return type of the method and return it as a NativeObject if possible
if (!returnType->IsValueType()) {
if (returnType->GetVMObjType() == VMObjType::Object) {
if (tempResult->QWord == 0) {
result = nullptr;
return true;
}

if (!returnType->IsValueType()) {
result = gcnew REFrameworkNET::NativeObject((uintptr_t)tempResult->QWord, returnType);
result = gcnew REFrameworkNET::ManagedObject((::REFrameworkManagedObjectHandle)tempResult->QWord);
return true;
}

// TODO: other managed types
result = gcnew REFrameworkNET::NativeObject((uintptr_t)tempResult->QWord, returnType);
return true;
}

const auto raw_rt = (reframework::API::TypeDefinition*)returnType;

#define CONCAT_X_C(X, DOT, C) X ## DOT ## C

#define MAKE_TYPE_HANDLER_2(X, C, Y, Z) \
case CONCAT_X_C(#X, ".", #C)_fnv: \
result = gcnew X::C((Y)tempResult->Z); \
break;

switch (REFrameworkNET::hash(raw_rt->get_full_name().c_str())) {
MAKE_TYPE_HANDLER_2(System, Boolean, bool, Byte)
MAKE_TYPE_HANDLER_2(System, Byte, uint8_t, Byte)
MAKE_TYPE_HANDLER_2(System, UInt16, uint16_t, Word)
MAKE_TYPE_HANDLER_2(System, UInt32, uint32_t, DWord)
MAKE_TYPE_HANDLER_2(System, UInt64, uint64_t, QWord)
MAKE_TYPE_HANDLER_2(System, SByte, int8_t, Byte)
MAKE_TYPE_HANDLER_2(System, Int16, int16_t, Word)
MAKE_TYPE_HANDLER_2(System, Int32, int32_t, DWord)
MAKE_TYPE_HANDLER_2(System, Int64, int64_t, QWord)
// Because invoke wrappers returning a single actually return a double
// for consistency purposes
MAKE_TYPE_HANDLER_2(System, Single, double, Double)
MAKE_TYPE_HANDLER_2(System, Double, double, Double)

default:
result = tempResult;
break;
}
}

Expand Down
Loading

0 comments on commit 148b081

Please sign in to comment.