Skip to content

Commit

Permalink
.NET: Optimize InvokeRet to use stack allocation
Browse files Browse the repository at this point in the history
  • Loading branch information
praydog committed Apr 16, 2024
1 parent a974a6e commit 0b4a934
Show file tree
Hide file tree
Showing 9 changed files with 128 additions and 163 deletions.
2 changes: 1 addition & 1 deletion csharp-api/REFrameworkNET/IObject.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<IObject^> {
Expand Down
2 changes: 1 addition & 1 deletion csharp-api/REFrameworkNET/IProxyable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
#include <cstdint>

namespace REFrameworkNET {
ref struct InvokeRet;
value struct InvokeRet;

public interface class IProxyable {
void* Ptr();
Expand Down
101 changes: 31 additions & 70 deletions csharp-api/REFrameworkNET/InvokeRet.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<uint8_t> pinned_bytes = &m_invokeRetBytes[0];
uint8_t* bytes = pinned_bytes;
[System::Runtime::InteropServices::FieldOffset(0)]
uint8_t Byte;

reframework::InvokeRet* ret = reinterpret_cast<reframework::InvokeRet*>(bytes);
[System::Runtime::InteropServices::FieldOffset(0)]
uint16_t Word;

return ret;
}
[System::Runtime::InteropServices::FieldOffset(0)]
uint32_t DWord;

property System::Span<uint8_t> Bytes {
public:
System::Span<uint8_t> get() {
pin_ptr<uint8_t> pinned_bytes = &m_invokeRetBytes[0];
uint8_t* bytes = pinned_bytes;
return System::Span<uint8_t>(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<System::Byte> pinned = &managed.Bytes;
memcpy(pinned, &native.bytes, 129);
managed.ExceptionThrown = native.exception_thrown;
return managed;
}

private:
//::reframework::InvokeRet m_impl;
array<uint8_t>^ m_invokeRetBytes = gcnew array<uint8_t>(sizeof(::reframework::InvokeRet));
};
}
2 changes: 1 addition & 1 deletion csharp-api/REFrameworkNET/ManagedObject.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
171 changes: 85 additions & 86 deletions csharp-api/REFrameworkNET/Method.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,11 @@ bool Method::IsOverride() {
return GetMatchingParentMethods()->Count > 0;
}

REFrameworkNET::InvokeRet^ Method::Invoke(System::Object^ obj, array<System::Object^>^ args) {
REFrameworkNET::InvokeRet Method::Invoke(System::Object^ obj, array<System::Object^>^ args) {
return REFrameworkNET::InvokeRet::FromNative(Invoke_Internal(obj, args));
}

::reframework::InvokeRet Method::Invoke_Internal(System::Object^ obj, array<System::Object^>^ 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() + ")";
Expand Down Expand Up @@ -248,110 +252,105 @@ REFrameworkNET::InvokeRet^ Method::Invoke(System::Object^ obj, array<System::Obj
System::Console::WriteLine("Error converting object: " + e->Message);
}

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<System::Object^>^ 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<System::Object^>(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<System::Object^>(REFrameworkNET::InvokeRet::FromNative(tempResult));
break;
}

return true;
Expand Down
7 changes: 6 additions & 1 deletion csharp-api/REFrameworkNET/Method.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,12 @@ public ref class Method : public System::IEquatable<Method^>
/// 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.
/// </remarks>
REFrameworkNET::InvokeRet^ Invoke(System::Object^ obj, array<System::Object^>^ args);
REFrameworkNET::InvokeRet Invoke(System::Object^ obj, array<System::Object^>^ args);

private:
::reframework::InvokeRet Invoke_Internal(System::Object^ obj, array<System::Object^>^ args);

public:

/// <summary>
/// Invokes this method with the given arguments.
Expand Down
2 changes: 1 addition & 1 deletion csharp-api/REFrameworkNET/NativeObject.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion csharp-api/REFrameworkNET/TypeDefinition.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ ref class Method;
ref class Field;
ref class Property;
ref class TypeInfo;
ref struct InvokeRet;
value struct InvokeRet;

/// <summary>
/// A shorthand enum for determining how a <see cref="TypeDefinition"/> is used in the VM.
Expand Down
2 changes: 1 addition & 1 deletion csharp-api/REFrameworkNET/UnifiedObject.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit 0b4a934

Please sign in to comment.