diff --git a/csharp-api/REFrameworkNET/IObject.hpp b/csharp-api/REFrameworkNET/IObject.hpp index a2e6de48e..45f874115 100644 --- a/csharp-api/REFrameworkNET/IObject.hpp +++ b/csharp-api/REFrameworkNET/IObject.hpp @@ -6,7 +6,7 @@ namespace REFrameworkNET { ref class TypeDefinition; -ref struct InvokeRet; +value struct InvokeRet; // Base interface of ManagedObject and NativeObject public interface class IObject : public IProxyable, public System::IEquatable { diff --git a/csharp-api/REFrameworkNET/IProxyable.hpp b/csharp-api/REFrameworkNET/IProxyable.hpp index c8c8e5a42..812eb16b7 100644 --- a/csharp-api/REFrameworkNET/IProxyable.hpp +++ b/csharp-api/REFrameworkNET/IProxyable.hpp @@ -3,7 +3,7 @@ #include namespace REFrameworkNET { -ref struct InvokeRet; +value struct InvokeRet; public interface class IProxyable { void* Ptr(); diff --git a/csharp-api/REFrameworkNET/InvokeRet.hpp b/csharp-api/REFrameworkNET/InvokeRet.hpp index 5d7f75297..cfb63d450 100644 --- a/csharp-api/REFrameworkNET/InvokeRet.hpp +++ b/csharp-api/REFrameworkNET/InvokeRet.hpp @@ -6,85 +6,46 @@ #pragma managed namespace REFrameworkNET { -public ref struct InvokeRet { - InvokeRet(const reframework::InvokeRet& ret) { - using namespace System::Runtime::InteropServices; - Marshal::Copy(System::IntPtr((void*)&ret), m_invokeRetBytes, 0, sizeof(::reframework::InvokeRet)); - } +[System::Runtime::InteropServices::StructLayout(System::Runtime::InteropServices::LayoutKind::Explicit, Pack = 1, Size = 129)] +public value struct InvokeRet { + [System::Runtime::InteropServices::FieldOffset(0), System::Runtime::InteropServices::MarshalAs( + System::Runtime::InteropServices::UnmanagedType::ByValArray, + ArraySubType = System::Runtime::InteropServices::UnmanagedType::U1, + SizeConst = 128) + ] + unsigned char Bytes; - reframework::InvokeRet* Marshal() { - pin_ptr pinned_bytes = &m_invokeRetBytes[0]; - uint8_t* bytes = pinned_bytes; + [System::Runtime::InteropServices::FieldOffset(0)] + uint8_t Byte; - reframework::InvokeRet* ret = reinterpret_cast(bytes); + [System::Runtime::InteropServices::FieldOffset(0)] + uint16_t Word; - return ret; - } + [System::Runtime::InteropServices::FieldOffset(0)] + uint32_t DWord; - property System::Span Bytes { - public: - System::Span get() { - pin_ptr pinned_bytes = &m_invokeRetBytes[0]; - uint8_t* bytes = pinned_bytes; - return System::Span(bytes, 128); - } - }; + [System::Runtime::InteropServices::FieldOffset(0)] + uint64_t QWord; - property uint8_t Byte { - public: - uint8_t get() { - return m_invokeRetBytes[0]; - } - } + [System::Runtime::InteropServices::FieldOffset(0)] + float Float; - property uint16_t Word { - public: - uint16_t get() { - return Marshal()->word; - } - } + [System::Runtime::InteropServices::FieldOffset(0)] + double Double; - property uint32_t DWord { - public: - uint32_t get() { - return Marshal()->dword; - } - } - property float Float { - public: - float get() { - return Marshal()->f; - } - } - property uint64_t QWord { - public: - uint64_t get() { - return Marshal()->qword; - } - } + [System::Runtime::InteropServices::FieldOffset(0)] + uintptr_t Ptr; - property double Double { - public: - double get() { - return Marshal()->d; - } - } - property System::Object^ Ptr { - public: - System::Object^ get() { - return System::UIntPtr(Marshal()->ptr); - } - } + [System::Runtime::InteropServices::FieldOffset(128)] + bool ExceptionThrown; - property bool ExceptionThrown { - public: - bool get() { - return Marshal()->exception_thrown; - } + // Method to convert unmanaged InvokeRet to managed InvokeRet + static InvokeRet FromNative(const reframework::InvokeRet& native) { + InvokeRet managed; + pin_ptr pinned = &managed.Bytes; + memcpy(pinned, &native.bytes, 129); + managed.ExceptionThrown = native.exception_thrown; + return managed; } - -private: - //::reframework::InvokeRet m_impl; - array^ m_invokeRetBytes = gcnew array(sizeof(::reframework::InvokeRet)); }; } \ No newline at end of file diff --git a/csharp-api/REFrameworkNET/ManagedObject.hpp b/csharp-api/REFrameworkNET/ManagedObject.hpp index 94060af64..fda437612 100644 --- a/csharp-api/REFrameworkNET/ManagedObject.hpp +++ b/csharp-api/REFrameworkNET/ManagedObject.hpp @@ -11,7 +11,7 @@ namespace REFrameworkNET { ref class TypeDefinition; ref class TypeInfo; -ref class InvokeRet; +value struct InvokeRet; ref class ManagedObject; public ref class ManagedObject : public REFrameworkNET::UnifiedObject diff --git a/csharp-api/REFrameworkNET/Method.cpp b/csharp-api/REFrameworkNET/Method.cpp index fd3f28d79..abcd230bf 100644 --- a/csharp-api/REFrameworkNET/Method.cpp +++ b/csharp-api/REFrameworkNET/Method.cpp @@ -128,7 +128,11 @@ bool Method::IsOverride() { return GetMatchingParentMethods()->Count > 0; } -REFrameworkNET::InvokeRet^ Method::Invoke(System::Object^ obj, array^ args) { +REFrameworkNET::InvokeRet Method::Invoke(System::Object^ obj, array^ args) { + return REFrameworkNET::InvokeRet::FromNative(Invoke_Internal(obj, args)); +} + +::reframework::InvokeRet Method::Invoke_Internal(System::Object^ obj, array^ args) { if (obj == nullptr && !this->IsStatic()) { System::String^ declaringName = this->GetDeclaringType() != nullptr ? this->GetDeclaringType()->GetFullName() : gcnew System::String("UnknownType"); System::String^ errorStr = "Cannot invoke a non-static method without an object (" + declaringName + "." + this->GetName() + ")"; @@ -248,110 +252,105 @@ REFrameworkNET::InvokeRet^ Method::Invoke(System::Object^ obj, arrayMessage); } - const auto native_result = m_method->invoke((reframework::API::ManagedObject*)obj_ptr, args2); - - return gcnew REFrameworkNET::InvokeRet(native_result); + return m_method->invoke((reframework::API::ManagedObject*)obj_ptr, args2); } bool Method::HandleInvokeMember_Internal(System::Object^ obj, array^ args, System::Object^% result) { - //auto methodName = binder->Name; - auto tempResult = this->Invoke(obj, args); + auto tempResult = this->Invoke_Internal(obj, args); + auto returnType = this->GetReturnType(); - if (tempResult != nullptr) { - auto returnType = this->GetReturnType(); + if (returnType == nullptr) { + // box the result + result = safe_cast(REFrameworkNET::InvokeRet::FromNative(tempResult)); + return true; + } - if (returnType == nullptr) { - result = tempResult; + // Check the return type of the method and return it as a NativeObject if possible + if (!returnType->IsValueType()) { + if (tempResult.qword == 0) { + result = nullptr; return true; } - // Check the return type of the method and return it as a NativeObject if possible - if (!returnType->IsValueType()) { - if (tempResult->QWord == 0) { - result = nullptr; - return true; - } - - if (returnType->GetVMObjType() == VMObjType::Object) { - result = REFrameworkNET::ManagedObject::Get((::REFrameworkManagedObjectHandle)tempResult->QWord); - return true; - } - - if (returnType->GetVMObjType() == VMObjType::String) { - // Maybe don't create the GC version and just use the native one? - auto strObject = REFrameworkNET::ManagedObject::Get((::REFrameworkManagedObjectHandle)tempResult->QWord); - auto strType = strObject->GetTypeDefinition(); - const auto firstCharField = strType->GetField("_firstChar"); - uint32_t offset = 0; - - if (firstCharField != nullptr) { - offset = strType->GetField("_firstChar")->GetOffsetFromBase(); - } else { - const auto fieldOffset = *(uint32_t*)(*(uintptr_t*)tempResult->QWord - sizeof(void*)); - offset = fieldOffset + 4; - } + if (returnType->GetVMObjType() == VMObjType::Object) { + result = REFrameworkNET::ManagedObject::Get((::REFrameworkManagedObjectHandle)tempResult.qword); + return true; + } - wchar_t* chars = (wchar_t*)((uintptr_t)strObject->Ptr() + offset); - result = gcnew System::String(chars); - return true; - } + if (returnType->GetVMObjType() == VMObjType::String) { + // Maybe don't create the GC version and just use the native one? + auto strObject = REFrameworkNET::ManagedObject::Get((::REFrameworkManagedObjectHandle)tempResult.qword); + auto strType = strObject->GetTypeDefinition(); + const auto firstCharField = strType->GetField("_firstChar"); + uint32_t offset = 0; - if (returnType->GetVMObjType() == VMObjType::Array) { - // TODO? Implement array - result = REFrameworkNET::ManagedObject::Get((::REFrameworkManagedObjectHandle)tempResult->QWord); - return true; + if (firstCharField != nullptr) { + offset = strType->GetField("_firstChar")->GetOffsetFromBase(); + } else { + const auto fieldOffset = *(uint32_t*)(*(uintptr_t*)tempResult.qword - sizeof(void*)); + offset = fieldOffset + 4; } - // TODO: other managed types - result = gcnew REFrameworkNET::NativeObject((uintptr_t)tempResult->QWord, returnType); + wchar_t* chars = (wchar_t*)((uintptr_t)strObject->Ptr() + offset); + result = gcnew System::String(chars); return true; } - if (returnType->IsEnum()) { - if (auto underlying = returnType->GetUnderlyingType(); underlying != nullptr) { - returnType = underlying; // easy mode - } + if (returnType->GetVMObjType() == VMObjType::Array) { + // TODO? Implement array + result = REFrameworkNET::ManagedObject::Get((::REFrameworkManagedObjectHandle)tempResult.qword); + 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) - case "System.RuntimeTypeHandle"_fnv: { - result = gcnew REFrameworkNET::TypeDefinition((::REFrameworkTypeDefinitionHandle)tempResult->QWord); - break; - } - case "System.RuntimeMethodHandle"_fnv: { - result = gcnew REFrameworkNET::Method((::REFrameworkMethodHandle)tempResult->QWord); - break; - } - case "System.RuntimeFieldHandle"_fnv: { - result = gcnew REFrameworkNET::Field((::REFrameworkFieldHandle)tempResult->QWord); - break; + // TODO: other managed types + result = gcnew REFrameworkNET::NativeObject((uintptr_t)tempResult.qword, returnType); + return true; + } + + if (returnType->IsEnum()) { + if (auto underlying = returnType->GetUnderlyingType(); underlying != nullptr) { + returnType = underlying; // easy mode } - default: - result = tempResult; + } + + 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, d) + MAKE_TYPE_HANDLER_2(System, Double, double, d) + case "System.RuntimeTypeHandle"_fnv: { + result = gcnew REFrameworkNET::TypeDefinition((::REFrameworkTypeDefinitionHandle)tempResult.qword); + break; + } + case "System.RuntimeMethodHandle"_fnv: { + result = gcnew REFrameworkNET::Method((::REFrameworkMethodHandle)tempResult.qword); + break; + } + case "System.RuntimeFieldHandle"_fnv: { + result = gcnew REFrameworkNET::Field((::REFrameworkFieldHandle)tempResult.qword); + break; + } + default: + result = safe_cast(REFrameworkNET::InvokeRet::FromNative(tempResult)); + break; } return true; diff --git a/csharp-api/REFrameworkNET/Method.hpp b/csharp-api/REFrameworkNET/Method.hpp index 53f02b048..0eb2e81a2 100644 --- a/csharp-api/REFrameworkNET/Method.hpp +++ b/csharp-api/REFrameworkNET/Method.hpp @@ -41,7 +41,12 @@ public ref class Method : public System::IEquatable /// Generally should not be used unless you know what you're doing. /// Use the other invoke method to automatically convert the return value correctly into a usable object. /// - REFrameworkNET::InvokeRet^ Invoke(System::Object^ obj, array^ args); + REFrameworkNET::InvokeRet Invoke(System::Object^ obj, array^ args); + +private: + ::reframework::InvokeRet Invoke_Internal(System::Object^ obj, array^ args); + +public: /// /// Invokes this method with the given arguments. diff --git a/csharp-api/REFrameworkNET/NativeObject.hpp b/csharp-api/REFrameworkNET/NativeObject.hpp index 5ec990ea5..77b21e913 100644 --- a/csharp-api/REFrameworkNET/NativeObject.hpp +++ b/csharp-api/REFrameworkNET/NativeObject.hpp @@ -9,7 +9,7 @@ #include "ObjectEnumerator.hpp" namespace REFrameworkNET { -ref class InvokeRet; +value struct InvokeRet; // Native objects are objects that are NOT managed objects // However, they still have reflection information associated with them diff --git a/csharp-api/REFrameworkNET/TypeDefinition.hpp b/csharp-api/REFrameworkNET/TypeDefinition.hpp index 633e5a53a..890230e80 100644 --- a/csharp-api/REFrameworkNET/TypeDefinition.hpp +++ b/csharp-api/REFrameworkNET/TypeDefinition.hpp @@ -14,7 +14,7 @@ ref class Method; ref class Field; ref class Property; ref class TypeInfo; -ref struct InvokeRet; +value struct InvokeRet; /// /// A shorthand enum for determining how a is used in the VM. diff --git a/csharp-api/REFrameworkNET/UnifiedObject.hpp b/csharp-api/REFrameworkNET/UnifiedObject.hpp index 48af611c6..f89ca36cb 100644 --- a/csharp-api/REFrameworkNET/UnifiedObject.hpp +++ b/csharp-api/REFrameworkNET/UnifiedObject.hpp @@ -5,7 +5,7 @@ namespace REFrameworkNET { ref class TypeDefinition; -ref struct InvokeRet; +value struct InvokeRet; // UnifiedObject is the base class that ManagedObject and NativeObject will derive from // It will have several shared methods but some unimplemented methods that will be implemented in the derived classes