Skip to content

Commit

Permalink
Releases/next (#141)
Browse files Browse the repository at this point in the history
* Fixing nullable enum translation, re: #134

* Extending ShowCapturedValues capabilities

* Adding .NET 8 test project

* Fixing parameterless Value Tuple translation, re: #135

* Tidy

* Fixing non-equality enum comparisons, re #136

* v4.1.2

* Release notes

---------

Co-authored-by: Steve Wilkes <[email protected]>
  • Loading branch information
SteveWilkes and SteveWilkes authored Jun 22, 2024
1 parent 6b9aa2c commit 9e44d6f
Show file tree
Hide file tree
Showing 30 changed files with 634 additions and 183 deletions.
2 changes: 1 addition & 1 deletion Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<Company>AgileObjects Ltd</Company>
<Product>AgileObjects.ReadableExpressions</Product>
<Authors>Steve Wilkes</Authors>
<Copyright>Copyright © AgileObjects Ltd 2023</Copyright>
<Copyright>Copyright © AgileObjects Ltd 2024</Copyright>
<NeutralResourcesLanguage>en</NeutralResourcesLanguage>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/AgileObjects/ReadableExpressions</RepositoryUrl>
Expand Down
Binary file not shown.
Binary file not shown.
11 changes: 11 additions & 0 deletions src/ReadableExpressions.sln
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReadableExpressions.Visuali
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReadableExpressions.Visualizers.Vs17.6.ObjectSource", "Visualizers\ReadableExpressions.Visualizers.Vs17.6.ObjectSource\ReadableExpressions.Visualizers.Vs17.6.ObjectSource.csproj", "{EFF649F7-ABD2-49E6-BA1A-07BC34F89AA2}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ReadableExpressions.UnitTests.Net8", "Tests\ReadableExpressions.UnitTests.Net8\ReadableExpressions.UnitTests.Net8.csproj", "{E40E25FE-C7FA-4455-87A3-15D41784706E}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -354,6 +356,14 @@ Global
{EFF649F7-ABD2-49E6-BA1A-07BC34F89AA2}.Release|Any CPU.Build.0 = Release|Any CPU
{EFF649F7-ABD2-49E6-BA1A-07BC34F89AA2}.Release|x86.ActiveCfg = Release|Any CPU
{EFF649F7-ABD2-49E6-BA1A-07BC34F89AA2}.Release|x86.Build.0 = Release|Any CPU
{E40E25FE-C7FA-4455-87A3-15D41784706E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E40E25FE-C7FA-4455-87A3-15D41784706E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E40E25FE-C7FA-4455-87A3-15D41784706E}.Debug|x86.ActiveCfg = Debug|Any CPU
{E40E25FE-C7FA-4455-87A3-15D41784706E}.Debug|x86.Build.0 = Debug|Any CPU
{E40E25FE-C7FA-4455-87A3-15D41784706E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E40E25FE-C7FA-4455-87A3-15D41784706E}.Release|Any CPU.Build.0 = Release|Any CPU
{E40E25FE-C7FA-4455-87A3-15D41784706E}.Release|x86.ActiveCfg = Release|Any CPU
{E40E25FE-C7FA-4455-87A3-15D41784706E}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down Expand Up @@ -394,6 +404,7 @@ Global
{53A69151-1721-4ED5-BB65-6A750D46B133} = {E2401C71-C5F2-46FB-B5A3-E6EFB85106B9}
{CAC4A53F-D4B9-4AD5-864A-F1141ECE91DA} = {4A12EE3E-81ED-4842-A69F-9D15413DC46D}
{EFF649F7-ABD2-49E6-BA1A-07BC34F89AA2} = {4A12EE3E-81ED-4842-A69F-9D15413DC46D}
{E40E25FE-C7FA-4455-87A3-15D41784706E} = {E2401C71-C5F2-46FB-B5A3-E6EFB85106B9}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {7EFE121E-7A84-43A4-8C76-7EE70DF2736A}
Expand Down
7 changes: 6 additions & 1 deletion src/ReadableExpressions.sln.DotSettings
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=LocalConstants/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateConstants/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/PredefinedNamingRules/=PrivateStaticReadonly/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=15b5b1f1_002D457c_002D4ca6_002Db278_002D5615aedc07d3/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Static" AccessRightKinds="Private" Description="Static readonly fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="READONLY_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=236f7aa5_002D7b06_002D43ca_002Dbf2a_002D9b31bfcff09a/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Private" Description="Constant fields (private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="CONSTANT_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=669e5282_002Dfb4b_002D4e90_002D91e7_002D07d269d04b60/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Protected, ProtectedInternal, Internal, Public, PrivateProtected" Description="Constant fields (not private)"&gt;&lt;ElementKinds&gt;&lt;Kind Name="CONSTANT_FIELD" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/UserRules/=a4f433b8_002Dabcd_002D4e55_002Da08f_002D82e78cef0f0c/@EntryIndexedValue">&lt;Policy&gt;&lt;Descriptor Staticness="Any" AccessRightKinds="Any" Description="Local constants"&gt;&lt;ElementKinds&gt;&lt;Kind Name="LOCAL_CONSTANT" /&gt;&lt;/ElementKinds&gt;&lt;/Descriptor&gt;&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AA_BB" /&gt;&lt;/Policy&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FCONSTANT/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FFUNCTION/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/JavaScriptNaming/UserRules/=JS_005FBLOCK_005FSCOPE_005FVARIABLE/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
Expand Down Expand Up @@ -45,4 +49,5 @@
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=NAMESPACE_005FALIAS/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=XAML_005FFIELD/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/CodeStyle/Naming/XamlNaming/UserRules/=XAML_005FRESOURCE/@EntryIndexedValue">&lt;Policy Inspect="True" Prefix="" Suffix="" Style="AaBb" /&gt;</s:String>
<s:String x:Key="/Default/Environment/PerformanceGuide/SwitchBehaviour/=Antivirus/@EntryIndexedValue">DO_NOTHING</s:String></wpf:ResourceDictionary>
<s:String x:Key="/Default/Environment/PerformanceGuide/SwitchBehaviour/=Antivirus/@EntryIndexedValue">DO_NOTHING</s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EPredefinedNamingRulesToUserRulesUpgrade/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ public static ReadOnlyCollection<T> ToReadOnlyCollection<T>(
this IList<T> items)
{
return items.Count != 0
? new ReadOnlyCollection<T>(items)
? new(items)
: Enumerable<T>.EmptyReadOnlyCollection;
}

Expand Down
20 changes: 10 additions & 10 deletions src/ReadableExpressions/Extensions/InternalExpressionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#endif
using System.Reflection;
using NetStandardPolyfills;
using Translations;
#if NET35
using static Microsoft.Scripting.Ast.ExpressionType;
#else
Expand Down Expand Up @@ -69,13 +70,10 @@ public static bool IsReturnable(this Expression expression)
public static bool IsReturnable(this BlockExpression block)
=> block.HasReturnType() && block.Result.IsReturnable();

public static bool IsCapturedValue(
this Expression expression,
out object capturedValue,
out bool isStatic)
public static bool IsCapture(this Expression expression, out Capture capture)
{
capturedValue = null;
isStatic = false;
capture = new();

var capturedMemberAccesses = new List<MemberInfo>();

while (true)
Expand All @@ -102,22 +100,23 @@ public static bool IsCapturedValue(
return false;
}

var declaringType = capturedMemberAccesses.LastOrDefault()?.DeclaringType;
var declaringType = capturedMemberAccesses
.LastOrDefault()?.DeclaringType;

if (captureConstant.Type != declaringType)
{
return false;
}

capturedValue = captureConstant.Value;
capture.Object = captureConstant.Value;
break;

case Convert:
expression = expression.GetUnaryOperand();
continue;

case null:
isStatic = true;
capture.IsStatic = true;
break;

default:
Expand All @@ -131,9 +130,10 @@ public static bool IsCapturedValue(

for (var i = capturedMemberAccesses.Count - 1; i >= 0; --i)
{
capturedValue = capturedMemberAccesses[i].GetValue(capturedValue);
capture.Object = capturedMemberAccesses[i].GetValue(capture.Object);
}

capture.Type = capturedMemberAccesses[0].GetMemberInfoType();
return true;
}
}
Expand Down
100 changes: 93 additions & 7 deletions src/ReadableExpressions/Extensions/InternalReflectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
namespace AgileObjects.ReadableExpressions.Extensions
{
#if FEATURE_VALUE_TUPLE
using System;
#endif
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using NetStandardPolyfills;
using Translations.Reflection;
Expand Down Expand Up @@ -49,18 +48,105 @@ public static string GetKeywordOrNull(this IType type)
}

public static object GetValue(this MemberInfo member, object subject)
{
var hasSubject = subject != null;
=> member.TryGetValue(subject, out var value) ? value : null;

public static Type GetMemberInfoType(this MemberInfo member)
{
return member switch
{
FieldInfo field when hasSubject || field.IsStatic => field.GetValue(subject),
PropertyInfo property when hasSubject || property.IsStatic() => property.GetValue(subject,
Enumerable<object>.EmptyArray),
FieldInfo field => field.FieldType,
PropertyInfo property => property.PropertyType,
MethodInfo method => method.ReturnType,
_ => null
};
}

public static bool TryGetValue(
this MemberInfo member,
object subject,
out object value)
{
var hasSubject = subject != null;

switch (member)
{
case FieldInfo field when hasSubject || field.IsStatic:
value = field.GetValue(subject);
break;

case PropertyInfo property when hasSubject || property.IsStatic():
value = property.GetValue(subject, Enumerable<object>.EmptyArray);
break;

case MethodInfo method when method.IsCallable(subject, out var parameters):
value = method.Invoke(subject, parameters);
break;

default:
value = null;
return false;
}

return true;
}

private static bool IsCallable(
this MethodInfo method,
object subject,
out object[] parameters)
{
if (!method.IsPure())
{
parameters = null;
return false;
}

var parameterCount = method.GetParameters().Length;
var isParameterless = parameterCount == 0;

if (!method.IsStatic)
{
parameters = Enumerable<object>.EmptyArray;
return isParameterless && subject != null;
}

if (isParameterless)
{
parameters = Enumerable<object>.EmptyArray;
return true;
}

if (parameterCount == 1 && subject != null &&
method.IsExtensionMethod())
{
parameters = new[] { subject };
return true;
}

parameters = null;
return false;
}

private static bool IsPure(this MethodInfo method)
{
if (method.DeclaringType == typeof(Enumerable))
{
return method.Name switch
{
nameof(Enumerable.Any) => true,
nameof(Enumerable.First) => true,
nameof(Enumerable.FirstOrDefault) => true,
nameof(Enumerable.Last) => true,
nameof(Enumerable.LastOrDefault) => true,
_ => false
};
}

return method
.GetCustomAttributes(inherit: false)
.Any(attr => attr.GetType().Name == "PureAttribute");
}

#if FEATURE_VALUE_TUPLE
public static bool IsValueTuple(this Type type)
{
Expand Down
14 changes: 8 additions & 6 deletions src/ReadableExpressions/ReadableExpressions.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@
<NetStandardImplicitPackageVersion Condition=" '$(TargetFramework)' == 'netstandard1.0' ">1.6.1</NetStandardImplicitPackageVersion>
<PackageTargetFallback Condition=" '$(TargetFramework)' == 'netstandard1.0' ">$(PackageTargetFallback);dnxcore50</PackageTargetFallback>

<AssemblyVersion>4.1.1.0</AssemblyVersion>
<FileVersion>4.1.1.0</FileVersion>
<VersionPrefix>4.1.1</VersionPrefix>
<Version>4.1.1</Version>
<AssemblyVersion>4.1.2.0</AssemblyVersion>
<FileVersion>4.1.2.0</FileVersion>
<VersionPrefix>4.1.2</VersionPrefix>
<Version>4.1.2</Version>

<PackageId>AgileObjects.ReadableExpressions</PackageId>
<Title>AgileObjects.ReadableExpressions</Title>
Expand All @@ -26,8 +26,10 @@
<PackageIcon>./Icon.png</PackageIcon>
<PackageTags>ExpressionTrees Debugging DebuggerVisualizers Linq DLR</PackageTags>
<PackageProjectUrl>https://github.com/AgileObjects/ReadableExpressions</PackageProjectUrl>
<PackageReleaseNotes>- Fixing static method access when showing captured values re: #129
- Translating enum comparisons as enum constants
<PackageReleaseNotes>- Fixing nullable enum translation, re: #134
- Improving ShowCapturedValues capabilities to include captured Linq calls, re: #133
- Fixing parameterless Value Tuple translation, re: #135
- Fixing non-equality enum comparisons, re: #136
</PackageReleaseNotes>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageOutputPath>../../NuGet</PackageOutputPath>
Expand Down
71 changes: 48 additions & 23 deletions src/ReadableExpressions/Translations/BinaryTranslation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,25 @@ internal class BinaryTranslation :
private readonly INodeTranslation _rightOperandTranslation;
private bool _suppressParentheses;

private BinaryTranslation(BinaryExpression binary, ITranslationContext context) :
base(IsChecked(binary.NodeType), "(", ")")
private BinaryTranslation(
BinaryExpression binary,
ITranslationContext context) :
this(binary.Left, binary.NodeType, binary.Right, context)
{
}

private BinaryTranslation(
Expression leftOperand,
ExpressionType nodeType,
Expression rightOperand,
ITranslationContext context) :
base(IsChecked(nodeType), "(", ")")
{
_context = context;
NodeType = binary.NodeType;
_leftOperandTranslation = context.GetTranslationFor(binary.Left);
NodeType = nodeType;
_leftOperandTranslation = context.GetTranslationFor(leftOperand);
_operator = GetOperator(NodeType);
_rightOperandTranslation = context.GetTranslationFor(binary.Right);
_rightOperandTranslation = context.GetTranslationFor(rightOperand);

if (_leftOperandTranslation is BinaryTranslation leftNestedBinary &&
HasComplimentaryOperator(leftNestedBinary))
Expand Down Expand Up @@ -138,32 +149,45 @@ public static INodeTranslation For(
goto default;

default:
TryGetEnumComparisonExpression(ref binary);
var isEnumComparison = TryGetEnumComparisonExpression(
binary,
out var leftOperand,
out var rightOperand);

if (isEnumComparison)
{
return new BinaryTranslation(
leftOperand,
binary.NodeType,
rightOperand,
context);
}

break;
}

return new BinaryTranslation(binary, context);
}

public static void TryGetEnumComparisonExpression(
ref BinaryExpression comparison)
private static bool TryGetEnumComparisonExpression(
BinaryExpression comparison,
out Expression leftOperand,
out Expression rightOperand)
{
var leftOperandIsEnum =
IsEnumType(comparison.Left, out var leftExpression);

var rightOperandIsEnum =
IsEnumType(comparison.Right, out var rightExpression);
var leftOperandIsEnum = IsEnumType(comparison.Left, out leftOperand);
var rightOperandIsEnum = IsEnumType(comparison.Right, out rightOperand);

if (leftOperandIsEnum || rightOperandIsEnum)
if (!(leftOperandIsEnum || rightOperandIsEnum))
{
var enumType = leftOperandIsEnum
? leftExpression.Type : rightExpression.Type;

comparison = comparison.Update(
GetEnumValue(leftExpression, enumType),
comparison.Conversion,
GetEnumValue(rightExpression, enumType));
return false;
}

var enumType = leftOperandIsEnum
? leftOperand.Type : rightOperand.Type;

leftOperand = GetEnumValue(leftOperand, enumType);
rightOperand = GetEnumValue(rightOperand, enumType);
return true;
}

private static bool IsEnumType(
Expand All @@ -187,16 +211,17 @@ private static bool IsEnumType(

private static Expression GetEnumValue(
Expression expression,
Type enumType)
Type enumValueType)
{
if (expression.NodeType != Constant)
{
return expression;
}

var value = ((ConstantExpression)expression).Value;
var enumType = enumValueType.GetNonNullableType();
var enumValue = Enum.Parse(enumType, value.ToString());
return Expression.Constant(enumValue, enumType);
return Expression.Constant(enumValue, enumValueType);
}

#endregion
Expand Down
Loading

0 comments on commit 9e44d6f

Please sign in to comment.