Rewrite of most internals

This commit is contained in:
Koen Bekkenutte
2021-05-30 21:45:24 +08:00
parent 0d7bec4bd6
commit 5085b1b6ae
60 changed files with 486 additions and 346 deletions
@@ -35,7 +35,7 @@ namespace EntityFrameworkCore.Projections.Generator
if (nextNode is not null)
{
return SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
SyntaxFactory.IdentifierName(ProjectionExpressionGenerator.ProjectionTargetParameterName),
SyntaxFactory.IdentifierName("@this"),
nextNode
);
}
@@ -52,7 +52,7 @@ namespace EntityFrameworkCore.Projections.Generator
if (symbolInfo.Symbol is not null && symbolInfo.Symbol.Kind is SymbolKind.Property or SymbolKind.Method or SymbolKind.Field && SymbolEqualityComparer.Default.Equals(symbolInfo.Symbol.ContainingType, _targetTypeSymbol))
{
return SyntaxFactory.MemberAccessExpression(SyntaxKind.SimpleMemberAccessExpression,
SyntaxFactory.IdentifierName(ProjectionExpressionGenerator.ProjectionTargetParameterName),
SyntaxFactory.IdentifierName("@this"),
node
);
}
@@ -1,4 +1,5 @@
using System.Linq;
using System.Collections.Generic;
using System.Linq;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
@@ -14,28 +15,27 @@ namespace EntityFrameworkCore.Projections.Generator
_semanticModel = semanticModel;
}
public override SyntaxNode? VisitQualifiedName(QualifiedNameSyntax node)
{
// todo: Fully qualify these
return node;
}
public override SyntaxNode? VisitParameter(ParameterSyntax node)
public override SyntaxNode? VisitIdentifierName(IdentifierNameSyntax node)
{
var symbol = _semanticModel.GetDeclaredSymbol(node);
if (symbol is not null)
{
node = SyntaxFactory.IdentifierName(symbol.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat));
}
return base.VisitIdentifierName(node);
}
public override SyntaxNode? VisitParameter(ParameterSyntax node)
{
var thisKeywordIndex = node.Modifiers.IndexOf(SyntaxKind.ThisKeyword);
if (thisKeywordIndex != -1)
{
node = node.WithModifiers(node.Modifiers.RemoveAt(thisKeywordIndex));
}
return node;
return base.VisitParameter(node);
}
}
}
@@ -1,4 +1,5 @@
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using System;
using System.Collections.Generic;
using System.Linq;
@@ -25,7 +26,7 @@ namespace EntityFrameworkCore.Projections.Generator
public string ReturnTypeName { get; set; }
public string ParametersListString { get; set; }
public ParameterListSyntax ParametersList { get; set; }
public SyntaxNode Body { get; set; }
}
@@ -50,9 +50,26 @@ namespace EntityFrameworkCore.Projections.Generator
ClassName = memberSymbol.ContainingType.Name,
ClassNamespace = memberSymbol.ContainingType.ContainingNamespace.IsGlobalNamespace ? null : memberSymbol.ContainingType.ContainingNamespace.ToDisplayString(),
MemberName = memberSymbol.Name,
NestedInClassNames = GetNestedInClassPath(memberSymbol.ContainingType)
NestedInClassNames = GetNestedInClassPath(memberSymbol.ContainingType),
ParametersList = SyntaxFactory.ParameterList()
};
if (!memberDeclarationSyntax.Modifiers.Any(SyntaxKind.StaticKeyword))
{
descriptor.ParametersList = descriptor.ParametersList.AddParameters(
SyntaxFactory.Parameter(
SyntaxFactory.Identifier("@this")
).WithType(
SyntaxFactory.ParseTypeName(
memberSymbol.ContainingType.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)
)
.WithTrailingTrivia(
SyntaxFactory.SyntaxTrivia(SyntaxKind.WhitespaceTrivia, " ")
)
)
);
}
if (memberSymbol is IMethodSymbol methodSymbol && methodSymbol.IsExtensionMethod)
{
var targetTypeSymbol = methodSymbol.Parameters.First().Type;
@@ -69,19 +86,23 @@ namespace EntityFrameworkCore.Projections.Generator
{
descriptor.ReturnTypeName = methodDeclarationSyntax.ReturnType.ToString();
descriptor.Body = expressionSyntaxRewriter.Visit(methodDeclarationSyntax.ExpressionBody.Expression);
descriptor.ParametersListString = parameterSyntaxRewriter.Visit(methodDeclarationSyntax.ParameterList).ToString();
foreach (var additionalParameter in ((ParameterListSyntax)parameterSyntaxRewriter.Visit(methodDeclarationSyntax.ParameterList)).Parameters)
{
descriptor.ParametersList = descriptor.ParametersList.AddParameters(additionalParameter);
}
}
else if (memberDeclarationSyntax is PropertyDeclarationSyntax propertyDeclarationSyntax)
{
descriptor.ReturnTypeName = propertyDeclarationSyntax.Type.ToString();
descriptor.Body = expressionSyntaxRewriter.Visit(propertyDeclarationSyntax.ExpressionBody.Expression);
descriptor.ParametersListString = "()";
}
else
{
return null;
}
descriptor.UsingDirectives =
memberDeclarationSyntax.SyntaxTree
.GetRoot()
@@ -1,5 +1,6 @@
using EntityFrameworkCore.Projections.Services;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Text;
using System;
using System.Collections.Generic;
@@ -12,8 +13,6 @@ namespace EntityFrameworkCore.Projections.Generator
[Generator]
public class ProjectionExpressionGenerator : ISourceGenerator
{
public const string ProjectionTargetParameterName = "projectionTarget";
public void Execute(GeneratorExecutionContext context)
{
if (context.SyntaxReceiver is not SyntaxReceiver receiver)
@@ -49,6 +48,11 @@ namespace EntityFrameworkCore.Projections.Generator
}
var generatedClassName = ProjectionExpressionClassNameGenerator.GenerateName(projectable.ClassNamespace, projectable.NestedInClassNames, projectable.MemberName);
var lambdaTypeArguments = SyntaxFactory.TypeArgumentList(
SyntaxFactory.SeparatedList(
projectable.ParametersList.Parameters.Select(p => p.Type)
)
);
resultBuilder.Append($@"
namespace EntityFrameworkCore.Projections.Generated
@@ -56,8 +60,8 @@ namespace EntityFrameworkCore.Projections.Generated
{{
public static class {generatedClassName}
{{
public static System.Linq.Expressions.Expression<System.Func<{projectable.TargetClassNamespace}.{string.Join(".", projectable.TargetNestedInClassNames)}, {projectable.ReturnTypeName}>> Expression{projectable.ParametersListString} =>
{ProjectionTargetParameterName} => {projectable.Body};
public static System.Linq.Expressions.Expression<System.Func<{lambdaTypeArguments.Arguments}, {projectable.ReturnTypeName}>> Expression =>
{projectable.ParametersList} => {projectable.Body};
}}
}}");
@@ -9,23 +9,26 @@ namespace EntityFrameworkCore.Projections.Services
{
public class ExpressionArgumentReplacer : ExpressionVisitor
{
readonly Expression _targetExpression;
readonly IEnumerable<(ParameterExpression parameter, Expression argument)>? _parameterArgumentMapping;
public ExpressionArgumentReplacer(Expression targetExpression)
public ExpressionArgumentReplacer(IEnumerable<(ParameterExpression, Expression)>? parameterArgumentMapping = null)
{
_targetExpression = targetExpression;
_parameterArgumentMapping = parameterArgumentMapping;
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (node.Name == "projectionTarget")
var mappedArgument = _parameterArgumentMapping?
.Where(x => x.parameter == node)
.Select(x => x.argument)
.FirstOrDefault();
if (mappedArgument is not null)
{
return _targetExpression;
}
else
{
return base.VisitParameter(node);
return mappedArgument;
}
return base.VisitParameter(node);
}
}
}
@@ -15,24 +15,25 @@ namespace EntityFrameworkCore.Projections.Services
{
if (node.Method.GetCustomAttributes(true).OfType<ProjectableAttribute>().Any())
{
var reflectedExpressionFactory = _resolver.FindGeneratedExpressionFactory(node.Method);
var reflectedExpresssion = reflectedExpressionFactory(node.Arguments);
if (reflectedExpresssion is not null)
var reflectedExpression = _resolver.FindGeneratedExpression(node.Method);
var parameterArgumentMapping = node.Object is not null
? Enumerable.Repeat((reflectedExpression.Parameters[0], node.Object), 1)
: Enumerable.Empty<(ParameterExpression, Expression)>();
if (reflectedExpression.Parameters.Count > 0)
{
if (node.Object is not null)
{
var expressionArgumentReplacer = new ExpressionArgumentReplacer(node.Object);
return Visit(
expressionArgumentReplacer.Visit(reflectedExpresssion.Body)
);
}
else
{
return Visit(
reflectedExpresssion.Body
);
}
parameterArgumentMapping = parameterArgumentMapping.Concat(
node.Object is not null
? reflectedExpression.Parameters.Skip(1).Zip(node.Arguments)
: reflectedExpression.Parameters.Zip(node.Arguments)
);
}
var expressionArgumentReplacer = new ExpressionArgumentReplacer(parameterArgumentMapping);
return Visit(
expressionArgumentReplacer.Visit(reflectedExpression.Body)
);
}
return base.VisitMethodCall(node);
@@ -40,25 +41,24 @@ namespace EntityFrameworkCore.Projections.Services
protected override Expression VisitMember(MemberExpression node)
{
if (node.Member.GetCustomAttributes(true).OfType<ProjectableAttribute>().Any())
if (node.Member.GetCustomAttributes(false).OfType<ProjectableAttribute>().Any())
{
var reflectedExpressionFactory = _resolver.FindGeneratedExpressionFactory(node.Member);
var reflectedExpression = reflectedExpressionFactory(null);
if (reflectedExpression is not null)
var reflectedExpression = _resolver.FindGeneratedExpression(node.Member);
if (node.Expression is not null)
{
if (node.Expression is not null)
{
var expressionArgumentReplacer = new ExpressionArgumentReplacer(node.Expression);
return Visit(
expressionArgumentReplacer.Visit(reflectedExpression.Body)
);
}
else
{
return Visit(
reflectedExpression.Body
);
}
var expressionArgumentReplacer = new ExpressionArgumentReplacer(
Enumerable.Repeat((reflectedExpression.Parameters[0], node.Expression), 1)
);
return Visit(
expressionArgumentReplacer.Visit(reflectedExpression.Body)
);
}
else
{
return Visit(
reflectedExpression.Body
);
}
}
@@ -12,9 +12,9 @@ namespace EntityFrameworkCore.Projections.Services
{
public sealed class ProjectionExpressionResolver
{
readonly ConcurrentDictionary<string, Func<IReadOnlyCollection<Expression>?, LambdaExpression>> _lookupCache = new();
readonly ConcurrentDictionary<string, LambdaExpression> _lookupCache = new();
public Func<IReadOnlyCollection<Expression>?, LambdaExpression> FindGeneratedExpressionFactory(MemberInfo projectableMemberInfo)
public LambdaExpression FindGeneratedExpression(MemberInfo projectableMemberInfo)
{
var reflectedType = projectableMemberInfo.ReflectedType ?? throw new InvalidOperationException("Expected a valid type here");
var generatedContainingTypeName = ProjectionExpressionClassNameGenerator.GenerateFullName(reflectedType.Namespace, reflectedType.GetNestedTypePath().Select(x => x.Name), projectableMemberInfo.Name);
@@ -35,32 +35,7 @@ namespace EntityFrameworkCore.Projections.Services
};
}
return new Func<IReadOnlyCollection<Expression>?, LambdaExpression>(argumentExpressions =>
{
if (argumentExpressions is null || argumentExpressions.Count is 0)
{
return expressionFactoryMethod.Invoke(null, null) as LambdaExpression ?? throw new InvalidOperationException("Expected lambda");
}
else
{
var parameterExtractor = new ParameterExtractor();
foreach (var argument in argumentExpressions)
{
parameterExtractor.Visit(argument);
}
var lambda = Expression.Lambda<Func<LambdaExpression>>(
Expression.Call(
expressionFactoryMethod,
argumentExpressions
),
parameterExtractor.ExtractedParameters
);
return lambda.Compile().Invoke();
}
});
return expressionFactoryMethod.Invoke(null, null) as LambdaExpression ?? throw new InvalidOperationException("Expected lambda");
});
}
}