Skip to content

Commit 9bcbdec

Browse files
committed
.NET: Add direct static method calling in generated ref assemblies
1 parent cd76bc4 commit 9bcbdec

File tree

6 files changed

+155
-158
lines changed

6 files changed

+155
-158
lines changed

csharp-api/AssemblyGenerator/ClassGenerator.cs

+36-83
Original file line numberDiff line numberDiff line change
@@ -13,33 +13,6 @@
1313
using Microsoft.CodeAnalysis.Operations;
1414
using REFrameworkNET;
1515
using System;
16-
using System.ComponentModel.DataAnnotations;
17-
18-
/*public interface CrappyTest {
19-
public string Concat(object arg0);
20-
21-
public string Concat(object arg0, object arg1);
22-
23-
public string Concat(object arg0, object arg1, object arg2);
24-
25-
public string Concat(global::System.Object[] args);
26-
27-
public string Concat(string str0, string str1);
28-
29-
public string Concat(string str0, string str1, string str2);
30-
31-
public string Concat(string str0, string str1, string str2, string str3);
32-
33-
public string Concat(object str0, object str1);
34-
35-
public string Concat(object str0, object str1, object str2);
36-
37-
public string Concat(object str0, object str1, object str2, object str3);
38-
39-
public string Concat(global::System.String[] values);
40-
41-
public string Format(string format, object arg0);
42-
}*/
4316

4417
public class ClassGenerator {
4518
private string className;
@@ -49,6 +22,8 @@ public class ClassGenerator {
4922
private TypeDeclarationSyntax? typeDeclaration;
5023
private bool addedNewKeyword = false;
5124

25+
private List<FieldDeclarationSyntax> internalFieldDeclarations = [];
26+
5227
public TypeDeclarationSyntax? TypeDeclaration {
5328
get {
5429
return typeDeclaration;
@@ -91,37 +66,6 @@ public ClassGenerator(string className_, REFrameworkNET.TypeDefinition t_) {
9166
}
9267

9368
private static TypeSyntax MakeProperType(REFrameworkNET.TypeDefinition? targetType, REFrameworkNET.TypeDefinition? containingType) {
94-
if (containingType != null && targetType != null) {
95-
if (containingType.FullName.StartsWith("System.")) {
96-
/*var containingRtType = containingType.GetRuntimeType();
97-
var targetRtType = targetType.GetRuntimeType();
98-
99-
if (containingRtType != null && targetRtType != null) {
100-
var containingRtAssembly = (ManagedObject)containingRtType.Call("get_Assembly");
101-
var targetRtAssembly = (ManagedObject)targetRtType.Call("get_Assembly");
102-
103-
if ((string)containingRtAssembly.Call("get_FullName") != (string)targetRtAssembly.Call("get_FullName")) {
104-
System.Console.WriteLine("Method " + containingType.FullName + " is referencing type " + targetType.FullName + " in a different assembly");
105-
return SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.ObjectKeyword));
106-
}
107-
}*/
108-
109-
// Check if any of the generic arguments are not in the same assembly
110-
/*if (containingRtType != null && targetType.IsGenericType()) {
111-
foreach (REFrameworkNET.TypeDefinition td in targetType.GenericArguments) {
112-
if (td != null) {
113-
var rtType = td.GetRuntimeType();
114-
if (rtType != null) {
115-
if ((ManagedObject)containingRtType.Call("get_Assembly") != (ManagedObject)rtType.Call("get_Assembly")) {
116-
return SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.ObjectKeyword));
117-
}
118-
}
119-
}
120-
}
121-
}*/
122-
}
123-
}
124-
12569
TypeSyntax outSyntax = SyntaxFactory.PredefinedType(SyntaxFactory.Token(SyntaxKind.VoidKeyword));
12670

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

295239
typeDeclaration = GenerateMethods(baseTypes);
296240

297-
List<FieldDeclarationSyntax> internalFieldDeclarations = [];
298-
299-
if (internalFieldDeclarations.Count > 0) {
300-
typeDeclaration = typeDeclaration.AddMembers(internalFieldDeclarations.ToArray());
301-
}
302-
303241
if (baseTypes.Count > 0 && typeDeclaration != null) {
304242
refTypeFieldDecl = refTypeFieldDecl.AddModifiers(SyntaxFactory.Token(SyntaxKind.NewKeyword));
305243
//fieldDeclaration2 = fieldDeclaration2.AddModifiers(SyntaxFactory.Token(SyntaxKind.NewKeyword));
@@ -312,6 +250,11 @@ private static TypeSyntax MakeProperType(REFrameworkNET.TypeDefinition? targetTy
312250
//typeDeclaration = typeDeclaration.AddMembers(refProxyFieldDecl);
313251
}
314252

253+
// Logically needs to come after the REFType field is added as they reference it
254+
if (internalFieldDeclarations.Count > 0 && typeDeclaration != null) {
255+
typeDeclaration = typeDeclaration.AddMembers(internalFieldDeclarations.ToArray());
256+
}
257+
315258
return GenerateNestedTypes();
316259
}
317260

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

327270
HashSet<string> seenMethodSignatures = [];
328271

329-
var validMethods = new List<REFrameworkNET.Method>();
272+
List<REFrameworkNET.Method> validMethods = [];
330273

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

322+
bool anyOutParams = false;
323+
379324
if (method.Parameters.Count > 0) {
380325
// If any of the params have ! in them, skip this method
381326
if (method.Parameters.Any(param => param != null && (param.Type == null || (param.Type != null && param.Type.FullName.Contains('!'))))) {
@@ -395,6 +340,7 @@ private TypeDeclarationSyntax GenerateMethods(List<SimpleBaseTypeSyntax> baseTyp
395340

396341
bool anyUnsafeParams = false;
397342

343+
398344
if (runtimeParams != null) {
399345
foreach (dynamic param in runtimeParams) {
400346
if (param.get_IsRetval() == true) {
@@ -430,6 +376,7 @@ private TypeDeclarationSyntax GenerateMethods(List<SimpleBaseTypeSyntax> baseTyp
430376
if (isOut == true) {
431377
simpleMethodSignature += "out";
432378
modifiers.Add(SyntaxFactory.Token(SyntaxKind.OutKeyword));
379+
anyOutParams = true;
433380
}
434381

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

463410
if (method.IsStatic()) {
464-
// Add System.ComponentModel.Description("static") attribute
465-
//methodDeclaration = methodDeclaration.AddAttributeLists(SyntaxFactory.AttributeList().AddAttributes(SyntaxFactory.Attribute(SyntaxFactory.ParseName("global::System.ComponentModel.DescriptionAttribute"), SyntaxFactory.ParseAttributeArgumentList("(\"static\")"))));
466-
467411
// lets see what happens if we just make it static
468-
/*methodDeclaration = methodDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.StaticKeyword));
412+
methodDeclaration = methodDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.StaticKeyword));
469413

470414
// Now we must add a body to it that actually calls the method
471415
// We have our REFType field, so we can lookup the method and call it
472416
// Make a private static field to hold the REFrameworkNET.Method
473-
var internalFieldName = "INTERNAL_" + method.GetIndex().ToString();
417+
var internalFieldName = "INTERNAL_" + method.Name + method.GetIndex().ToString();
474418
var methodVariableDeclaration = SyntaxFactory.VariableDeclaration(SyntaxFactory.ParseTypeName("global::REFrameworkNET.Method"))
475-
.AddVariables(SyntaxFactory.VariableDeclarator(internalFieldName).WithInitializer(SyntaxFactory.EqualsValueClause(SyntaxFactory.ParseExpression("REFType.GetMethod(\"" + method.Name + "\")"))));
476-
419+
.AddVariables(SyntaxFactory.VariableDeclarator(internalFieldName).WithInitializer(SyntaxFactory.EqualsValueClause(SyntaxFactory.ParseExpression("REFType.GetMethod(\"" + method.GetMethodSignature() + "\")"))));
420+
477421
var methodFieldDeclaration = SyntaxFactory.FieldDeclaration(methodVariableDeclaration).AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword), SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword));
478422
internalFieldDeclarations.Add(methodFieldDeclaration);
479423

480-
// Now we can add the body
481-
// bool HandleInvokeMember_Internal(System::Object^ obj, array<System::Object^>^ args, System::Object^% result);
424+
List<StatementSyntax> bodyStatements = [];
425+
482426
if (method.ReturnType.FullName == "System.Void") {
483-
var body = internalFieldName + ".Invoke(null, null)";
484-
methodDeclaration = methodDeclaration.AddBodyStatements(SyntaxFactory.ParseStatement(body));
427+
if (method.Parameters.Count == 0) {
428+
bodyStatements.Add(SyntaxFactory.ParseStatement(internalFieldName + ".Invoke(null, null);"));
429+
} else if (!anyOutParams) {
430+
bodyStatements.Add(SyntaxFactory.ParseStatement(internalFieldName + ".Invoke(null, new object[] {" + string.Join(", ", method.Parameters.Select(param => param.Name)) + "});"));
431+
} else {
432+
bodyStatements.Add(SyntaxFactory.ParseStatement("throw new System.NotImplementedException();")); // TODO: Implement this
433+
}
485434
} else {
486-
var body1 = "object INTERNAL_result = null;";
487-
var body2 = internalFieldName + ".HandleInvokeMember_Internal(null, " + (method.Parameters.Count > 0 ? "new object[] {" + string.Join(", ", method.Parameters.Select(param => param.Name)) + "}" : "null") + ", ref INTERNAL_result);";
488-
string body3 = "return (" + returnType.GetText() + ")INTERNAL_result;";
435+
if (method.Parameters.Count == 0) {
436+
bodyStatements.Add(SyntaxFactory.ParseStatement("return (" + returnType.GetText().ToString() + ")" + internalFieldName + ".InvokeBoxed(typeof(" + returnType.GetText().ToString() + "), null, null);"));
437+
} else if (!anyOutParams) {
438+
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)) + "});"));
439+
} else {
440+
bodyStatements.Add(SyntaxFactory.ParseStatement("throw new System.NotImplementedException();")); // TODO: Implement this
441+
}
442+
}
489443

490-
methodDeclaration = methodDeclaration.AddBodyStatements(
491-
[SyntaxFactory.ParseStatement(body1), SyntaxFactory.ParseStatement(body2), SyntaxFactory.ParseStatement(body3)]
492-
);
493-
}*/
444+
methodDeclaration = methodDeclaration.AddBodyStatements(
445+
[.. bodyStatements]
446+
);
494447
}
495448

496449
if (seenMethodSignatures.Contains(simpleMethodSignature)) {

csharp-api/REFrameworkNET/Method.cpp

+80
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
#include "API.hpp"
1111
#include "VM.hpp"
1212
#include "SystemString.hpp"
13+
#include "Proxy.hpp"
1314

1415
#include "Utility.hpp"
1516

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

136+
public interface class DummyInterface {
137+
138+
};
139+
140+
System::Object^ Method::InvokeBoxed(System::Type^ targetReturnType, System::Object^ obj, array<System::Object^>^ args) {
141+
System::Object^ result = nullptr;
142+
this->HandleInvokeMember_Internal(obj, args, result);
143+
144+
if (result == nullptr) {
145+
return nullptr; // ez
146+
}
147+
148+
if (targetReturnType == nullptr) {
149+
return result;
150+
}
151+
152+
if (!targetReturnType->IsPrimitive && !targetReturnType->IsEnum && !targetReturnType->IsInterface) {
153+
if (targetReturnType == String::typeid) {
154+
return result;
155+
}
156+
157+
if (targetReturnType == REFrameworkNET::ManagedObject::typeid) {
158+
return result;
159+
}
160+
161+
if (targetReturnType == REFrameworkNET::NativeObject::typeid) {
162+
return result;
163+
}
164+
165+
if (targetReturnType == REFrameworkNET::TypeDefinition::typeid) {
166+
return result;
167+
}
168+
169+
if (targetReturnType == REFrameworkNET::Method::typeid) {
170+
return result;
171+
}
172+
173+
if (targetReturnType == REFrameworkNET::Field::typeid) {
174+
return result;
175+
}
176+
}
177+
178+
if (targetReturnType->IsEnum) {
179+
auto underlyingType = targetReturnType->GetEnumUnderlyingType();
180+
181+
if (underlyingType != nullptr) {
182+
auto underlyingResult = Convert::ChangeType(result, underlyingType);
183+
return Enum::ToObject(targetReturnType, underlyingResult);
184+
}
185+
}
186+
187+
if (this->DeclaringType == nullptr) {
188+
return result;
189+
}
190+
191+
if (!targetReturnType->IsPrimitive && targetReturnType->IsInterface) {
192+
auto iobjectResult = dynamic_cast<REFrameworkNET::IObject^>(result);
193+
194+
if (iobjectResult != nullptr && targetReturnType->IsInterface) {
195+
// Caching mechanism to prevent creating multiple proxies for the same object and type so we dont stress the GC
196+
if (auto existingProxy = iobjectResult->GetProxy(targetReturnType); existingProxy != nullptr) {
197+
return existingProxy;
198+
}
199+
200+
auto proxy = System::Reflection::DispatchProxy::Create(targetReturnType, Proxy<DummyInterface^, IObject^>::typeid->GetGenericTypeDefinition()->MakeGenericType(targetReturnType, result->GetType()));
201+
((IProxy^)proxy)->SetInstance(iobjectResult);
202+
203+
if (auto unified = dynamic_cast<REFrameworkNET::UnifiedObject^>(iobjectResult); unified != nullptr) {
204+
unified->AddProxy(targetReturnType, (IProxy^)proxy);
205+
}
206+
207+
result = proxy;
208+
return result;
209+
}
210+
}
211+
212+
return result;
213+
}
214+
135215
::reframework::InvokeRet Method::Invoke_Internal(System::Object^ obj, array<System::Object^>^ args) {
136216
if (obj == nullptr && !this->IsStatic()) {
137217
System::String^ declaringName = this->GetDeclaringType() != nullptr ? this->GetDeclaringType()->GetFullName() : gcnew System::String("UnknownType");

csharp-api/REFrameworkNET/Method.hpp

+2
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,8 @@ public ref class Method : public System::IEquatable<Method^>
6161
/// </remarks>
6262
REFrameworkNET::InvokeRet Invoke(System::Object^ obj, array<System::Object^>^ args);
6363

64+
System::Object^ InvokeBoxed(System::Type^ targetReturnType, System::Object^ obj, array<System::Object^>^ args);
65+
6466
private:
6567
::reframework::InvokeRet Invoke_Internal(System::Object^ obj, array<System::Object^>^ args);
6668

csharp-api/REFrameworkNET/Proxy.hpp

+7-69
Original file line numberDiff line numberDiff line change
@@ -154,86 +154,24 @@ public ref class Proxy : public Reflection::DispatchProxy, public IProxy, public
154154
if (methodAttribute != nullptr) {
155155
if (iobject != nullptr) {
156156
auto method = methodAttribute->GetMethod(iobject->GetTypeDefinition());
157-
iobject->HandleInvokeMember_Internal(method, args, result);
157+
return method->InvokeBoxed(targetMethod->ReturnType, iobject, args);
158158
} else {
159159
throw gcnew System::InvalidOperationException("Proxy: T2 must be IObject derived");
160160
}
161161
} else {
162162
// This is a fallback
163163
if (iobject != nullptr) {
164-
iobject->HandleInvokeMember_Internal(targetMethod->Name, args, result);
165-
} else {
166-
throw gcnew System::InvalidOperationException("Proxy: T2 must be IObject derived");
167-
}
168-
}
169-
170-
auto targetReturnType = targetMethod->ReturnType;
171-
172-
if (targetReturnType == nullptr) {
173-
return result;
174-
}
175-
176-
if (!targetReturnType->IsPrimitive && !targetReturnType->IsEnum) {
177-
if (targetReturnType == String::typeid) {
178-
return result;
179-
}
180-
181-
if (targetReturnType == REFrameworkNET::ManagedObject::typeid) {
182-
return result;
183-
}
184-
185-
if (targetReturnType == REFrameworkNET::NativeObject::typeid) {
186-
return result;
187-
}
188-
189-
if (targetReturnType == REFrameworkNET::TypeDefinition::typeid) {
190-
return result;
191-
}
192-
193-
if (targetReturnType == REFrameworkNET::Method::typeid) {
194-
return result;
195-
}
164+
auto method = iobject->GetTypeDefinition()->GetMethod(targetMethod->Name);
196165

197-
if (targetReturnType == REFrameworkNET::Field::typeid) {
198-
return result;
199-
}
200-
}
201-
202-
if (targetReturnType->IsEnum) {
203-
auto underlyingType = targetReturnType->GetEnumUnderlyingType();
204-
205-
if (underlyingType != nullptr) {
206-
auto underlyingResult = Convert::ChangeType(result, underlyingType);
207-
return Enum::ToObject(targetReturnType, underlyingResult);
208-
}
209-
}
210-
211-
if (targetMethod->DeclaringType == nullptr) {
212-
return result;
213-
}
214-
215-
if (!targetReturnType->IsPrimitive && targetMethod->DeclaringType->IsInterface && result != nullptr) {
216-
auto iobjectResult = dynamic_cast<REFrameworkNET::IObject^>(result);
217-
218-
if (iobjectResult != nullptr && targetReturnType->IsInterface) {
219-
// Caching mechanism to prevent creating multiple proxies for the same object and type so we dont stress the GC
220-
if (auto existingProxy = iobjectResult->GetProxy(targetReturnType); existingProxy != nullptr) {
221-
return existingProxy;
222-
}
223-
224-
auto proxy = DispatchProxy::Create(targetReturnType, Proxy<T, T2>::typeid->GetGenericTypeDefinition()->MakeGenericType(T::typeid, result->GetType()));
225-
((IProxy^)proxy)->SetInstance(iobjectResult);
226-
227-
if (auto unified = dynamic_cast<REFrameworkNET::UnifiedObject^>(iobjectResult); unified != nullptr) {
228-
unified->AddProxy(targetReturnType, (IProxy^)proxy);
166+
if (method != nullptr) {
167+
return method->InvokeBoxed(targetMethod->ReturnType, iobject, args);
229168
}
230-
231-
result = proxy;
232-
return result;
169+
} else {
170+
throw gcnew System::InvalidOperationException("Proxy: T2 must be IObject derived");
233171
}
234172
}
235173

236-
return result;
174+
return nullptr;
237175
}
238176

239177
private:

0 commit comments

Comments
 (0)