BITFALL/Assets/Plugins/FastScriptReload/Scripts/Editor/Compilation/CodeRewriting/CreateNewFieldInitMethodRew...

150 lines
7.3 KiB
C#
Raw Normal View History

2023-06-08 14:09:50 +08:00
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using ImmersiveVrToolsCommon.Runtime.Logging;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace FastScriptReload.Editor.Compilation.CodeRewriting
{
class CreateNewFieldInitMethodRewriter: FastScriptReloadCodeRewriterBase {
private readonly Dictionary<string, List<string>> _typeToNewFieldDeclarations;
private static readonly string NewFieldsToCreateValueFnDictionaryFieldName = "__Patched_NewFieldNameToInitialValueFn";
private static readonly string NewFieldsToGetTypeFnDictionaryFieldName = "__Patched_NewFieldsToGetTypeFnDictionaryFieldName";
private static readonly string DictionaryFullNamespaceTypeName = "System.Collections.Generic.Dictionary";
public static Dictionary<string, Func<object>> ResolveNewFieldsToCreateValueFn(Type forType)
{
return (Dictionary<string, Func<object>>) forType.GetField(NewFieldsToCreateValueFnDictionaryFieldName, BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);
}
public static Dictionary<string, Func<object>> ResolveNewFieldsToTypeFn(Type forType)
{
return (Dictionary<string, Func<object>>) forType.GetField(NewFieldsToGetTypeFnDictionaryFieldName, BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);
}
public CreateNewFieldInitMethodRewriter(Dictionary<string, List<string>> typeToNewFieldDeclarations, bool writeRewriteReasonAsComment)
:base(writeRewriteReasonAsComment)
{
_typeToNewFieldDeclarations = typeToNewFieldDeclarations;
}
public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node)
{
var fullClassName = RoslynUtils.GetMemberFQDN(node, node.Identifier.ToString());
if (!_typeToNewFieldDeclarations.TryGetValue(fullClassName, out var newClassFields))
{
LoggerScoped.LogWarning($"Unable to find new-fields for type: {fullClassName}, this is not an issue if there are no new fields for that type.");
}
Func<FieldDeclarationSyntax, ExpressionSyntax> getObjectFnSyntax = fieldDeclarationNode => fieldDeclarationNode.Declaration.Variables[0].Initializer?.Value //value captured from initializer
?? SyntaxFactory.DefaultExpression(SyntaxFactory.IdentifierName(fieldDeclarationNode.Declaration.Type.ToString()));
var withDictionaryFieldNameToInitFieldValue = CreateNewFieldNameToGetObjectFnDictionary(node, newClassFields, getObjectFnSyntax, NewFieldsToCreateValueFnDictionaryFieldName);
Func<FieldDeclarationSyntax, ExpressionSyntax> getObjectTypeFnSyntax = fieldDeclarationNode => SyntaxFactory.TypeOfExpression(fieldDeclarationNode.Declaration.Type);
return CreateNewFieldNameToGetObjectFnDictionary(withDictionaryFieldNameToInitFieldValue, newClassFields, getObjectTypeFnSyntax, NewFieldsToGetTypeFnDictionaryFieldName);
}
private ClassDeclarationSyntax CreateNewFieldNameToGetObjectFnDictionary(ClassDeclarationSyntax node,
List<string> newClassFields, Func<FieldDeclarationSyntax, ExpressionSyntax> getObjectFnSyntax, string dictionaryFieldName)
{
var dictionaryKeyToInitValueNodes = newClassFields.SelectMany(fieldName =>
{
var fieldDeclarationNode = node.ChildNodes().OfType<FieldDeclarationSyntax>()
.Single(f => f.Declaration.Variables.First().Identifier.ToString() == fieldName);
return new[]
{
(SyntaxNodeOrToken)SyntaxFactory.AssignmentExpression(
SyntaxKind.SimpleAssignmentExpression,
SyntaxFactory.ImplicitElementAccess()
.WithArgumentList(
SyntaxFactory.BracketedArgumentList(
SyntaxFactory.SingletonSeparatedList<ArgumentSyntax>(
SyntaxFactory.Argument(
SyntaxFactory.LiteralExpression(
SyntaxKind.StringLiteralExpression,
SyntaxFactory.Literal(fieldDeclarationNode.Declaration.Variables.First()
.Identifier.ToString())))))), //variable name
SyntaxFactory.ParenthesizedLambdaExpression(getObjectFnSyntax(fieldDeclarationNode))),
SyntaxFactory.Token(SyntaxKind.CommaToken) //comma, add for all
};
}).ToArray();
var dictionaryInitializer =
SyntaxFactory.InitializerExpression(
SyntaxKind.ObjectInitializerExpression,
SyntaxFactory.SeparatedList<ExpressionSyntax>(
dictionaryKeyToInitValueNodes.ToArray()
));
var withDictionaryFieldNameToInitFieldValue = node.AddMembers(
SyntaxFactory.FieldDeclaration(
SyntaxFactory.VariableDeclaration(
SyntaxFactory.GenericName(
SyntaxFactory.Identifier(DictionaryFullNamespaceTypeName))
.WithTypeArgumentList(
SyntaxFactory.TypeArgumentList(
SyntaxFactory.SeparatedList<TypeSyntax>(
new SyntaxNodeOrToken[]
{
SyntaxFactory.PredefinedType(
SyntaxFactory.Token(SyntaxKind.StringKeyword)),
SyntaxFactory.Token(SyntaxKind.CommaToken),
SyntaxFactory.GenericName(
SyntaxFactory.Identifier("System.Func"))
.WithTypeArgumentList(
SyntaxFactory.TypeArgumentList(
SyntaxFactory.SingletonSeparatedList<TypeSyntax>(
SyntaxFactory.PredefinedType(
SyntaxFactory.Token(SyntaxKind.ObjectKeyword)))))
}))))
.WithVariables(
SyntaxFactory.SingletonSeparatedList<VariableDeclaratorSyntax>(
SyntaxFactory.VariableDeclarator(
SyntaxFactory.Identifier(dictionaryFieldName))
.WithInitializer(
SyntaxFactory.EqualsValueClause(
SyntaxFactory.ObjectCreationExpression(
SyntaxFactory.GenericName(
SyntaxFactory.Identifier(DictionaryFullNamespaceTypeName))
.WithTypeArgumentList(
SyntaxFactory.TypeArgumentList(
SyntaxFactory.SeparatedList<TypeSyntax>(
new SyntaxNodeOrToken[]
{
SyntaxFactory.PredefinedType(
SyntaxFactory.Token(SyntaxKind.StringKeyword)),
SyntaxFactory.Token(SyntaxKind.CommaToken),
SyntaxFactory.QualifiedName(
SyntaxFactory.IdentifierName("System"),
SyntaxFactory.GenericName(
SyntaxFactory.Identifier("Func"))
.WithTypeArgumentList(
SyntaxFactory.TypeArgumentList(
SyntaxFactory
.SingletonSeparatedList<
TypeSyntax>(
SyntaxFactory
.PredefinedType(
SyntaxFactory.Token(
SyntaxKind
.ObjectKeyword))))))
}))))
.WithInitializer(dictionaryInitializer))))))
.WithTriviaFrom(node)
.WithModifiers(
SyntaxFactory.TokenList(
SyntaxFactory.Token(SyntaxKind.PrivateKeyword),
SyntaxFactory.Token(SyntaxKind.StaticKeyword)))
.NormalizeWhitespace()
.WithLeadingTrivia(SyntaxFactory.TriviaList(SyntaxFactory.ElasticCarriageReturnLineFeed))
.WithTrailingTrivia(SyntaxFactory.TriviaList(SyntaxFactory.ElasticCarriageReturnLineFeed, SyntaxFactory.ElasticCarriageReturnLineFeed))
);
return AddRewriteCommentIfNeeded(withDictionaryFieldNameToInitFieldValue, $"{nameof(CreateNewFieldInitMethodRewriter)}", true);
}
}
}