Skip to content

Commit b40e499

Browse files
committed
Handle generics through type name regeneration
1 parent 00d315f commit b40e499

File tree

2 files changed

+118
-91
lines changed

2 files changed

+118
-91
lines changed

csharp-api/AssemblyGenerator/ClassGenerator.cs

Lines changed: 111 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
using System.ComponentModel.DataAnnotations;
1717

1818
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
19+
using REFrameworkNET.Attributes;
1920

2021
public class ClassGenerator {
2122
public class PseudoProperty {
@@ -209,16 +210,24 @@ private void Generate() {
209210
var refProxyFieldDecl = SyntaxFactory.FieldDeclaration(refProxyVarDecl).AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword), SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword));
210211

211212

213+
// var typeIdExpr = GenericDictExpr((t) => t.Index);
214+
var refTypeName = (FieldDeclarationSyntax)ParseMemberDeclaration($"public static readonly string REFTypeName = {GenericTypeNameExpr()};")!;
215+
if (baseTypes.Length > 0)
216+
refTypeName = refTypeName.AddModifiers(Token(SyntaxKind.NewKeyword));
217+
typeDeclaration = typeDeclaration.AddMembers(refTypeName);
218+
212219
// Add a static field to the class that holds the REFrameworkNET.TypeDefinition
213220
var refTypeFieldDecl = ParseMemberDeclaration(
214-
$"public static readonly global::REFrameworkNET.TypeDefinition REFType = global::REFrameworkNET.TDB.Get().FindType(\"{t.FullName}\");"
221+
$"public static readonly global::REFrameworkNET.TypeDefinition REFType = global::REFrameworkNET.TDB.Get().FindType(REFTypeName);"
215222
)!;
216223
if (baseTypes.Length > 0) {
217224
refTypeFieldDecl = refTypeFieldDecl.AddModifiers(SyntaxFactory.Token(SyntaxKind.NewKeyword));
218225
}
219226
typeDeclaration = typeDeclaration.AddMembers(refTypeFieldDecl);
220227
//typeDeclaration = typeDeclaration.AddMembers(refProxyFieldDecl);
221228

229+
230+
222231
GenerateMethods();
223232
GenerateFields();
224233
GenerateProperties();
@@ -251,6 +260,38 @@ var t when t.IsEquivalentTo(TypeHandler.VoidType()) =>
251260
return ParseStatement(stmt);
252261
}
253262

263+
// This is a fun one
264+
private string GenericTypeNameExpr() {
265+
if (!generic)
266+
return $"\"{t.FullName}\"";
267+
if (t.FullName == "!0[]")
268+
return $"(string)typeof(T).GetField(\"REFTypeName\").GetValue(null) + \"[]\"";
269+
var hierarchy = TypeHandler.NameHierarchy(t);
270+
int genericCount = 0;
271+
var expr = "\"\"";
272+
bool dot = false;
273+
foreach (var elem in hierarchy) {
274+
if (dot) expr += "+ \".\"";
275+
dot = true;
276+
277+
var (name, count) = TypeHandler.BaseTypeName(elem);
278+
if (count == 0) {
279+
expr += $" + \"{name}\"";
280+
}
281+
282+
if (count != 0) {
283+
expr += $"+ \"{elem}<\"";
284+
for (int i = 0; i < count; ++i) {
285+
if (i > 0) expr += "+ \",\"";
286+
var genericParamName = GenericNames[genericCount++];
287+
expr += $"+ (string) typeof({genericParamName}).GetField(\"REFTypeName\").GetValue(null)";
288+
}
289+
expr += $"+ \">\"";
290+
}
291+
}
292+
return expr;
293+
}
294+
254295
private void GenerateProperties() {
255296
if (pseudoProperties.Count == 0) {
256297
return;
@@ -285,10 +326,12 @@ private void GenerateProperties() {
285326
SyntaxFactory.ParseName("global::REFrameworkNET.Attributes.Method"),
286327
SyntaxFactory.ParseAttributeArgumentList("(" + property.Value.getter.Index.ToString() + ", global::REFrameworkNET.FieldFacadeType.None)"))
287328
));
288-
329+
289330
if (property.Value.getter.IsStatic()) {
290331
shouldAddStaticKeyword = true;
332+
}
291333

334+
if (generic | property.Value.getter.IsStatic()) {
292335
// Now we must add a body to it that actually calls the method
293336
// We have our REFType field, so we can lookup the method and call it
294337
// Make a private static field to hold the REFrameworkNET.Method
@@ -299,15 +342,9 @@ private void GenerateProperties() {
299342
var methodFieldDeclaration = SyntaxFactory.FieldDeclaration(methodVariableDeclaration).AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword), SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword));
300343
internalFieldDeclarations.Add(methodFieldDeclaration);
301344

302-
List<StatementSyntax> bodyStatements = [];
303-
bodyStatements.Add(SyntaxFactory.ParseStatement("return (" + propertyType.GetText().ToString() + ")" + internalFieldName + ".InvokeBoxed(typeof(" + propertyType.GetText().ToString() + "), null, null);"));
304-
305-
getter = getter.AddBodyStatements(bodyStatements.ToArray());
306-
} else if (generic) {
307-
var index = t.Methods.IndexOf(property.Value.getter);
308-
getter = getter
309-
.AddBodyStatements(GenericStub(propertyType, [], index))
310-
.WithAttributeLists([]);
345+
var instance = property.Value.getter.IsStatic() ? "null" : "this";
346+
var stmt = $"return ({propertyType.ToFullString()}) {internalFieldName}.InvokeBoxed(typeof({propertyType.ToFullString()}), {instance}, null);";
347+
getter = getter.AddBodyStatements(ParseStatement(stmt));
311348
} else {
312349
getter = getter.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken));
313350
}
@@ -327,9 +364,11 @@ private void GenerateProperties() {
327364
SyntaxFactory.ParseName("global::REFrameworkNET.Attributes.Method"),
328365
SyntaxFactory.ParseAttributeArgumentList("(" + property.Value.setter.Index.ToString() + ", global::REFrameworkNET.FieldFacadeType.None)"))
329366
));
330-
367+
331368
if (property.Value.setter.IsStatic()) {
332369
shouldAddStaticKeyword = true;
370+
}
371+
if (generic | property.Value.setter.IsStatic()) {
333372

334373
// Now we must add a body to it that actually calls the method
335374
// We have our REFType field, so we can lookup the method and call it
@@ -341,15 +380,9 @@ private void GenerateProperties() {
341380
var methodFieldDeclaration = SyntaxFactory.FieldDeclaration(methodVariableDeclaration).AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword), SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword));
342381
internalFieldDeclarations.Add(methodFieldDeclaration);
343382

344-
List<StatementSyntax> bodyStatements = [];
345-
bodyStatements.Add(SyntaxFactory.ParseStatement(internalFieldName + ".Invoke(null, new object[] {value});"));
346-
347-
setter = setter.AddBodyStatements(bodyStatements.ToArray());
348-
} else if (generic) {
349-
var index = t.Methods.IndexOf(property.Value.setter);
350-
setter = setter
351-
.AddBodyStatements(GenericStub(null, [], index))
352-
.WithAttributeLists([]);
383+
var instance = property.Value.setter.IsStatic() ? "null" : "this";
384+
var stmt = $"{internalFieldName}.Invoke({instance}, [value]);";
385+
setter = setter.AddBodyStatements(ParseStatement(stmt));
353386
} else {
354387
setter = setter.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken));
355388
}
@@ -455,8 +488,9 @@ private void GenerateFields() {
455488
var propertyDeclaration = SyntaxFactory.PropertyDeclaration(fieldType, fieldName)
456489
.AddModifiers([SyntaxFactory.Token(SyntaxKind.PublicKeyword)]);
457490

458-
if (field.IsStatic()) {
459-
propertyDeclaration = propertyDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.StaticKeyword));
491+
if (field.IsStatic() || generic) {
492+
if (field.IsStatic())
493+
propertyDeclaration = propertyDeclaration.AddModifiers(Token(SyntaxKind.StaticKeyword));
460494

461495
// Now we must add a body to it that actually calls the method
462496
// We have our REFType field, so we can lookup the method and call it
@@ -471,19 +505,15 @@ private void GenerateFields() {
471505
List<StatementSyntax> bodyStatementsSetter = [];
472506
List<StatementSyntax> bodyStatementsGetter = [];
473507

508+
var instance = field.IsStatic()
509+
? "0"
510+
: "(this as REFrameworkNET.IObject).GetAddress()";
474511

475-
bodyStatementsGetter.Add(SyntaxFactory.ParseStatement("return (" + fieldType.GetText().ToString() + ")" + internalFieldName + ".GetDataBoxed(typeof(" + fieldType.GetText().ToString() + "), 0, false);"));
476-
bodyStatementsSetter.Add(SyntaxFactory.ParseStatement(internalFieldName + ".SetDataBoxed(0, new object[] {value}, false);"));
512+
var getterStatement = ParseStatement(@$" return ({fieldType.ToFullString()}) {internalFieldName} .GetDataBoxed(typeof({fieldType.ToFullString()}), {instance}, false);");
513+
var setterStatement = ParseStatement($"{internalFieldName}.SetDataBoxed({instance}, new object[] {{value}}, false);");
477514

478-
getter = getter.AddBodyStatements(bodyStatementsGetter.ToArray());
479-
setter = setter.AddBodyStatements(bodyStatementsSetter.ToArray());
480-
} else if (generic) {
481-
getter = getter
482-
.AddBodyStatements(ParseStatement("throw new System.NotImplementedException();"))
483-
.WithAttributeLists([]);
484-
setter = setter
485-
.AddBodyStatements(ParseStatement("throw new System.NotImplementedException();"))
486-
.WithAttributeLists([]);
515+
getter = getter.AddBodyStatements(getterStatement);
516+
setter = setter.AddBodyStatements(setterStatement);
487517
} else {
488518
getter = getter.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken));
489519
setter = setter.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken));
@@ -609,7 +639,7 @@ private void GenerateMethods() {
609639
);
610640

611641
bool anyOutParams = false;
612-
System.Collections.Generic.List<string> paramNames = [];
642+
List<string> paramNames = [];
613643

614644
if (method.Parameters.Count > 0) {
615645
var runtimeMethod = method.GetRuntimeMethod();
@@ -710,48 +740,46 @@ private void GenerateMethods() {
710740
simpleMethodSignature += "()";
711741
}
712742

713-
if (method.IsStatic()) {
743+
if (method.IsStatic() || generic) {
744+
714745
// lets see what happens if we just make it static
715-
methodDeclaration = methodDeclaration.AddModifiers(SyntaxFactory.Token(SyntaxKind.StaticKeyword));
746+
if (method.IsStatic())
747+
methodDeclaration = methodDeclaration.AddModifiers(Token(SyntaxKind.StaticKeyword));
716748

717749
// Now we must add a body to it that actually calls the method
718750
// We have our REFType field, so we can lookup the method and call it
719751
// Make a private static field to hold the REFrameworkNET.Method
752+
var index = t.Methods.IndexOf(method);
720753
var internalFieldName = "INTERNAL_" + method.Name + method.GetIndex().ToString();
721-
var methodVariableDeclaration = SyntaxFactory.VariableDeclaration(SyntaxFactory.ParseTypeName("global::REFrameworkNET.Method"))
722-
.AddVariables(SyntaxFactory.VariableDeclarator(internalFieldName).WithInitializer(SyntaxFactory.EqualsValueClause(SyntaxFactory.ParseExpression("REFType.GetMethod(\"" + method.GetMethodSignature() + "\")"))));
723-
724-
var methodFieldDeclaration = SyntaxFactory.FieldDeclaration(methodVariableDeclaration).AddModifiers(SyntaxFactory.Token(SyntaxKind.PrivateKeyword), SyntaxFactory.Token(SyntaxKind.StaticKeyword), SyntaxFactory.Token(SyntaxKind.ReadOnlyKeyword));
754+
internalFieldName = internalFieldName.Replace(".", "_");
755+
var methodVariableDeclaration = VariableDeclaration(
756+
ParseTypeName("global::REFrameworkNET.Method"))
757+
.AddVariables(VariableDeclarator(internalFieldName)
758+
.WithInitializer(
759+
EqualsValueClause(ParseExpression($"REFType.GetMethods()[{index}]"))));
760+
761+
var methodFieldDeclaration = FieldDeclaration(methodVariableDeclaration)
762+
.AddModifiers(
763+
Token(SyntaxKind.PrivateKeyword),
764+
Token(SyntaxKind.StaticKeyword),
765+
Token(SyntaxKind.ReadOnlyKeyword));
725766
internalFieldDeclarations.Add(methodFieldDeclaration);
726767

727768
List<StatementSyntax> bodyStatements = [];
728769

729-
if (method.ReturnType.FullName == "System.Void") {
730-
if (method.Parameters.Count == 0) {
731-
bodyStatements.Add(SyntaxFactory.ParseStatement(internalFieldName + ".Invoke(null, null);"));
732-
} else if (!anyOutParams) {
733-
bodyStatements.Add(SyntaxFactory.ParseStatement(internalFieldName + ".Invoke(null, new object[] {" + string.Join(", ", paramNames) + "});"));
734-
} else {
735-
bodyStatements.Add(SyntaxFactory.ParseStatement("throw new System.NotImplementedException();")); // TODO: Implement this
736-
}
737-
} else {
738-
if (method.Parameters.Count == 0) {
739-
bodyStatements.Add(SyntaxFactory.ParseStatement("return (" + returnType.GetText().ToString() + ")" + internalFieldName + ".InvokeBoxed(typeof(" + returnType.GetText().ToString() + "), null, null);"));
740-
} else if (!anyOutParams) {
741-
bodyStatements.Add(SyntaxFactory.ParseStatement("return (" + returnType.GetText().ToString() + ")" + internalFieldName + ".InvokeBoxed(typeof(" + returnType.GetText().ToString() + "), null, new object[] {" + string.Join(", ", paramNames) + "});"));
742-
} else {
743-
bodyStatements.Add(SyntaxFactory.ParseStatement("throw new System.NotImplementedException();")); // TODO: Implement this
744-
}
770+
var instance = "this";
771+
if (method.IsStatic()) {
772+
instance = "null";
745773
}
746774

747-
methodDeclaration = methodDeclaration.AddBodyStatements(
748-
[.. bodyStatements]
749-
);
750-
} else if (generic) {
751-
var index = t.Methods.IndexOf(method);
752-
methodDeclaration = methodDeclaration
753-
.AddBodyStatements(GenericStub(returnType, [.. paramNames], index))
754-
.WithAttributeLists([]);
775+
var statement = (method.ReturnType.FullName, method.Parameters) switch {
776+
_ when anyOutParams => "throw new System.NotImplementedException();",
777+
("System.Void", []) => $"{internalFieldName}.Invoke({instance}, null);",
778+
("System.Void", [..]) => $"{internalFieldName}.Invoke({instance}, [{string.Join(",", paramNames)}]);",
779+
(_, []) => $"return ({returnType.ToFullString()}) {internalFieldName}.InvokeBoxed(typeof({returnType.ToFullString()}), {instance}, null);",
780+
(_, [..]) => $"return ({returnType.ToFullString()}) {internalFieldName}.InvokeBoxed(typeof({returnType.ToFullString()}), {instance}, [{string.Join(",", paramNames)}]);"
781+
};
782+
methodDeclaration = methodDeclaration.AddBodyStatements(ParseStatement(statement));
755783
} else {
756784
methodDeclaration = methodDeclaration.WithSemicolonToken(Token(SyntaxKind.SemicolonToken));
757785
}
@@ -863,6 +891,22 @@ public static TypeSyntax ProperType(TypeDefinition type) {
863891
return ObjType();
864892
}
865893

894+
public static string[] NameHierarchy(TypeDefinition type) {
895+
var typeList = new List<string>();
896+
while (true) {
897+
typeList.Insert(0, type.Name);
898+
if (type.DeclaringType is null || type.DeclaringType == type)
899+
break;
900+
type = type.DeclaringType;
901+
}
902+
if (type.Namespace is not null && type.Namespace.Any()) {
903+
typeList.Insert(0, type.Namespace);
904+
} else {
905+
typeList.Insert(0, "_");
906+
}
907+
return [.. typeList];
908+
}
909+
866910
static TypeSyntax BuildProperType(REFrameworkNET.TypeDefinition? targetType) {
867911

868912
if (targetType is null) return VoidType();
@@ -886,22 +930,7 @@ static TypeSyntax BuildProperType(REFrameworkNET.TypeDefinition? targetType) {
886930
}
887931
Cache[targetType.Index] = ObjType();
888932

889-
var typeList = new List<string>();
890-
{
891-
var type = targetType!;
892-
while (true) {
893-
typeList.Insert(0, type.Name ?? "UNKN");
894-
if (type.DeclaringType is null || type.DeclaringType == type)
895-
break;
896-
type = type.DeclaringType;
897-
}
898-
if (type.Namespace is not null && type.Namespace.Any()) {
899-
typeList.Insert(0, type.Namespace);
900-
} else {
901-
typeList.Insert(0, "_");
902-
}
903-
}
904-
933+
var typeList = NameHierarchy(targetType);
905934
int genericIndex = 0;
906935
var generics = targetType.GenericArguments ?? [];
907936
var toParse = string.Join(".", typeList.Select(tName => {
@@ -933,7 +962,7 @@ public static void BuildProperTypes() {
933962
BuildProperType(type);
934963
// Special case delegates again
935964
if (!(type.FullName.StartsWith("System.Action") || type.FullName.StartsWith("System.Func"))) {
936-
if (type.IsGenericType()) BuildProperType(type.GetGenericTypeDefinition());
965+
if (type.IsGenericType()) BuildProperType(type.GetGenericTypeDefinition());
937966
}
938967
}
939968
API.LogInfo("Built proper types");
@@ -965,4 +994,3 @@ public static MemberDeclarationSyntax[] GenerateNestedTypes(TypeDefinition t) {
965994
.ToArray() ?? [];
966995
}
967996
}
968-

0 commit comments

Comments
 (0)