Skip to content

Commit

Permalink
.NET: Add direct static method calling in generated ref assemblies
Browse files Browse the repository at this point in the history
  • Loading branch information
praydog committed Apr 26, 2024
1 parent f29afeb commit 3625963
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 158 deletions.
119 changes: 36 additions & 83 deletions csharp-api/AssemblyGenerator/ClassGenerator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,33 +13,6 @@
using Microsoft.CodeAnalysis.Operations;
using REFrameworkNET;
using System;
using System.ComponentModel.DataAnnotations;

/*public interface CrappyTest {
public string Concat(object arg0);
public string Concat(object arg0, object arg1);
public string Concat(object arg0, object arg1, object arg2);
public string Concat(global::System.Object[] args);
public string Concat(string str0, string str1);
public string Concat(string str0, string str1, string str2);
public string Concat(string str0, string str1, string str2, string str3);
public string Concat(object str0, object str1);
public string Concat(object str0, object str1, object str2);
public string Concat(object str0, object str1, object str2, object str3);
public string Concat(global::System.String[] values);
public string Format(string format, object arg0);
}*/

public class ClassGenerator {
private string className;
Expand All @@ -49,6 +22,8 @@ public class ClassGenerator {
private TypeDeclarationSyntax? typeDeclaration;
private bool addedNewKeyword = false;

private List<FieldDeclarationSyntax> internalFieldDeclarations = [];

public TypeDeclarationSyntax? TypeDeclaration {
get {
return typeDeclaration;
Expand Down Expand Up @@ -91,37 +66,6 @@ public ClassGenerator(string className_, REFrameworkNET.TypeDefinition t_) {
}

private static TypeSyntax MakeProperType(REFrameworkNET.TypeDefinition? targetType, REFrameworkNET.TypeDefinition? containingType) {
if (containingType != null && targetType != null) {
if (containingType.FullName.StartsWith("System.")) {
/*var containingRtType = containingType.GetRuntimeType();
var targetRtType = targetType.GetRuntimeType();
if (containingRtType != null && targetRtType != null) {
var containingRtAssembly = (ManagedObject)containingRtType.Call("get_Assembly");
var targetRtAssembly = (ManagedObject)targetRtType.Call("get_Assembly");
if ((string)containingRtAssembly.Call("get_FullName") != (string)targetRtAssembly.Call("get_FullName")) {
System.Console.WriteLine("Method " + containingType.FullName + " is referencing type " + targetType.FullName + " in a different assembly");
return SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.ObjectKeyword));
}
}*/

// Check if any of the generic arguments are not in the same assembly
/*if (containingRtType != null && targetType.IsGenericType()) {
foreach (REFrameworkNET.TypeDefinition td in targetType.GenericArguments) {
if (td != null) {
var rtType = td.GetRuntimeType();
if (rtType != null) {
if ((ManagedObject)containingRtType.Call("get_Assembly") != (ManagedObject)rtType.Call("get_Assembly")) {
return SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.ObjectKeyword));
}
}
}
}
}*/
}
}

TypeSyntax outSyntax = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.VoidKeyword));

string ogTargetTypename = targetType != null ? targetType.GetFullName() : "";
Expand Down Expand Up @@ -294,12 +238,6 @@ private static TypeSyntax MakeProperType(REFrameworkNET.TypeDefinition? targetTy

typeDeclaration = GenerateMethods(baseTypes);

List<FieldDeclarationSyntax> internalFieldDeclarations = [];

if (internalFieldDeclarations.Count > 0) {
typeDeclaration = typeDeclaration.AddMembers(internalFieldDeclarations.ToArray());
}

if (baseTypes.Count > 0 && typeDeclaration != null) {
refTypeFieldDecl = refTypeFieldDecl.AddModifiers(SyntaxFactory.Token(SyntaxKind.NewKeyword));
//fieldDeclaration2 = fieldDeclaration2.AddModifiers(SyntaxFactory.Token(SyntaxKind.NewKeyword));
Expand All @@ -312,6 +250,11 @@ private static TypeSyntax MakeProperType(REFrameworkNET.TypeDefinition? targetTy
//typeDeclaration = typeDeclaration.AddMembers(refProxyFieldDecl);
}

// Logically needs to come after the REFType field is added as they reference it
if (internalFieldDeclarations.Count > 0 && typeDeclaration != null) {
typeDeclaration = typeDeclaration.AddMembers(internalFieldDeclarations.ToArray());
}

return GenerateNestedTypes();
}

Expand All @@ -326,7 +269,7 @@ private TypeDeclarationSyntax GenerateMethods(List<SimpleBaseTypeSyntax> baseTyp

HashSet<string> seenMethodSignatures = [];

var validMethods = new List<REFrameworkNET.Method>();
List<REFrameworkNET.Method> validMethods = [];

try {
foreach(REFrameworkNET.Method m in methods) {
Expand Down Expand Up @@ -376,6 +319,8 @@ private TypeDeclarationSyntax GenerateMethods(List<SimpleBaseTypeSyntax> baseTyp
SyntaxFactory.ParseAttributeArgumentList("(" + method.GetIndex().ToString() + ")")))
);

bool anyOutParams = false;

if (method.Parameters.Count > 0) {
// If any of the params have ! in them, skip this method
if (method.Parameters.Any(param => param != null && (param.Type == null || (param.Type != null && param.Type.FullName.Contains('!'))))) {
Expand All @@ -395,6 +340,7 @@ private TypeDeclarationSyntax GenerateMethods(List<SimpleBaseTypeSyntax> baseTyp

bool anyUnsafeParams = false;


if (runtimeParams != null) {
foreach (dynamic param in runtimeParams) {
if (param.get_IsRetval() == true) {
Expand Down Expand Up @@ -430,6 +376,7 @@ private TypeDeclarationSyntax GenerateMethods(List<SimpleBaseTypeSyntax> baseTyp
if (isOut == true) {
simpleMethodSignature += "out";
modifiers.Add(SyntaxFactory.Token(SyntaxKind.OutKeyword));
anyOutParams = true;
}

if (isByRef == true) {
Expand Down Expand Up @@ -461,36 +408,42 @@ private TypeDeclarationSyntax GenerateMethods(List<SimpleBaseTypeSyntax> baseTyp
}

if (method.IsStatic()) {
// Add System.ComponentModel.Description("static") attribute
//methodDeclaration = methodDeclaration.AddAttributeLists(SyntaxFactory.AttributeList().AddAttributes(SyntaxFactory.Attribute(SyntaxFactory.ParseName("global::System.ComponentModel.DescriptionAttribute"), SyntaxFactory.ParseAttributeArgumentList("(\"static\")"))));

// lets see what happens if we just make it static
/*methodDeclaration = methodDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.StaticKeyword));
methodDeclaration = methodDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.StaticKeyword));

// Now we must add a body to it that actually calls the method
// We have our REFType field, so we can lookup the method and call it
// Make a private static field to hold the REFrameworkNET.Method
var internalFieldName = "INTERNAL_" + method.GetIndex().ToString();
var internalFieldName = "INTERNAL_" + method.Name + method.GetIndex().ToString();
var methodVariableDeclaration = SyntaxFactory.VariableDeclaration(SyntaxFactory.ParseTypeName("global::REFrameworkNET.Method"))
.AddVariables(SyntaxFactory.VariableDeclarator(internalFieldName).WithInitializer(SyntaxFactory.EqualsValueClause(SyntaxFactory.ParseExpression("REFType.GetMethod(\"" + method.Name + "\")"))));
.AddVariables(SyntaxFactory.VariableDeclarator(internalFieldName).WithInitializer(SyntaxFactory.EqualsValueClause(SyntaxFactory.ParseExpression("REFType.GetMethod(\"" + method.GetMethodSignature() + "\")"))));

var methodFieldDeclaration = SyntaxFactory.FieldDeclaration(methodVariableDeclaration).AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword), SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword));
internalFieldDeclarations.Add(methodFieldDeclaration);

// Now we can add the body
// bool HandleInvokeMember_Internal(System::Object^ obj, array<System::Object^>^ args, System::Object^% result);
List<StatementSyntax> bodyStatements = [];

if (method.ReturnType.FullName == "System.Void") {
var body = internalFieldName + ".Invoke(null, null)";
methodDeclaration = methodDeclaration.AddBodyStatements(SyntaxFactory.ParseStatement(body));
if (method.Parameters.Count == 0) {
bodyStatements.Add(SyntaxFactory.ParseStatement(internalFieldName + ".Invoke(null, null);"));
} else if (!anyOutParams) {
bodyStatements.Add(SyntaxFactory.ParseStatement(internalFieldName + ".Invoke(null, new object[] {" + string.Join(", ", method.Parameters.Select(param => param.Name)) + "});"));
} else {
bodyStatements.Add(SyntaxFactory.ParseStatement("throw new System.NotImplementedException();")); // TODO: Implement this
}
} else {
var body1 = "object INTERNAL_result = null;";
var body2 = internalFieldName + ".HandleInvokeMember_Internal(null, " + (method.Parameters.Count > 0 ? "new object[] {" + string.Join(", ", method.Parameters.Select(param => param.Name)) + "}" : "null") + ", ref INTERNAL_result);";
string body3 = "return (" + returnType.GetText() + ")INTERNAL_result;";
if (method.Parameters.Count == 0) {
bodyStatements.Add(SyntaxFactory.ParseStatement("return (" + returnType.GetText().ToString() + ")" + internalFieldName + ".InvokeBoxed(typeof(" + returnType.GetText().ToString() + "), null, null);"));
} else if (!anyOutParams) {
bodyStatements.Add(SyntaxFactory.ParseStatement("return (" + returnType.GetText().ToString() + ")" + internalFieldName + ".InvokeBoxed(typeof(" + returnType.GetText().ToString() + "), null, new object[] {" + string.Join(", ", method.Parameters.Select(param => param.Name)) + "});"));
} else {
bodyStatements.Add(SyntaxFactory.ParseStatement("throw new System.NotImplementedException();")); // TODO: Implement this
}
}

methodDeclaration = methodDeclaration.AddBodyStatements(
[SyntaxFactory.ParseStatement(body1), SyntaxFactory.ParseStatement(body2), SyntaxFactory.ParseStatement(body3)]
);
}*/
methodDeclaration = methodDeclaration.AddBodyStatements(
[.. bodyStatements]
);
}

if (seenMethodSignatures.Contains(simpleMethodSignature)) {
Expand Down
80 changes: 80 additions & 0 deletions csharp-api/REFrameworkNET/Method.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "API.hpp"
#include "VM.hpp"
#include "SystemString.hpp"
#include "Proxy.hpp"

#include "Utility.hpp"

Expand Down Expand Up @@ -132,6 +133,85 @@ REFrameworkNET::InvokeRet Method::Invoke(System::Object^ obj, array<System::Obje
return REFrameworkNET::InvokeRet::FromNative(Invoke_Internal(obj, args));
}

public interface class DummyInterface {

};

System::Object^ Method::InvokeBoxed(System::Type^ targetReturnType, System::Object^ obj, array<System::Object^>^ args) {
System::Object^ result = nullptr;
this->HandleInvokeMember_Internal(obj, args, result);

if (result == nullptr) {
return nullptr; // ez
}

if (targetReturnType == nullptr) {
return result;
}

if (!targetReturnType->IsPrimitive && !targetReturnType->IsEnum && !targetReturnType->IsInterface) {
if (targetReturnType == String::typeid) {
return result;
}

if (targetReturnType == REFrameworkNET::ManagedObject::typeid) {
return result;
}

if (targetReturnType == REFrameworkNET::NativeObject::typeid) {
return result;
}

if (targetReturnType == REFrameworkNET::TypeDefinition::typeid) {
return result;
}

if (targetReturnType == REFrameworkNET::Method::typeid) {
return result;
}

if (targetReturnType == REFrameworkNET::Field::typeid) {
return result;
}
}

if (targetReturnType->IsEnum) {
auto underlyingType = targetReturnType->GetEnumUnderlyingType();

if (underlyingType != nullptr) {
auto underlyingResult = Convert::ChangeType(result, underlyingType);
return Enum::ToObject(targetReturnType, underlyingResult);
}
}

if (this->DeclaringType == nullptr) {
return result;
}

if (!targetReturnType->IsPrimitive && targetReturnType->IsInterface) {
auto iobjectResult = dynamic_cast<REFrameworkNET::IObject^>(result);

if (iobjectResult != nullptr && targetReturnType->IsInterface) {
// Caching mechanism to prevent creating multiple proxies for the same object and type so we dont stress the GC
if (auto existingProxy = iobjectResult->GetProxy(targetReturnType); existingProxy != nullptr) {
return existingProxy;
}

auto proxy = System::Reflection::DispatchProxy::Create(targetReturnType, Proxy<DummyInterface^, IObject^>::typeid->GetGenericTypeDefinition()->MakeGenericType(targetReturnType, result->GetType()));
((IProxy^)proxy)->SetInstance(iobjectResult);

if (auto unified = dynamic_cast<REFrameworkNET::UnifiedObject^>(iobjectResult); unified != nullptr) {
unified->AddProxy(targetReturnType, (IProxy^)proxy);
}

result = proxy;
return result;
}
}

return result;
}

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

System::Object^ InvokeBoxed(System::Type^ targetReturnType, System::Object^ obj, array<System::Object^>^ args);

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

Expand Down
76 changes: 7 additions & 69 deletions csharp-api/REFrameworkNET/Proxy.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -154,86 +154,24 @@ public ref class Proxy : public Reflection::DispatchProxy, public IProxy, public
if (methodAttribute != nullptr) {
if (iobject != nullptr) {
auto method = methodAttribute->GetMethod(iobject->GetTypeDefinition());
iobject->HandleInvokeMember_Internal(method, args, result);
return method->InvokeBoxed(targetMethod->ReturnType, iobject, args);
} else {
throw gcnew System::InvalidOperationException("Proxy: T2 must be IObject derived");
}
} else {
// This is a fallback
if (iobject != nullptr) {
iobject->HandleInvokeMember_Internal(targetMethod->Name, args, result);
} else {
throw gcnew System::InvalidOperationException("Proxy: T2 must be IObject derived");
}
}

auto targetReturnType = targetMethod->ReturnType;

if (targetReturnType == nullptr) {
return result;
}

if (!targetReturnType->IsPrimitive && !targetReturnType->IsEnum) {
if (targetReturnType == String::typeid) {
return result;
}

if (targetReturnType == REFrameworkNET::ManagedObject::typeid) {
return result;
}

if (targetReturnType == REFrameworkNET::NativeObject::typeid) {
return result;
}

if (targetReturnType == REFrameworkNET::TypeDefinition::typeid) {
return result;
}

if (targetReturnType == REFrameworkNET::Method::typeid) {
return result;
}
auto method = iobject->GetTypeDefinition()->GetMethod(targetMethod->Name);

if (targetReturnType == REFrameworkNET::Field::typeid) {
return result;
}
}

if (targetReturnType->IsEnum) {
auto underlyingType = targetReturnType->GetEnumUnderlyingType();

if (underlyingType != nullptr) {
auto underlyingResult = Convert::ChangeType(result, underlyingType);
return Enum::ToObject(targetReturnType, underlyingResult);
}
}

if (targetMethod->DeclaringType == nullptr) {
return result;
}

if (!targetReturnType->IsPrimitive && targetMethod->DeclaringType->IsInterface && result != nullptr) {
auto iobjectResult = dynamic_cast<REFrameworkNET::IObject^>(result);

if (iobjectResult != nullptr && targetReturnType->IsInterface) {
// Caching mechanism to prevent creating multiple proxies for the same object and type so we dont stress the GC
if (auto existingProxy = iobjectResult->GetProxy(targetReturnType); existingProxy != nullptr) {
return existingProxy;
}

auto proxy = DispatchProxy::Create(targetReturnType, Proxy<T, T2>::typeid->GetGenericTypeDefinition()->MakeGenericType(T::typeid, result->GetType()));
((IProxy^)proxy)->SetInstance(iobjectResult);

if (auto unified = dynamic_cast<REFrameworkNET::UnifiedObject^>(iobjectResult); unified != nullptr) {
unified->AddProxy(targetReturnType, (IProxy^)proxy);
if (method != nullptr) {
return method->InvokeBoxed(targetMethod->ReturnType, iobject, args);
}

result = proxy;
return result;
} else {
throw gcnew System::InvalidOperationException("Proxy: T2 must be IObject derived");
}
}

return result;
return nullptr;
}

private:
Expand Down
Loading

0 comments on commit 3625963

Please sign in to comment.