Essentials + extra crap
Loadings Startups Movement Camera
This commit is contained in:
1 parent
662f4a1d05
commit
5f5ace1b52
734 files changed
+107323
-1713
No files matched your search
+36
@@ -0,0 +1,36 @@
|
||||
using System.Linq;
|
||||
using ImmersiveVrToolsCommon.Runtime.Logging;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace FastScriptReload.Editor.Compilation.CodeRewriting
|
||||
{
|
||||
class BuilderPatternFunctionsRewriter : FastScriptReloadCodeRewriterBase
|
||||
{
|
||||
public BuilderPatternFunctionsRewriter(bool writeRewriteReasonAsComment, bool visitIntoStructuredTrivia = false)
|
||||
: base(writeRewriteReasonAsComment, visitIntoStructuredTrivia)
|
||||
{
|
||||
}
|
||||
|
||||
public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node)
|
||||
{
|
||||
var ancestorName = node.Ancestors().OfType<TypeDeclarationSyntax>().FirstOrDefault()?.Identifier.ValueText;
|
||||
if (string.IsNullOrEmpty(ancestorName)) //TODO: that happens too often for some methods, odd. Why? At very least don't spam user
|
||||
{
|
||||
LoggerScoped.LogWarning($"Unable to find ancestor for node '{node.ToFullString()}'");
|
||||
}
|
||||
else
|
||||
{
|
||||
var ancestorNameWithoutPatchedPostfix = ancestorName.Replace(FastScriptReload.Runtime.AssemblyChangesLoader.ClassnamePatchedPostfix, "");
|
||||
|
||||
if (node.ReturnType is IdentifierNameSyntax name && name.Identifier.ValueText == ancestorNameWithoutPatchedPostfix)
|
||||
{
|
||||
return node.WithReturnType(SyntaxFactory.IdentifierName(ancestorName + " "));
|
||||
}
|
||||
}
|
||||
|
||||
return base.VisitMethodDeclaration(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ad3d5960a86c431cbea11b8962fa361e
|
||||
timeCreated: 1678960928
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 239351
|
||||
packageName: Fast Script Reload
|
||||
packageVersion: 1.6.1
|
||||
assetPath: Assets/FastScriptReload/Scripts/Editor/Compilation/CodeRewriting/BuilderPatternFunctionsRewriter.cs
|
||||
uploadId: 710802
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
using System.Linq;
|
||||
using FastScriptReload.Runtime;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace FastScriptReload.Editor.Compilation.CodeRewriting
|
||||
{
|
||||
class ConstructorRewriter : FastScriptReloadCodeRewriterBase
|
||||
{
|
||||
private readonly bool _adjustCtorOnlyForNonNestedTypes;
|
||||
|
||||
public ConstructorRewriter(bool adjustCtorOnlyForNonNestedTypes, bool writeRewriteReasonAsComment)
|
||||
: base(writeRewriteReasonAsComment)
|
||||
{
|
||||
_adjustCtorOnlyForNonNestedTypes = adjustCtorOnlyForNonNestedTypes;
|
||||
}
|
||||
|
||||
public override SyntaxNode VisitConstructorDeclaration(ConstructorDeclarationSyntax node)
|
||||
{
|
||||
if (_adjustCtorOnlyForNonNestedTypes)
|
||||
{
|
||||
var typeNestedLevel = node.Ancestors().Count(a => a is TypeDeclarationSyntax);
|
||||
if (typeNestedLevel == 1)
|
||||
{
|
||||
return AdjustCtorOrDestructorNameForTypeAdjustment(node, node.Identifier);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return AdjustCtorOrDestructorNameForTypeAdjustment(node, node.Identifier);
|
||||
}
|
||||
|
||||
return base.VisitConstructorDeclaration(node);
|
||||
}
|
||||
|
||||
public override SyntaxNode VisitDestructorDeclaration(DestructorDeclarationSyntax node)
|
||||
{
|
||||
if (_adjustCtorOnlyForNonNestedTypes)
|
||||
{
|
||||
var typeNestedLevel = node.Ancestors().Count(a => a is TypeDeclarationSyntax);
|
||||
if (typeNestedLevel == 1)
|
||||
{
|
||||
return AdjustCtorOrDestructorNameForTypeAdjustment(node, node.Identifier);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return AdjustCtorOrDestructorNameForTypeAdjustment(node, node.Identifier);
|
||||
}
|
||||
|
||||
return base.VisitDestructorDeclaration(node);
|
||||
}
|
||||
|
||||
private SyntaxNode AdjustCtorOrDestructorNameForTypeAdjustment(BaseMethodDeclarationSyntax node, SyntaxToken nodeIdentifier)
|
||||
{
|
||||
var typeName = (node.Ancestors().First(n => n is TypeDeclarationSyntax) as TypeDeclarationSyntax).Identifier.ToString();
|
||||
if (!nodeIdentifier.ToFullString().Contains(typeName))
|
||||
{
|
||||
//Used Roslyn version bug, some static methods are also interpreted as ctors, eg
|
||||
// public static void Method()
|
||||
// {
|
||||
// Bar(); //treated as Ctor declaration...
|
||||
// }
|
||||
//
|
||||
// private static void Bar()
|
||||
// {
|
||||
//
|
||||
// }
|
||||
return node;
|
||||
}
|
||||
|
||||
if (!typeName.EndsWith(AssemblyChangesLoader.ClassnamePatchedPostfix))
|
||||
{
|
||||
typeName += AssemblyChangesLoader.ClassnamePatchedPostfix;
|
||||
}
|
||||
|
||||
return AddRewriteCommentIfNeeded(
|
||||
node.ReplaceToken(nodeIdentifier, SyntaxFactory.Identifier(typeName)),
|
||||
$"{nameof(ConstructorRewriter)}:{nameof(AdjustCtorOrDestructorNameForTypeAdjustment)}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1d2a76467ecd40529f59bc68be8d7289
|
||||
timeCreated: 1672992987
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 239351
|
||||
packageName: Fast Script Reload
|
||||
packageVersion: 1.6.1
|
||||
assetPath: Assets/FastScriptReload/Scripts/Editor/Compilation/CodeRewriting/ConstructorRewriter.cs
|
||||
uploadId: 710802
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
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 = "global::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);
|
||||
|
||||
//TODO: slightly odd scenario 'When explicit #nullable enable is used, reference types should be rewritten to typeof(type) for initialization and value types should remain typeof(type?)'
|
||||
// Func<FieldDeclarationSyntax, ExpressionSyntax> getObjectTypeFnSyntax = fieldDeclarationNode => SyntaxFactory.TypeOfExpression(
|
||||
// SyntaxFactory.ParseTypeName(fieldDeclarationNode.Declaration.Type.ToFullString().Replace("?", ""))
|
||||
// );
|
||||
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("global::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("global::System"),
|
||||
SyntaxFactory.GenericName(
|
||||
SyntaxFactory.Identifier("Func"))
|
||||
.WithTypeArgumentList(
|
||||
SyntaxFactory.TypeArgumentList(
|
||||
SyntaxFactory
|
||||
.SingletonSeparatedList<
|
||||
TypeSyntax>(
|
||||
SyntaxFactory
|
||||
.PredefinedType(
|
||||
SyntaxFactory.Token(
|
||||
SyntaxKind
|
||||
.ObjectKeyword))))))
|
||||
}))))
|
||||
.WithInitializer(dictionaryInitializer))))))
|
||||
.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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6bbeaafe1bf54bda9b46e237bec3ccc0
|
||||
timeCreated: 1674215064
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 239351
|
||||
packageName: Fast Script Reload
|
||||
packageVersion: 1.6.1
|
||||
assetPath: Assets/FastScriptReload/Scripts/Editor/Compilation/CodeRewriting/CreateNewFieldInitMethodRewriter.cs
|
||||
uploadId: 710802
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace FastScriptReload.Editor.Compilation.CodeRewriting
|
||||
{
|
||||
// When calling other extension methods from same file compilation would fail with 'The call is ambiguous between the following methods or properties'
|
||||
class ExtensionMethodsCallingOtherExtensionMethodsInSameFileRewriter : FastScriptReloadCodeRewriterBase
|
||||
{
|
||||
private Dictionary<string, Dictionary<string, string>> _typeNameToDeclaredMethodNameToThisArgName = new Dictionary<string, Dictionary<string, string>>();
|
||||
|
||||
public ExtensionMethodsCallingOtherExtensionMethodsInSameFileRewriter(bool writeRewriteReasonAsComment)
|
||||
: base(writeRewriteReasonAsComment)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node)
|
||||
{
|
||||
var classDeclaredMethodNameToThisArgName = new Dictionary<string, string>();
|
||||
_typeNameToDeclaredMethodNameToThisArgName[node.Identifier.Text] = classDeclaredMethodNameToThisArgName;
|
||||
|
||||
foreach (var methodDeclaration in node.Members
|
||||
.Where(n => n.Kind() == SyntaxKind.MethodDeclaration)
|
||||
.Select(n => (MethodDeclarationSyntax)n))
|
||||
{
|
||||
var parameters = methodDeclaration.ParameterList.Parameters;
|
||||
if (parameters.Count > 0 && parameters[0].Modifiers.Any(SyntaxKind.ThisKeyword))
|
||||
{
|
||||
var thisArgumentName = parameters[0].Identifier.Text;
|
||||
classDeclaredMethodNameToThisArgName.TryAdd(methodDeclaration.Identifier.Text, thisArgumentName);
|
||||
}
|
||||
}
|
||||
|
||||
return base.VisitClassDeclaration(node);
|
||||
}
|
||||
|
||||
public override SyntaxNode VisitInvocationExpression(InvocationExpressionSyntax node)
|
||||
{
|
||||
// Check if the invocation is an instance method with a member access (e.g., selfName.CallingOtherTest())
|
||||
if (node.Expression is MemberAccessExpressionSyntax memberAccess)
|
||||
{
|
||||
var methodName = memberAccess.Name.Identifier.Text;
|
||||
var instanceArgName = memberAccess.Expression;
|
||||
var className = node.Ancestors().OfType<ClassDeclarationSyntax>().First().Identifier.Text;
|
||||
if (_typeNameToDeclaredMethodNameToThisArgName.TryGetValue(className, out var declaredMethodNameToThisArgName)
|
||||
&& declaredMethodNameToThisArgName.TryGetValue(methodName, out var definedInstanceArgName))
|
||||
{
|
||||
if (instanceArgName.ToString() == definedInstanceArgName)
|
||||
{
|
||||
// Rewrite the invocation to a static method call
|
||||
var newInvocation = SyntaxFactory.InvocationExpression(SyntaxFactory.IdentifierName(methodName))
|
||||
.WithArgumentList(
|
||||
SyntaxFactory.ArgumentList(
|
||||
SyntaxFactory.SeparatedList(
|
||||
node.ArgumentList.Arguments.Insert(0, SyntaxFactory.Argument(instanceArgName)) // Add instance as first argument
|
||||
)
|
||||
)
|
||||
).NormalizeWhitespace();
|
||||
|
||||
return AddRewriteCommentIfNeeded(
|
||||
newInvocation,
|
||||
$"{nameof(ExtensionMethodsCallingOtherExtensionMethodsInSameFileRewriter)}:Replaced extension method call with static call"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return base.VisitInvocationExpression(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9f739c197f9040efabf7f64461882d7b
|
||||
timeCreated: 1727194643
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 239351
|
||||
packageName: Fast Script Reload
|
||||
packageVersion: 1.6.1
|
||||
assetPath: Assets/FastScriptReload/Scripts/Editor/Compilation/CodeRewriting/ExtensionMethodsCallingOtherExtensionMethodsInSameFileRewriter.cs
|
||||
uploadId: 710802
|
||||
+64
@@ -0,0 +1,64 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
|
||||
namespace FastScriptReload.Editor.Compilation.CodeRewriting
|
||||
{
|
||||
public abstract class FastScriptReloadCodeRewriterBase : CSharpSyntaxRewriter
|
||||
{
|
||||
protected readonly bool _writeRewriteReasonAsComment;
|
||||
|
||||
protected FastScriptReloadCodeRewriterBase(bool writeRewriteReasonAsComment, bool visitIntoStructuredTrivia = false) : base(visitIntoStructuredTrivia)
|
||||
{
|
||||
_writeRewriteReasonAsComment = writeRewriteReasonAsComment;
|
||||
}
|
||||
|
||||
protected SyntaxToken AddRewriteCommentIfNeeded(SyntaxToken syntaxToken, string commentText, bool append = false)
|
||||
{
|
||||
return AddRewriteCommentIfNeeded(syntaxToken, commentText, _writeRewriteReasonAsComment, append);
|
||||
}
|
||||
|
||||
public static SyntaxToken AddRewriteCommentIfNeeded(SyntaxToken syntaxToken, string commentText, bool writeRewriteReasonAsComment, bool append)
|
||||
{
|
||||
if (writeRewriteReasonAsComment)
|
||||
{
|
||||
if (append)
|
||||
{
|
||||
return syntaxToken.WithLeadingTrivia(
|
||||
syntaxToken.LeadingTrivia.Add(SyntaxFactory.Comment($"/*FSR:{commentText}*/")));
|
||||
}
|
||||
else
|
||||
{
|
||||
return syntaxToken.WithTrailingTrivia(
|
||||
syntaxToken.TrailingTrivia.Add(SyntaxFactory.Comment($"/*FSR:{commentText}*/")));
|
||||
}
|
||||
}
|
||||
|
||||
return syntaxToken;
|
||||
}
|
||||
|
||||
protected T AddRewriteCommentIfNeeded<T>(T syntaxNode, string commentText, bool append = false)
|
||||
where T : SyntaxNode
|
||||
{
|
||||
return AddRewriteCommentIfNeeded(syntaxNode, commentText, _writeRewriteReasonAsComment, append);
|
||||
}
|
||||
|
||||
public static T AddRewriteCommentIfNeeded<T>(T syntaxNode, string commentText, bool writeRewriteReasonAsComment, bool append) where T : SyntaxNode
|
||||
{
|
||||
if (writeRewriteReasonAsComment)
|
||||
{
|
||||
if (append)
|
||||
{
|
||||
return syntaxNode.WithLeadingTrivia(syntaxNode.GetLeadingTrivia()
|
||||
.Add(SyntaxFactory.Comment($"/*FSR:{commentText}*/")));
|
||||
}
|
||||
else
|
||||
{
|
||||
return syntaxNode.WithTrailingTrivia(syntaxNode.GetTrailingTrivia()
|
||||
.Add(SyntaxFactory.Comment($"/*FSR:{commentText}*/")));
|
||||
}
|
||||
}
|
||||
|
||||
return syntaxNode;
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bcc848f0140d41cdbf1003b0f594694e
|
||||
timeCreated: 1677233811
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 239351
|
||||
packageName: Fast Script Reload
|
||||
packageVersion: 1.6.1
|
||||
assetPath: Assets/FastScriptReload/Scripts/Editor/Compilation/CodeRewriting/FastScriptReloadCodeRewriterBase.cs
|
||||
uploadId: 710802
|
||||
@@ -0,0 +1,43 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace FastScriptReload.Editor.Compilation.CodeRewriting
|
||||
{
|
||||
class FieldsWalker : CSharpSyntaxWalker {
|
||||
private readonly Dictionary<string, List<NewFieldDeclaration>> _typeNameToFieldDeclarations = new Dictionary<string, List<NewFieldDeclaration>>();
|
||||
|
||||
public override void VisitClassDeclaration(ClassDeclarationSyntax node)
|
||||
{
|
||||
var className = node.Identifier;
|
||||
var fullClassName = RoslynUtils.GetMemberFQDN(node, className.ToString());
|
||||
if(!_typeNameToFieldDeclarations.ContainsKey(fullClassName)) {
|
||||
_typeNameToFieldDeclarations[fullClassName] = new List<NewFieldDeclaration>();
|
||||
}
|
||||
|
||||
base.VisitClassDeclaration(node);
|
||||
}
|
||||
|
||||
public override void VisitFieldDeclaration(FieldDeclarationSyntax node)
|
||||
{
|
||||
var fieldName = node.Declaration.Variables.First().Identifier.ToString();
|
||||
var fullClassName = RoslynUtils.GetMemberFQDNWithoutMemberName(node);
|
||||
if(!_typeNameToFieldDeclarations.ContainsKey(fullClassName)) {
|
||||
_typeNameToFieldDeclarations[fullClassName] = new List<NewFieldDeclaration>();
|
||||
}
|
||||
|
||||
_typeNameToFieldDeclarations[fullClassName].Add(new NewFieldDeclaration(fieldName, node.Declaration.Type.ToString(), node));
|
||||
|
||||
base.VisitFieldDeclaration(node);
|
||||
}
|
||||
|
||||
public Dictionary<string, List<NewFieldDeclaration>> GetTypeToFieldDeclarations() {
|
||||
return _typeNameToFieldDeclarations;
|
||||
}
|
||||
|
||||
public List<string> GetTypeNames() {
|
||||
return _typeNameToFieldDeclarations.Select(kv => kv.Key).ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e4a18d5133af410bab07d9c1a973fe7a
|
||||
timeCreated: 1672993042
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 239351
|
||||
packageName: Fast Script Reload
|
||||
packageVersion: 1.6.1
|
||||
assetPath: Assets/FastScriptReload/Scripts/Editor/Compilation/CodeRewriting/FieldsWalker.cs
|
||||
uploadId: 710802
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
using System.Collections.Generic;
|
||||
using FastScriptReload.Runtime;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace FastScriptReload.Editor.Compilation.CodeRewriting
|
||||
{
|
||||
class HotReloadCompliantRewriter : FastScriptReloadCodeRewriterBase
|
||||
{
|
||||
public List<string> StrippedUsingDirectives = new List<string>();
|
||||
public List<string> OriginalIdentifiersRenamedToContainPatchedPostfix = new List<string>();
|
||||
|
||||
public HotReloadCompliantRewriter(bool writeRewriteReasonAsComment, bool visitIntoStructuredTrivia = false)
|
||||
: base(writeRewriteReasonAsComment, visitIntoStructuredTrivia)
|
||||
{
|
||||
}
|
||||
|
||||
public override SyntaxNode VisitClassDeclaration(ClassDeclarationSyntax node)
|
||||
{
|
||||
return AddPatchedPostfixToTopLevelDeclarations(node, node.Identifier);
|
||||
//if subclasses need to be adjusted, it's done via recursion.
|
||||
// foreach (var childNode in node.ChildNodes().OfType<ClassDeclarationSyntax>())
|
||||
// {
|
||||
// var changed = Visit(childNode);
|
||||
// node = node.ReplaceNode(childNode, changed);
|
||||
// }
|
||||
}
|
||||
|
||||
public override SyntaxNode VisitStructDeclaration(StructDeclarationSyntax node)
|
||||
{
|
||||
return AddPatchedPostfixToTopLevelDeclarations(node, node.Identifier);
|
||||
}
|
||||
|
||||
public override SyntaxNode VisitEnumDeclaration(EnumDeclarationSyntax node)
|
||||
{
|
||||
return AddPatchedPostfixToTopLevelDeclarations(node, node.Identifier);
|
||||
}
|
||||
|
||||
public override SyntaxNode VisitDelegateDeclaration(DelegateDeclarationSyntax node)
|
||||
{
|
||||
return AddPatchedPostfixToTopLevelDeclarations(node, node.Identifier);
|
||||
}
|
||||
|
||||
public override SyntaxNode VisitInterfaceDeclaration(InterfaceDeclarationSyntax node)
|
||||
{
|
||||
return AddPatchedPostfixToTopLevelDeclarations(node, node.Identifier);
|
||||
}
|
||||
|
||||
public override SyntaxNode VisitUsingDirective(UsingDirectiveSyntax node)
|
||||
{
|
||||
if (node.Parent is CompilationUnitSyntax)
|
||||
{
|
||||
StrippedUsingDirectives.Add(node.ToFullString());
|
||||
return null;
|
||||
}
|
||||
|
||||
return base.VisitUsingDirective(node);
|
||||
}
|
||||
|
||||
private SyntaxNode AddPatchedPostfixToTopLevelDeclarations(CSharpSyntaxNode node, SyntaxToken identifier)
|
||||
{
|
||||
OriginalIdentifiersRenamedToContainPatchedPostfix.Add(identifier.ValueText);
|
||||
|
||||
var newIdentifier = SyntaxFactory.Identifier(identifier + AssemblyChangesLoader.ClassnamePatchedPostfix);
|
||||
newIdentifier = AddRewriteCommentIfNeeded(newIdentifier, $"{nameof(HotReloadCompliantRewriter)}:{nameof(AddPatchedPostfixToTopLevelDeclarations)}");
|
||||
node = node.ReplaceToken(identifier, newIdentifier);
|
||||
return node;
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f75e3e02acfa4397a558a923f7fa4f8e
|
||||
timeCreated: 1672993042
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 239351
|
||||
packageName: Fast Script Reload
|
||||
packageVersion: 1.6.1
|
||||
assetPath: Assets/FastScriptReload/Scripts/Editor/Compilation/CodeRewriting/HotReloadCompliantRewriter.cs
|
||||
uploadId: 710802
|
||||
+127
@@ -0,0 +1,127 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace FastScriptReload.Editor.Compilation.CodeRewriting
|
||||
{
|
||||
public class ManualUserDefinedScriptOverridesRewriter : FastScriptReloadCodeRewriterBase
|
||||
{
|
||||
private readonly SyntaxNode _userDefinedOverridesRoot;
|
||||
|
||||
public ManualUserDefinedScriptOverridesRewriter(SyntaxNode userDefinedOverridesRoot, bool writeRewriteReasonAsComment, bool visitIntoStructuredTrivia = false)
|
||||
: base(writeRewriteReasonAsComment, visitIntoStructuredTrivia)
|
||||
{
|
||||
_userDefinedOverridesRoot = userDefinedOverridesRoot;
|
||||
}
|
||||
|
||||
//TODO: refactor to use OverrideDeclarationWithMatchingUserDefinedIfExists
|
||||
public override SyntaxNode VisitConversionOperatorDeclaration(ConversionOperatorDeclarationSyntax node)
|
||||
{
|
||||
var methodFQDN = RoslynUtils.GetMemberFQDN(node, "operator");
|
||||
var matchingInOverride = _userDefinedOverridesRoot.DescendantNodes()
|
||||
//implicit conversion operators do not have name, just parameter list
|
||||
.OfType<BaseMethodDeclarationSyntax>()
|
||||
.FirstOrDefault(m => m.ParameterList.ToString() == node.ParameterList.ToString() //parameter lists is type / order / names, all good for targetting if there's a proper match
|
||||
&& methodFQDN == RoslynUtils.GetMemberFQDN(m, "operator") //make sure same FQDN, even though there's no name there could be more implicit operators in file
|
||||
);
|
||||
|
||||
if (matchingInOverride != null)
|
||||
{
|
||||
return AddRewriteCommentIfNeeded(matchingInOverride.WithTriviaFrom(node), $"User defined custom conversion override", true);
|
||||
}
|
||||
else {
|
||||
return base.VisitConversionOperatorDeclaration(node);
|
||||
}
|
||||
}
|
||||
|
||||
//TODO: refactor to use OverrideDeclarationWithMatchingUserDefinedIfExists
|
||||
public override SyntaxNode VisitMethodDeclaration(MethodDeclarationSyntax node)
|
||||
{
|
||||
var methodName = node.Identifier.ValueText;
|
||||
var methodFQDN = RoslynUtils.GetMemberFQDN(node, node.Identifier.ToString());
|
||||
var matchingInOverride = _userDefinedOverridesRoot.DescendantNodes()
|
||||
.OfType<MethodDeclarationSyntax>()
|
||||
.FirstOrDefault(m => m.Identifier.ValueText == methodName
|
||||
&& m.ParameterList.Parameters.Count == node.ParameterList.Parameters.Count
|
||||
&& m.ParameterList.ToString() == node.ParameterList.ToString() //parameter lists is type / order / names, all good for targetting if there's a proper match
|
||||
&& m.TypeParameterList?.ToString() == node.TypeParameterList?.ToString() //typed paratemets are for generics, also check
|
||||
&& methodFQDN == RoslynUtils.GetMemberFQDN(m, m.Identifier.ToString()) //last check for mathod FQDN (potentially slower than others)
|
||||
);
|
||||
|
||||
if (matchingInOverride != null)
|
||||
{
|
||||
return AddRewriteCommentIfNeeded(matchingInOverride.WithTriviaFrom(node), $"User defined custom method override", true);
|
||||
}
|
||||
else {
|
||||
return base.VisitMethodDeclaration(node);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public override SyntaxNode VisitConstructorDeclaration(ConstructorDeclarationSyntax node)
|
||||
{
|
||||
return OverrideDeclarationWithMatchingUserDefinedIfExists(
|
||||
node,
|
||||
(d) => d.Identifier.ValueText,
|
||||
(d) => HasSameParametersPredicate(node.ParameterList)(d.ParameterList),
|
||||
(d) => base.VisitConstructorDeclaration(d)
|
||||
);
|
||||
}
|
||||
|
||||
public override SyntaxNode VisitDestructorDeclaration(DestructorDeclarationSyntax node)
|
||||
{
|
||||
return OverrideDeclarationWithMatchingUserDefinedIfExists(
|
||||
node,
|
||||
(d) => d.Identifier.ValueText,
|
||||
(d) => HasSameParametersPredicate(node.ParameterList)(d.ParameterList),
|
||||
(d) => base.VisitDestructorDeclaration(d)
|
||||
);
|
||||
}
|
||||
|
||||
public override SyntaxNode VisitPropertyDeclaration(PropertyDeclarationSyntax node)
|
||||
{
|
||||
return OverrideDeclarationWithMatchingUserDefinedIfExists(
|
||||
node,
|
||||
(d) => d.Identifier.ValueText,
|
||||
(d) => true,
|
||||
(d) => base.VisitPropertyDeclaration(d)
|
||||
);
|
||||
}
|
||||
|
||||
private SyntaxNode OverrideDeclarationWithMatchingUserDefinedIfExists<T>(T node, Func<T, string> getName,
|
||||
Func<T, bool> customFindMatchInOverridePredicate, Func<T, SyntaxNode> visitDefault)
|
||||
where T: MemberDeclarationSyntax
|
||||
{
|
||||
var name = getName(node);
|
||||
var fqdn = RoslynUtils.GetMemberFQDN(node, name);
|
||||
var matchingInOverride = _userDefinedOverridesRoot.DescendantNodes()
|
||||
.OfType<T>()
|
||||
.FirstOrDefault(d =>
|
||||
{
|
||||
var declarationName = getName(d);
|
||||
return declarationName == name
|
||||
&& customFindMatchInOverridePredicate(d)
|
||||
&& fqdn == RoslynUtils.GetMemberFQDN(d, declarationName); //last check for mathod FQDN (potentially slower than others)
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
if (matchingInOverride != null)
|
||||
{
|
||||
return AddRewriteCommentIfNeeded(matchingInOverride.WithTriviaFrom(node),
|
||||
$"User defined custom {typeof(T)} override", true);
|
||||
}
|
||||
else
|
||||
{
|
||||
return visitDefault(node);
|
||||
}
|
||||
}
|
||||
|
||||
private Func<ParameterListSyntax, bool> HasSameParametersPredicate(ParameterListSyntax parameters)
|
||||
{
|
||||
return (resolvedParams) => resolvedParams.Parameters.Count == parameters.Parameters.Count
|
||||
&& resolvedParams.ToString() == parameters.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7d61f64c891c45b68a7db1ea67854127
|
||||
timeCreated: 1678101034
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 239351
|
||||
packageName: Fast Script Reload
|
||||
packageVersion: 1.6.1
|
||||
assetPath: Assets/FastScriptReload/Scripts/Editor/Compilation/CodeRewriting/ManualUserDefinedScriptOverridesRewriter.cs
|
||||
uploadId: 710802
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace FastScriptReload.Editor.Compilation.CodeRewriting
|
||||
{
|
||||
public class NewFieldDeclaration
|
||||
{
|
||||
public string FieldName { get; }
|
||||
public string TypeName { get; }
|
||||
public FieldDeclarationSyntax FieldDeclarationSyntax { get; } //TODO: PERF: will that block whole tree from being garbage collected
|
||||
|
||||
public NewFieldDeclaration(string fieldName, string typeName, FieldDeclarationSyntax fieldDeclarationSyntax)
|
||||
{
|
||||
FieldName = fieldName;
|
||||
TypeName = typeName;
|
||||
FieldDeclarationSyntax = fieldDeclarationSyntax;
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ad6e49db99ff4065b14a3fc416af560b
|
||||
timeCreated: 1673019285
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 239351
|
||||
packageName: Fast Script Reload
|
||||
packageVersion: 1.6.1
|
||||
assetPath: Assets/FastScriptReload/Scripts/Editor/Compilation/CodeRewriting/NewFieldDeclaration.cs
|
||||
uploadId: 710802
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using FastScriptReload.Runtime;
|
||||
using FastScriptReload.Scripts.Runtime;
|
||||
using ImmersiveVrToolsCommon.Runtime.Logging;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace FastScriptReload.Editor.Compilation.CodeRewriting
|
||||
{
|
||||
class NewFieldsRewriter : FastScriptReloadCodeRewriterBase
|
||||
{
|
||||
private readonly Dictionary<string, List<string>> _typeToNewFieldDeclarations;
|
||||
|
||||
public NewFieldsRewriter(Dictionary<string, List<string>> typeToNewFieldDeclarations, bool writeRewriteReasonAsComment)
|
||||
:base(writeRewriteReasonAsComment)
|
||||
{
|
||||
_typeToNewFieldDeclarations = typeToNewFieldDeclarations;
|
||||
}
|
||||
|
||||
public static List<MemberInfo> GetReplaceableMembers(Type type)
|
||||
{ //TODO: later other might need to be included? props?
|
||||
return type.GetFields(BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public | BindingFlags.NonPublic).Cast<MemberInfo>().ToList();
|
||||
}
|
||||
|
||||
|
||||
public override SyntaxNode VisitInvocationExpression(InvocationExpressionSyntax node)
|
||||
{
|
||||
if (node.Expression.ToString() == "nameof")
|
||||
{
|
||||
var classNode = node.Ancestors().OfType<ClassDeclarationSyntax>().FirstOrDefault();
|
||||
if (classNode != null)
|
||||
{
|
||||
var fullClassName = RoslynUtils.GetMemberFQDN(classNode, classNode.Identifier.ToString());
|
||||
if (!string.IsNullOrEmpty(fullClassName))
|
||||
{
|
||||
var nameofExpressionParts = node.ArgumentList.Arguments.First().ToFullString().Split('.'); //nameof could have multiple . like NewFieldCustomClass.FieldInThatClass
|
||||
var fieldName = nameofExpressionParts.First(); // should take first part only to determine if new field eg. 'NewFieldCustomClass'
|
||||
if (_typeToNewFieldDeclarations.TryGetValue(fullClassName, out var allNewFieldNamesForClass))
|
||||
{
|
||||
if (allNewFieldNamesForClass.Contains(fieldName))
|
||||
{
|
||||
return AddRewriteCommentIfNeeded(
|
||||
SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal(nameofExpressionParts.Last())), // should take last part only to for actual string eg. 'FieldInThatClass'
|
||||
$"{nameof(NewFieldsRewriter)}:{nameof(VisitInvocationExpression)}");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return base.VisitInvocationExpression(node);
|
||||
}
|
||||
|
||||
public override SyntaxNode VisitIdentifierName(IdentifierNameSyntax node)
|
||||
{
|
||||
var classNode = node.Ancestors().OfType<ClassDeclarationSyntax>().FirstOrDefault();
|
||||
if (classNode != null)
|
||||
{
|
||||
var fullClassName = RoslynUtils.GetMemberFQDN(classNode, classNode.Identifier.ToString());
|
||||
if (!string.IsNullOrEmpty(fullClassName))
|
||||
{
|
||||
var fieldName = node.Identifier.ToString();
|
||||
if (_typeToNewFieldDeclarations.TryGetValue(fullClassName, out var allNewFieldNamesForClass))
|
||||
{
|
||||
if (allNewFieldNamesForClass.Contains(fieldName))
|
||||
{
|
||||
if(node.Parent is MemberAccessExpressionSyntax memberAccessExpressionSyntax)
|
||||
{
|
||||
//for member access expressions only rewrite if used with .this, eg this.NewField, that prevents rewriting of static class fields with same name
|
||||
if (!memberAccessExpressionSyntax.ToString().StartsWith("this."))
|
||||
{
|
||||
return base.VisitIdentifierName(node);
|
||||
}
|
||||
}
|
||||
|
||||
var isNameOfExpression = node.Ancestors().OfType<InvocationExpressionSyntax>().Any(e => e.Expression.ToString() == "nameof");
|
||||
if (!isNameOfExpression) //nameof expression will be rewritten via VisitInvocationExpression
|
||||
{
|
||||
return
|
||||
AddRewriteCommentIfNeeded(
|
||||
SyntaxFactory.MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
SyntaxFactory.InvocationExpression(
|
||||
SyntaxFactory.MemberAccessExpression(
|
||||
SyntaxKind.SimpleMemberAccessExpression,
|
||||
SyntaxFactory.IdentifierName(typeof(TemporaryNewFieldValues).FullName),
|
||||
SyntaxFactory.GenericName(
|
||||
SyntaxFactory.Identifier(nameof(TemporaryNewFieldValues.ResolvePatchedObject)))
|
||||
.WithTypeArgumentList(
|
||||
SyntaxFactory.TypeArgumentList(
|
||||
SyntaxFactory.SingletonSeparatedList<TypeSyntax>(
|
||||
SyntaxFactory.IdentifierName(fullClassName + AssemblyChangesLoader.ClassnamePatchedPostfix))))))
|
||||
.WithArgumentList(
|
||||
SyntaxFactory.ArgumentList(
|
||||
SyntaxFactory.SingletonSeparatedList<ArgumentSyntax>(
|
||||
SyntaxFactory.Argument(
|
||||
SyntaxFactory.ThisExpression())))),
|
||||
SyntaxFactory.IdentifierName(fieldName))
|
||||
.WithTriviaFrom(node),
|
||||
$"{nameof(NewFieldsRewriter)}:{nameof(VisitIdentifierName)}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LoggerScoped.LogWarning($"Unable to find type: {fullClassName}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return base.VisitIdentifierName(node);
|
||||
}
|
||||
|
||||
public override SyntaxNode VisitFieldDeclaration(FieldDeclarationSyntax node)
|
||||
{
|
||||
var fieldName = node.Declaration.Variables.First().Identifier.ToString();
|
||||
var fullClassName = RoslynUtils.GetMemberFQDNWithoutMemberName(node);
|
||||
|
||||
if (_typeToNewFieldDeclarations.TryGetValue(fullClassName, out var newFields))
|
||||
{
|
||||
if (newFields.Contains(fieldName))
|
||||
{
|
||||
var existingLeading = node.GetLeadingTrivia();
|
||||
var existingTrailing = node.GetTrailingTrivia();
|
||||
|
||||
return AddRewriteCommentIfNeeded(
|
||||
node
|
||||
.WithLeadingTrivia(existingLeading.Add(SyntaxFactory.Comment("/* ")))
|
||||
.WithTrailingTrivia(existingTrailing.Insert(0, SyntaxFactory.Comment(" */ //Auto-excluded to prevent exceptions - see docs"))),
|
||||
$"{nameof(NewFieldsRewriter)}:{nameof(VisitFieldDeclaration)}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return base.VisitFieldDeclaration(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0e9e687bdfc0444395deab1e7c68ed3f
|
||||
timeCreated: 1672993042
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 239351
|
||||
packageName: Fast Script Reload
|
||||
packageVersion: 1.6.1
|
||||
assetPath: Assets/FastScriptReload/Scripts/Editor/Compilation/CodeRewriting/NewFieldsRewriter.cs
|
||||
uploadId: 710802
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 919d4ff85d4d4156bddf5c48bcc99da1
|
||||
timeCreated: 1719699288
|
||||
+87
@@ -0,0 +1,87 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
|
||||
public static class PartialClassFinder
|
||||
{
|
||||
//TODO: optimize lookup, currently this loads all files in directory and checks if they have 'partial' string in them. For directories with many files this will not be sufficient
|
||||
public static IEnumerable<string> FindPartialClassFilesInDirectory(string filePath, int maxDepth = int.MaxValue)
|
||||
{
|
||||
if (string.IsNullOrEmpty(filePath) || !File.Exists(filePath))
|
||||
return Enumerable.Empty<string>();
|
||||
|
||||
string directory = Path.GetDirectoryName(filePath);
|
||||
string fileContent = File.ReadAllText(filePath);
|
||||
|
||||
string partialClassName = ExtractPartialClassName(fileContent);
|
||||
if (string.IsNullOrEmpty(partialClassName))
|
||||
{
|
||||
Debug.LogWarning($"No partial class/struct found in file: {filePath}");
|
||||
return Enumerable.Empty<string>();
|
||||
}
|
||||
|
||||
return FindPartialClassFilesRecursively(directory, partialClassName, maxDepth, currentDepth: 0);
|
||||
}
|
||||
|
||||
private static IEnumerable<string> FindPartialClassFilesRecursively(string directory, string partialClassName, int maxDepth, int currentDepth)
|
||||
{
|
||||
var files = new List<string>();
|
||||
|
||||
try
|
||||
{
|
||||
// Search in current directory
|
||||
files.AddRange(Directory.GetFiles(directory, "*.cs")
|
||||
.Where(file => IsPartialClassFile(file, partialClassName)));
|
||||
|
||||
// If we haven't reached max depth, search in subdirectories
|
||||
if (currentDepth < maxDepth)
|
||||
{
|
||||
foreach (var subDir in Directory.GetDirectories(directory))
|
||||
{
|
||||
files.AddRange(FindPartialClassFilesRecursively(subDir, partialClassName, maxDepth, currentDepth + 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (UnauthorizedAccessException ex)
|
||||
{
|
||||
Debug.LogWarning($"Access denied to directory: {directory}. Error: {ex.Message}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"Error searching directory: {directory}. Error: {ex.Message}");
|
||||
}
|
||||
|
||||
return files;
|
||||
}
|
||||
|
||||
private static bool IsPartialClassFile(string filePath, string className)
|
||||
{
|
||||
try
|
||||
{
|
||||
string content = File.ReadAllText(filePath);
|
||||
return content.Contains($"partial class {className}")
|
||||
|| content.Contains($"partial struct {className}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Debug.LogError($"Error reading file: {filePath}. Error: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static string ExtractPartialClassName(string fileContent)
|
||||
{
|
||||
// This is a simple implementation and might need to be more robust
|
||||
// depending on your specific needs
|
||||
var partialClassDeclaration = fileContent.Split(new[] { "partial class", "partial struct" }, StringSplitOptions.RemoveEmptyEntries)
|
||||
.Skip(1)
|
||||
.FirstOrDefault();
|
||||
|
||||
if (partialClassDeclaration == null)
|
||||
return string.Empty;
|
||||
|
||||
return partialClassDeclaration.TrimStart().Split(new[] { ' ', '{', ':' }, StringSplitOptions.RemoveEmptyEntries).FirstOrDefault().Trim();
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6f182f9db524441e80f367f8f99d8a5f
|
||||
timeCreated: 1719778688
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 239351
|
||||
packageName: Fast Script Reload
|
||||
packageVersion: 1.6.1
|
||||
assetPath: Assets/FastScriptReload/Scripts/Editor/Compilation/CodeRewriting/Partials/PartialClassFinder.cs
|
||||
uploadId: 710802
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace FastScriptReload.Editor.Compilation.CodeRewriting
|
||||
{
|
||||
internal static class PartialTreeProcessor
|
||||
{
|
||||
/// <summary>
|
||||
/// Processes a syntax tree containing partial type definitions, merging it with other partial definitions found in the same directory.
|
||||
/// </summary>
|
||||
/// <param name="tree">The original syntax tree to process.</param>
|
||||
/// <param name="definedPreprocessorSymbols">A collection of defined preprocessor symbols.</param>
|
||||
/// <returns>A new SyntaxTree with merged partial types and added InternalsVisibleTo attribute.</returns>
|
||||
/// <remarks>
|
||||
/// This method performs the following steps:
|
||||
/// 1. Checks if the tree contains partial types.
|
||||
/// 2. If it does, processes the current tree and other partial files in the same directory.
|
||||
/// 3. Combines all partial type declarations.
|
||||
/// 4. Creates a new compilation unit with merged types and using directives.
|
||||
/// 5. Adds the InternalsVisibleTo attribute to the resulting tree.
|
||||
/// </remarks>
|
||||
internal static SyntaxTree ProcessPartialTree(SyntaxTree tree, IEnumerable<string> definedPreprocessorSymbols)
|
||||
{
|
||||
if (!HasPartialTypes(tree))
|
||||
{
|
||||
return tree;
|
||||
}
|
||||
|
||||
var combinedTypes = new Dictionary<string, List<TypeDeclarationSyntax>>();
|
||||
var combinedUsingDirectives = new HashSet<UsingDirectiveSyntax>();
|
||||
var combinedTypesDefined = new HashSet<string>();
|
||||
|
||||
ProcessTree(tree, combinedTypes, combinedUsingDirectives, combinedTypesDefined);
|
||||
|
||||
const int fileSearchMaxDepth = 5;
|
||||
var otherPartialFiles = PartialClassFinder.FindPartialClassFilesInDirectory(tree.FilePath, fileSearchMaxDepth)
|
||||
.Where(file => file != tree.FilePath);
|
||||
|
||||
foreach (var file in otherPartialFiles)
|
||||
{
|
||||
var partialTree = CSharpSyntaxTree.ParseText(File.ReadAllText(file), path: file);
|
||||
ProcessTree(partialTree, combinedTypes, combinedUsingDirectives, combinedTypesDefined);
|
||||
}
|
||||
|
||||
var combinedTypeDeclarations = combinedTypes
|
||||
.Select(kvp => PartialTypeCombiner.CombinePartialType(kvp.Value))
|
||||
.ToList<MemberDeclarationSyntax>();
|
||||
|
||||
var newRoot = SyntaxFactory.CompilationUnit()
|
||||
.WithUsings(SyntaxFactory.List(combinedUsingDirectives))
|
||||
.WithMembers(SyntaxFactory.List(combinedTypeDeclarations))
|
||||
.WithAdditionalAnnotations(new SyntaxAnnotation("PreprocessorSymbols", string.Join(",", definedPreprocessorSymbols)));
|
||||
|
||||
return CSharpSyntaxTree.Create(newRoot, null, tree.FilePath)
|
||||
.AddInternalsVisibleToAttribute();
|
||||
}
|
||||
|
||||
private static bool HasPartialTypes(SyntaxTree tree)
|
||||
{
|
||||
var root = tree.GetCompilationUnitRoot();
|
||||
return root.DescendantNodes().OfType<TypeDeclarationSyntax>().Any(type => type.Modifiers.Any(SyntaxKind.PartialKeyword));
|
||||
}
|
||||
|
||||
private static void ProcessTree(
|
||||
SyntaxTree tree,
|
||||
Dictionary<string, List<TypeDeclarationSyntax>> combinedTypes,
|
||||
HashSet<UsingDirectiveSyntax> combinedUsingDirectives,
|
||||
HashSet<string> combinedTypesDefined)
|
||||
{
|
||||
var root = tree.GetCompilationUnitRoot();
|
||||
|
||||
combinedUsingDirectives.UnionWith(root.Usings);
|
||||
|
||||
foreach (var typeDecl in root.DescendantNodes().OfType<TypeDeclarationSyntax>())
|
||||
{
|
||||
var fullName = NamespaceHelper.GetFullyQualifiedName(typeDecl);
|
||||
combinedTypesDefined.Add(fullName);
|
||||
|
||||
if (!combinedTypes.TryGetValue(fullName, out var declarations))
|
||||
{
|
||||
declarations = new List<TypeDeclarationSyntax>();
|
||||
combinedTypes[fullName] = declarations;
|
||||
}
|
||||
declarations.Add(typeDecl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4aafad9b85b44ebe902ba3cee7bda205
|
||||
timeCreated: 1719954990
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 239351
|
||||
packageName: Fast Script Reload
|
||||
packageVersion: 1.6.1
|
||||
assetPath: Assets/FastScriptReload/Scripts/Editor/Compilation/CodeRewriting/Partials/PartialTreeProcessor.cs
|
||||
uploadId: 710802
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace FastScriptReload.Editor.Compilation.CodeRewriting
|
||||
{
|
||||
internal static class PartialTypeCombiner
|
||||
{
|
||||
internal static TypeDeclarationSyntax CombinePartialType(List<TypeDeclarationSyntax> declarations)
|
||||
{
|
||||
if (declarations.Count == 1)
|
||||
return declarations[0];
|
||||
|
||||
var firstDeclaration = declarations[0];
|
||||
var combinedMembers = declarations.SelectMany(d => d.Members).ToList();
|
||||
|
||||
var combinedModifiers = declarations
|
||||
.SelectMany(d => d.Modifiers)
|
||||
.Where(m => !m.IsKind(SyntaxKind.PartialKeyword))
|
||||
.GroupBy(m => m.ValueText)
|
||||
.Select(g => g.First().WithTrailingTrivia(SyntaxFactory.Space))
|
||||
.ToList();
|
||||
|
||||
if (!combinedModifiers.Any(m => m.IsKind(SyntaxKind.PartialKeyword)))
|
||||
combinedModifiers.Add(SyntaxFactory.Token(SyntaxKind.PartialKeyword).WithTrailingTrivia(SyntaxFactory.Space));
|
||||
|
||||
var combinedBaseList = declarations
|
||||
.Where(d => d.BaseList != null)
|
||||
.SelectMany(d => d.BaseList.Types)
|
||||
.GroupBy(t => t.ToString())
|
||||
.Select(g => g.First())
|
||||
.ToList();
|
||||
|
||||
return firstDeclaration
|
||||
.WithMembers(SyntaxFactory.List(combinedMembers))
|
||||
.WithModifiers(SyntaxFactory.TokenList(combinedModifiers))
|
||||
.WithKeyword(firstDeclaration.Keyword.WithLeadingTrivia(SyntaxFactory.Space))
|
||||
.WithBaseList(combinedBaseList.Any()
|
||||
? SyntaxFactory.BaseList(SyntaxFactory.SeparatedList(combinedBaseList))
|
||||
: null);
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cc8170320f3a463590d434f7e0570ea0
|
||||
timeCreated: 1719955225
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 239351
|
||||
packageName: Fast Script Reload
|
||||
packageVersion: 1.6.1
|
||||
assetPath: Assets/FastScriptReload/Scripts/Editor/Compilation/CodeRewriting/Partials/PartialTypeCombiner.cs
|
||||
uploadId: 710802
|
||||
+52
@@ -0,0 +1,52 @@
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace FastScriptReload.Editor.Compilation.CodeRewriting
|
||||
{
|
||||
internal static class NamespaceHelper
|
||||
{
|
||||
internal static string GetFullyQualifiedName(TypeDeclarationSyntax typeDeclaration)
|
||||
{
|
||||
var namespaces = new List<string>();
|
||||
var currentNode = typeDeclaration.Parent;
|
||||
while (currentNode != null)
|
||||
{
|
||||
if (currentNode is NamespaceDeclarationSyntax namespaceDeclaration)
|
||||
{
|
||||
namespaces.Add(namespaceDeclaration.Name.ToString());
|
||||
}
|
||||
currentNode = currentNode.Parent;
|
||||
}
|
||||
namespaces.Reverse();
|
||||
var namespaceName = string.Join(".", namespaces);
|
||||
return string.IsNullOrEmpty(namespaceName)
|
||||
? typeDeclaration.Identifier.Text
|
||||
: $"{namespaceName}.{typeDeclaration.Identifier.Text}";
|
||||
}
|
||||
}
|
||||
|
||||
internal static class SyntaxTreeExtensions
|
||||
{
|
||||
internal static SyntaxTree AddInternalsVisibleToAttribute(this SyntaxTree tree)
|
||||
{
|
||||
var root = tree.GetCompilationUnitRoot();
|
||||
|
||||
var attributeArgument = SyntaxFactory.AttributeArgument(
|
||||
SyntaxFactory.LiteralExpression(SyntaxKind.StringLiteralExpression, SyntaxFactory.Literal("Assembly-CSharp"))
|
||||
);
|
||||
var attribute = SyntaxFactory.Attribute(
|
||||
SyntaxFactory.ParseName("System.Runtime.CompilerServices.InternalsVisibleTo"),
|
||||
SyntaxFactory.AttributeArgumentList(SyntaxFactory.SeparatedList(new[] { attributeArgument }))
|
||||
);
|
||||
|
||||
var attributeList = SyntaxFactory.AttributeList(SyntaxFactory.SingletonSeparatedList(attribute))
|
||||
.WithTarget(SyntaxFactory.AttributeTargetSpecifier(SyntaxFactory.Token(SyntaxKind.AssemblyKeyword)));
|
||||
|
||||
var newRoot = root.AddAttributeLists(attributeList);
|
||||
|
||||
return tree.WithRootAndOptions(newRoot, tree.Options);
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1c2f0e22f1a24fcda6d0c0516b428420
|
||||
timeCreated: 1719954970
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 239351
|
||||
packageName: Fast Script Reload
|
||||
packageVersion: 1.6.1
|
||||
assetPath: Assets/FastScriptReload/Scripts/Editor/Compilation/CodeRewriting/Partials/SyntaxTreeHelpers.cs
|
||||
uploadId: 710802
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace FastScriptReload.Editor.Compilation.CodeRewriting
|
||||
{
|
||||
public static class SyntaxTreeMergePartialsExtensions
|
||||
{
|
||||
public static IEnumerable<SyntaxTree> MergePartials(
|
||||
this IEnumerable<SyntaxTree> trees,
|
||||
IEnumerable<string> definedPreprocessorSymbols)
|
||||
{
|
||||
return trees
|
||||
.Select(tree => PartialTreeProcessor.ProcessPartialTree(tree, definedPreprocessorSymbols))
|
||||
.ToList();
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 550c942c025f4fd5a1062891f3623c60
|
||||
timeCreated: 1719174342
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 239351
|
||||
packageName: Fast Script Reload
|
||||
packageVersion: 1.6.1
|
||||
assetPath: Assets/FastScriptReload/Scripts/Editor/Compilation/CodeRewriting/Partials/SyntaxTreeMergePartialsExtensions.cs
|
||||
uploadId: 710802
|
||||
@@ -0,0 +1,36 @@
|
||||
using FastScriptReload.Runtime;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace FastScriptReload.Editor.Compilation.CodeRewriting
|
||||
{
|
||||
class RecordeRewriter : FastScriptReloadCodeRewriterBase
|
||||
{
|
||||
public RecordeRewriter(bool writeRewriteReasonAsComment)
|
||||
: base(writeRewriteReasonAsComment)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public override SyntaxNode VisitRecordDeclaration(RecordDeclarationSyntax node)
|
||||
{
|
||||
return AdjustRecordName(node, node.Identifier);
|
||||
}
|
||||
|
||||
private SyntaxNode AdjustRecordName(RecordDeclarationSyntax node, SyntaxToken nodeIdentifier)
|
||||
{
|
||||
var typeName = nodeIdentifier.ToString(); //Not ToFullString() as it may include spaces and break.
|
||||
|
||||
if (!typeName.EndsWith(AssemblyChangesLoader.ClassnamePatchedPostfix))
|
||||
{
|
||||
typeName += AssemblyChangesLoader.ClassnamePatchedPostfix;
|
||||
}
|
||||
|
||||
return AddRewriteCommentIfNeeded(
|
||||
node.ReplaceToken(nodeIdentifier, SyntaxFactory.Identifier(typeName)),
|
||||
$"{nameof(RecordeRewriter)}:{nameof(AdjustRecordName)}"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bb801c55172044ed9294ef722937f12f
|
||||
timeCreated: 1705523787
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 239351
|
||||
packageName: Fast Script Reload
|
||||
packageVersion: 1.6.1
|
||||
assetPath: Assets/FastScriptReload/Scripts/Editor/Compilation/CodeRewriting/RecordRewriter.cs
|
||||
uploadId: 710802
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace FastScriptReload.Editor.Compilation.CodeRewriting
|
||||
{
|
||||
//ideally we'd use Simplifier but FSR doesn't have access to semantic model on which simplifier works
|
||||
|
||||
//Removes redundant type name qualifiers, eg: RootClass.NestedEnum.Value -> NestedEnum.Value, root class component is rewritten to have
|
||||
//__Patched_ postfix and if original remains it'll cause type mismatch
|
||||
class RedundantTypeNameQualifierRewriter : FastScriptReloadCodeRewriterBase
|
||||
{
|
||||
public RedundantTypeNameQualifierRewriter(bool writeRewriteReasonAsComment, bool visitIntoStructuredTrivia = false)
|
||||
: base(writeRewriteReasonAsComment, visitIntoStructuredTrivia)
|
||||
{
|
||||
}
|
||||
|
||||
public override SyntaxNode VisitMemberAccessExpression(MemberAccessExpressionSyntax node)
|
||||
{
|
||||
if(node.Parent is MemberAccessExpressionSyntax)
|
||||
return base.VisitMemberAccessExpression(node); //only target outermost member access for full rewrite
|
||||
|
||||
var firstNode = node.DescendantNodes().OfType<IdentifierNameSyntax>().FirstOrDefault();
|
||||
if(firstNode is IdentifierNameSyntax idNode && node.DescendantNodes().Count() > 1)
|
||||
{
|
||||
//enclosing type name same as first id node
|
||||
if((idNode.Ancestors().First(n => n is TypeDeclarationSyntax) as TypeDeclarationSyntax)?.Identifier.ToString() == idNode.Identifier.ToString())
|
||||
{
|
||||
return AddRewriteCommentIfNeeded(
|
||||
SyntaxFactory.ParseExpression(node.ToString().Replace($"{firstNode.Identifier.ToString()}.", string.Empty)),
|
||||
$"{nameof(RedundantTypeNameQualifierRewriter)}"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return base.VisitMemberAccessExpression(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 80a540048e524157a41df1241bd5c168
|
||||
timeCreated: 1679325667
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 239351
|
||||
packageName: Fast Script Reload
|
||||
packageVersion: 1.6.1
|
||||
assetPath: Assets/FastScriptReload/Scripts/Editor/Compilation/CodeRewriting/RedundantTypeNameQualifierRewriter.cs
|
||||
uploadId: 710802
|
||||
@@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace FastScriptReload.Editor.Compilation.CodeRewriting
|
||||
{
|
||||
public class RoslynUtils
|
||||
{
|
||||
public static string GetMemberFQDN(MemberDeclarationSyntax memberNode, string memberName) //TODO: try get rid of member name (needs to cast to whatever member it could be to get identifier)
|
||||
{
|
||||
var outer = GetMemberFQDNWithoutMemberName(memberNode);
|
||||
return !string.IsNullOrEmpty(outer)
|
||||
? $"{outer}.{memberName}"
|
||||
: memberName;
|
||||
}
|
||||
|
||||
public static string GetMemberFQDNWithoutMemberName(MemberDeclarationSyntax memberNode) //TODO: move out to helper class
|
||||
{
|
||||
var fullTypeContibutingAncestorNames = memberNode.Ancestors().OfType<MemberDeclarationSyntax>().Select(da =>
|
||||
{
|
||||
if (da is TypeDeclarationSyntax t) return t.Identifier.ToString();
|
||||
else if (da is NamespaceDeclarationSyntax n) return n.Name.ToString();
|
||||
else throw new Exception("Unable to resolve full field name");
|
||||
}).Reverse().ToList();
|
||||
|
||||
return string.Join(".", fullTypeContibutingAncestorNames);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 29bd7fad38034046a8dd4c7eea68907c
|
||||
timeCreated: 1673017013
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 239351
|
||||
packageName: Fast Script Reload
|
||||
packageVersion: 1.6.1
|
||||
assetPath: Assets/FastScriptReload/Scripts/Editor/Compilation/CodeRewriting/RoslynUtils.cs
|
||||
uploadId: 710802
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace FastScriptReload.Editor.Compilation.CodeRewriting
|
||||
{
|
||||
class ThisAssignmentRewriter: ThisRewriterBase {
|
||||
public ThisAssignmentRewriter(bool writeRewriteReasonAsComment, bool visitIntoStructuredTrivia = false)
|
||||
: base(writeRewriteReasonAsComment, visitIntoStructuredTrivia)
|
||||
{
|
||||
}
|
||||
|
||||
public override SyntaxNode VisitThisExpression(ThisExpressionSyntax node)
|
||||
{
|
||||
if (node.Parent is AssignmentExpressionSyntax) {
|
||||
return CreateCastedThisExpression(node);
|
||||
}
|
||||
|
||||
return base.VisitThisExpression(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c5a960528b2b44edbf644e0ac0950d35
|
||||
timeCreated: 1672993042
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 239351
|
||||
packageName: Fast Script Reload
|
||||
packageVersion: 1.6.1
|
||||
assetPath: Assets/FastScriptReload/Scripts/Editor/Compilation/CodeRewriting/ThisAssignmentRewriter.cs
|
||||
uploadId: 710802
|
||||
@@ -0,0 +1,22 @@
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace FastScriptReload.Editor.Compilation.CodeRewriting
|
||||
{
|
||||
class ThisCallRewriter : ThisRewriterBase
|
||||
{
|
||||
public ThisCallRewriter(bool writeRewriteReasonAsComment, bool visitIntoStructuredTrivia = false)
|
||||
: base(writeRewriteReasonAsComment, visitIntoStructuredTrivia)
|
||||
{
|
||||
}
|
||||
|
||||
public override SyntaxNode VisitThisExpression(ThisExpressionSyntax node)
|
||||
{
|
||||
if (node.Parent is ArgumentSyntax)
|
||||
{
|
||||
return CreateCastedThisExpression(node);
|
||||
}
|
||||
return base.VisitThisExpression(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f7e7aad39cec445db504fe5a117beae5
|
||||
timeCreated: 1672993042
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 239351
|
||||
packageName: Fast Script Reload
|
||||
packageVersion: 1.6.1
|
||||
assetPath: Assets/FastScriptReload/Scripts/Editor/Compilation/CodeRewriting/ThisCallRewriter.cs
|
||||
uploadId: 710802
|
||||
@@ -0,0 +1,51 @@
|
||||
using System.Linq;
|
||||
using ImmersiveVrToolsCommon.Runtime.Logging;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
|
||||
namespace FastScriptReload.Editor.Compilation.CodeRewriting
|
||||
{
|
||||
abstract class ThisRewriterBase : FastScriptReloadCodeRewriterBase
|
||||
{
|
||||
protected ThisRewriterBase(bool writeRewriteReasonAsComment, bool visitIntoStructuredTrivia = false)
|
||||
: base(writeRewriteReasonAsComment, visitIntoStructuredTrivia)
|
||||
{
|
||||
}
|
||||
|
||||
protected SyntaxNode CreateCastedThisExpression(ThisExpressionSyntax node)
|
||||
{
|
||||
var ancestors = node.Ancestors().Where(n => n is TypeDeclarationSyntax).Cast<TypeDeclarationSyntax>().ToList();
|
||||
if (ancestors.Count() > 1)
|
||||
{
|
||||
LoggerScoped.LogWarning($"ThisRewriter: for class: '{ancestors.First().Identifier}' - 'this' call/assignment in nested class / struct. Dynamic cast will be used but this could cause issues in some cases:" +
|
||||
$"\r\n\r\n1) - If called method has multiple overrides, using dynamic will cause compiler issue as it'll no longer be able to pick correct one" +
|
||||
$"\r\n\r\n If you see any issues with that message, please look at 'Limitation' section in documentation as this outlines how to deal with it.");
|
||||
|
||||
//TODO: casting to dynamic seems to be best option (and one that doesn't fail for nested classes), what's the performance overhead?
|
||||
return SyntaxFactory.CastExpression(
|
||||
SyntaxFactory.ParseTypeName("dynamic"),
|
||||
node
|
||||
);
|
||||
}
|
||||
|
||||
var firstAncestor = ancestors.FirstOrDefault();
|
||||
if (firstAncestor == null)
|
||||
{
|
||||
LoggerScoped.LogWarning($"Unable to find first ancestor for node: {node.ToFullString()}, this rewrite will not be applied");
|
||||
return node;
|
||||
}
|
||||
|
||||
var methodInType = firstAncestor.Identifier.ToString();
|
||||
var resultNode = SyntaxFactory.CastExpression(
|
||||
SyntaxFactory.ParseTypeName(methodInType),
|
||||
SyntaxFactory.CastExpression(
|
||||
SyntaxFactory.ParseTypeName(typeof(object).FullName),
|
||||
node
|
||||
)
|
||||
);
|
||||
|
||||
return AddRewriteCommentIfNeeded(resultNode, $"{nameof(ThisRewriterBase)}:{nameof(CreateCastedThisExpression)}");
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0fd0b779590247168b3873c939c42d71
|
||||
timeCreated: 1672993042
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 239351
|
||||
packageName: Fast Script Reload
|
||||
packageVersion: 1.6.1
|
||||
assetPath: Assets/FastScriptReload/Scripts/Editor/Compilation/CodeRewriting/ThisRewriterBase.cs
|
||||
uploadId: 710802
|
||||
Reference in new issue
Block a user