Skip to content

Commit

Permalink
Merge branch 'master' into assemblyUsedBy
Browse files Browse the repository at this point in the history
  • Loading branch information
fowl2 authored Nov 2, 2023
2 parents 0b7f47c + 0bab8a0 commit 9af97da
Show file tree
Hide file tree
Showing 32 changed files with 453 additions and 179 deletions.
12 changes: 6 additions & 6 deletions ICSharpCode.Decompiler.Tests/TestCases/Pretty/ExpressionTrees.cs
Original file line number Diff line number Diff line change
Expand Up @@ -223,15 +223,15 @@ public SimpleTypeWithMultipleCtors(int i)
private dynamic ViewBag;

public static readonly object[] SupportedMethods = new object[2] {
ToCode(null, () => ((IQueryable<object>)null).Aggregate((object o1, object o2) => null)),
ToCode(null, () => ((IEnumerable<object>)null).Aggregate((object o1, object o2) => null))
ToCode(null, () => ((IQueryable<object>)null).Aggregate((object o1, object o2) => (object)null)),
ToCode(null, () => ((IEnumerable<object>)null).Aggregate((object o1, object o2) => (object)null))
};

public static readonly object[] SupportedMethods2 = new object[4] {
ToCode(null, () => ((IQueryable<object>)null).Aggregate(null, (object o1, object o2) => null)),
ToCode(null, () => ((IQueryable<object>)null).Aggregate((object)null, (Expression<Func<object, object, object>>)((object o1, object o2) => null), (Expression<Func<object, object>>)((object o) => null))),
ToCode(null, () => ((IEnumerable<object>)null).Aggregate(null, (object o1, object o2) => null)),
ToCode(null, () => ((IEnumerable<object>)null).Aggregate((object)null, (Func<object, object, object>)((object o1, object o2) => null), (Func<object, object>)((object o) => null)))
ToCode(null, () => ((IQueryable<object>)null).Aggregate(null, (object o1, object o2) => (object)null)),
ToCode(null, () => ((IQueryable<object>)null).Aggregate(null, (object o1, object o2) => (object)null, (object o) => (object)null)),
ToCode(null, () => ((IEnumerable<object>)null).Aggregate(null, (object o1, object o2) => (object)null)),
ToCode(null, () => ((IEnumerable<object>)null).Aggregate(null, (object o1, object o2) => (object)null, (object o) => (object)null))
};

public static void TestCall(object a)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ public class Issue1660 : Issue1660Base
public Action<object> M(object state)
{
return delegate (object x) {
base.BaseCall(x, state, (Func<object>)(() => null));
base.BaseCall(x, state, () => (object)null);
};
}
}
Expand Down
25 changes: 23 additions & 2 deletions ICSharpCode.Decompiler.Tests/TestCases/Pretty/TupleTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public struct GenericStruct<T>
public int AccessPartiallyNamed => PartiallyNamed.a + PartiallyNamed.Item3;

public ValueTuple<int> NewTuple1 => new ValueTuple<int>(1);
public (int a, int b) NewTuple2 => (1, 2);
public (int a, int b) NewTuple2 => (a: 1, b: 2);
public object BoxedTuple10 => (1, 2, 3, 4, 5, 6, 7, 8, 9, 10);

public (uint, int) SwapUnnamed => (Unnamed2.Item2, Unnamed2.Item1);
Expand Down Expand Up @@ -115,7 +115,7 @@ public void UnnamedTupleRef(ref (int, string, Action, dynamic) tuple)

public void NamedTupleOut(out (int A, string B, Action C, dynamic D) tuple)
{
tuple = (42, "Hello", Console.WriteLine, null);
tuple = (A: 42, B: "Hello", C: Console.WriteLine, D: null);
}

public void NamedTupleIn(in (int A, string B, Action C, dynamic D) tuple)
Expand All @@ -141,6 +141,27 @@ public void UseDict()
Console.WriteLine(TupleDict.Values.ToList().First().d);
}

private static (string, string) Issue3014a(string[] args)
{
return (from v in args
select (Name: v, Value: v) into kvp
orderby kvp.Name
select kvp).First();
}

private static (string, string) Issue3014b(string[] args)
{
return (from v in args
select ((string Name, string Value))GetTuple() into kvp
orderby kvp.Name
select kvp).First();

(string, string) GetTuple()
{
return (args[0], args[1]);
}
}

public void Issue1174()
{
Console.WriteLine((1, 2, 3).GetHashCode());
Expand Down
17 changes: 13 additions & 4 deletions ICSharpCode.Decompiler/CSharp/CallBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ public CallBuilder(ExpressionBuilder expressionBuilder, IDecompilerTypeSystem ty
this.typeSystem = typeSystem;
}

public TranslatedExpression Build(CallInstruction inst)
public TranslatedExpression Build(CallInstruction inst, IType typeHint = null)
{
if (inst is NewObj newobj && IL.Transforms.DelegateConstruction.MatchDelegateConstruction(newobj, out _, out _, out _))
{
Expand All @@ -198,20 +198,29 @@ public TranslatedExpression Build(CallInstruction inst)
if (settings.TupleTypes && TupleTransform.MatchTupleConstruction(inst as NewObj, out var tupleElements) && tupleElements.Length >= 2)
{
var elementTypes = TupleType.GetTupleElementTypes(inst.Method.DeclaringType);
Debug.Assert(!elementTypes.IsDefault, "MatchTupleConstruction should not success unless we got a valid tuple type.");
var elementNames = typeHint is TupleType tt ? tt.ElementNames : default;
Debug.Assert(!elementTypes.IsDefault, "MatchTupleConstruction should not succeed unless we got a valid tuple type.");
Debug.Assert(elementTypes.Length == tupleElements.Length);
var tuple = new TupleExpression();
var elementRRs = new List<ResolveResult>();
foreach (var (element, elementType) in tupleElements.Zip(elementTypes))
foreach (var (index, element, elementType) in tupleElements.ZipWithIndex(elementTypes))
{
var translatedElement = expressionBuilder.Translate(element, elementType)
.ConvertTo(elementType, expressionBuilder, allowImplicitConversion: true);
tuple.Elements.Add(translatedElement.Expression);
if (elementNames.IsDefaultOrEmpty || elementNames.ElementAtOrDefault(index) is not string { Length: > 0 } name)
{
tuple.Elements.Add(translatedElement.Expression);
}
else
{
tuple.Elements.Add(new NamedArgumentExpression(name, translatedElement.Expression));
}
elementRRs.Add(translatedElement.ResolveResult);
}
return tuple.WithRR(new TupleResolveResult(
expressionBuilder.compilation,
elementRRs.ToImmutableArray(),
elementNames,
valueTupleAssembly: inst.Method.DeclaringType.GetDefinition()?.ParentModule
)).WithILInstruction(inst);
}
Expand Down
3 changes: 1 addition & 2 deletions ICSharpCode.Decompiler/CSharp/ExpressionBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
using System.Diagnostics;
using System.Linq;
using System.Reflection.Metadata;
using System.Runtime.CompilerServices;
using System.Threading;

using ICSharpCode.Decompiler.CSharp.Resolver;
Expand Down Expand Up @@ -438,7 +437,7 @@ protected internal override TranslatedExpression VisitNewObj(NewObj inst, Transl
return TranslateStackAllocInitializer(b, type.TypeArguments[0]);
}
}
return new CallBuilder(this, typeSystem, settings).Build(inst);
return new CallBuilder(this, typeSystem, settings).Build(inst, context.TypeHint);
}

protected internal override TranslatedExpression VisitLdVirtDelegate(LdVirtDelegate inst, TranslationContext context)
Expand Down
19 changes: 19 additions & 0 deletions ICSharpCode.Decompiler/CSharp/StatementBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -395,8 +395,14 @@ protected internal override TranslatedStatement VisitLeave(Leave inst)
return new YieldBreakStatement().WithILInstruction(inst);
else if (!inst.Value.MatchNop())
{
bool isLambdaOrExprTree = currentFunction.Kind is ILFunctionKind.ExpressionTree or ILFunctionKind.Delegate;
var expr = exprBuilder.Translate(inst.Value, typeHint: currentResultType)
.ConvertTo(currentResultType, exprBuilder, allowImplicitConversion: true);
if (isLambdaOrExprTree && IsPossibleLossOfTypeInformation(expr.Type, currentResultType))
{
expr = new CastExpression(exprBuilder.ConvertType(currentResultType), expr)
.WithRR(new ConversionResolveResult(currentResultType, expr.ResolveResult, Conversion.IdentityConversion)).WithoutILInstruction();
}
return new ReturnStatement(expr).WithILInstruction(inst);
}
else
Expand All @@ -419,6 +425,19 @@ protected internal override TranslatedStatement VisitLeave(Leave inst)
return new GotoStatement(label).WithILInstruction(inst);
}

private bool IsPossibleLossOfTypeInformation(IType givenType, IType expectedType)
{
if (NormalizeTypeVisitor.IgnoreNullability.EquivalentTypes(givenType, expectedType))
return false;
if (expectedType is TupleType { ElementNames.IsEmpty: false })
return true;
if (expectedType == SpecialType.Dynamic)
return true;
if (givenType == SpecialType.NullType)
return true;
return false;
}

protected internal override TranslatedStatement VisitThrow(Throw inst)
{
return new ThrowStatement(exprBuilder.Translate(inst.Argument)).WithILInstruction(inst);
Expand Down
15 changes: 12 additions & 3 deletions ICSharpCode.Decompiler/CSharp/TranslatedExpression.cs
Original file line number Diff line number Diff line change
Expand Up @@ -272,11 +272,20 @@ public TranslatedExpression ConvertTo(IType targetType, ExpressionBuilder expres
// Conversion of a tuple literal: convert element-wise
var newTupleExpr = new TupleExpression();
var newElementRRs = new List<ResolveResult>();
foreach (var (elementExpr, elementTargetType) in tupleExpr.Elements.Zip(targetTupleType.ElementTypes))
// element names: discard existing names and use targetTupleType instead
var newElementNames = targetTupleType.ElementNames;
foreach (var (index, elementExpr, elementTargetType) in tupleExpr.Elements.ZipWithIndex(targetTupleType.ElementTypes))
{
var newElementExpr = new TranslatedExpression(elementExpr.Detach())
var newElementExpr = new TranslatedExpression((elementExpr is NamedArgumentExpression nae ? nae.Expression : elementExpr).Detach())
.ConvertTo(elementTargetType, expressionBuilder, checkForOverflow, allowImplicitConversion);
newTupleExpr.Elements.Add(newElementExpr.Expression);
if (newElementNames.IsDefaultOrEmpty || newElementNames.ElementAtOrDefault(index) is not string { Length: > 0 } name)
{
newTupleExpr.Elements.Add(newElementExpr.Expression);
}
else
{
newTupleExpr.Elements.Add(new NamedArgumentExpression(name, newElementExpr.Expression));
}
newElementRRs.Add(newElementExpr.ResolveResult);
}
return newTupleExpr.WithILInstruction(this.ILInstructions)
Expand Down
32 changes: 20 additions & 12 deletions ICSharpCode.Decompiler/FlowAnalysis/ControlFlowNode.cs
Original file line number Diff line number Diff line change
Expand Up @@ -88,24 +88,32 @@ public void AddEdgeTo(ControlFlowNode target)

public void TraversePreOrder(Func<ControlFlowNode, IEnumerable<ControlFlowNode>> children, Action<ControlFlowNode> visitAction)
{
if (Visited)
return;
Visited = true;
visitAction(this);
foreach (ControlFlowNode t in children(this))
t.TraversePreOrder(children, visitAction);
GraphTraversal.DepthFirstSearch(new[] { this }, Visit, children);

bool Visit(ControlFlowNode node)
{
if (node.Visited)
return false;
node.Visited = true;
visitAction(node);
return true;
}
}

public void TraversePostOrder(Func<ControlFlowNode, IEnumerable<ControlFlowNode>> children, Action<ControlFlowNode> visitAction)
{
if (Visited)
return;
Visited = true;
foreach (ControlFlowNode t in children(this))
t.TraversePostOrder(children, visitAction);
visitAction(this);
GraphTraversal.DepthFirstSearch(new[] { this }, Visit, children, postorderAction: visitAction);

bool Visit(ControlFlowNode node)
{
if (node.Visited)
return false;
node.Visited = true;
return true;
}
}


/// <summary>
/// Gets whether <c>this</c> dominates <paramref name="node"/>.
/// </summary>
Expand Down
1 change: 1 addition & 0 deletions ICSharpCode.Decompiler/ICSharpCode.Decompiler.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,7 @@
<Compile Include="Util\CSharpPrimitiveCast.cs" />
<Compile Include="Util\EmptyList.cs" />
<Compile Include="Util\ExtensionMethods.cs" />
<Compile Include="Util\GraphTraversal.cs" />
<Compile Include="Util\Interval.cs" />
<Compile Include="Util\LazyInit.cs" />
<Compile Include="Util\LongSet.cs" />
Expand Down
26 changes: 16 additions & 10 deletions ICSharpCode.Decompiler/IL/Instructions/BlockContainer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ public List<Block> TopologicalSort(bool deleteUnreachableBlocks = false)
// Visit blocks in post-order
BitSet visited = new BitSet(Blocks.Count);
List<Block> postOrder = new List<Block>();
Visit(EntryPoint);
GraphTraversal.DepthFirstSearch(new[] { EntryPoint }, MarkAsVisited, Successors, postOrder.Add, reverseSuccessors: true);
postOrder.Reverse();
if (!deleteUnreachableBlocks)
{
Expand All @@ -291,24 +291,30 @@ public List<Block> TopologicalSort(bool deleteUnreachableBlocks = false)
}
return postOrder;

void Visit(Block block)
bool MarkAsVisited(Block block)
{
Debug.Assert(block.Parent == this);
if (!visited[block.ChildIndex])
{
visited[block.ChildIndex] = true;
return true;
}
else
{
return false;
}
}

foreach (var branch in block.Descendants.OfType<Branch>())
IEnumerable<Block> Successors(Block block)
{
foreach (var branch in block.Descendants.OfType<Branch>())
{
if (branch.TargetBlock.Parent == this)
{
if (branch.TargetBlock.Parent == this)
{
Visit(branch.TargetBlock);
}
yield return branch.TargetBlock;
}

postOrder.Add(block);
}
};
}
}

/// <summary>
Expand Down
46 changes: 27 additions & 19 deletions ICSharpCode.Decompiler/IL/Transforms/BlockTransform.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

using ICSharpCode.Decompiler.FlowAnalysis;
using ICSharpCode.Decompiler.IL.ControlFlow;
using ICSharpCode.Decompiler.Util;

namespace ICSharpCode.Decompiler.IL.Transforms
{
Expand Down Expand Up @@ -105,29 +106,36 @@ public void Run(ILFunction function, ILTransformContext context)
}
}

void VisitBlock(ControlFlowNode cfgNode, BlockTransformContext context)
/// <summary>
/// Walks the dominator tree rooted at entryNode, calling the transforms on each block.
/// </summary>
void VisitBlock(ControlFlowNode entryNode, BlockTransformContext context)
{
Block block = (Block)cfgNode.UserData;
context.StepStartGroup(block.Label, block);

context.ControlFlowNode = cfgNode;
context.Block = block;
context.IndexOfFirstAlreadyTransformedInstruction = block.Instructions.Count;
block.RunTransforms(PreOrderTransforms, context);

// First, process the children in the dominator tree.
// The ConditionDetection transform requires dominated blocks to
// be already processed.
foreach (var child in cfgNode.DominatorTreeChildren)
IEnumerable<ControlFlowNode> Preorder(ControlFlowNode cfgNode)
{
VisitBlock(child, context);
// preorder processing:
Block block = (Block)cfgNode.UserData;
context.StepStartGroup(block.Label, block);

context.ControlFlowNode = cfgNode;
context.Block = block;
context.IndexOfFirstAlreadyTransformedInstruction = block.Instructions.Count;
block.RunTransforms(PreOrderTransforms, context);

// process the children
return cfgNode.DominatorTreeChildren;
}

context.ControlFlowNode = cfgNode;
context.Block = block;
context.IndexOfFirstAlreadyTransformedInstruction = block.Instructions.Count;
block.RunTransforms(PostOrderTransforms, context);
context.StepEndGroup();
foreach (var cfgNode in TreeTraversal.PostOrder(entryNode, Preorder))
{
// in post-order:
Block block = (Block)cfgNode.UserData;
context.ControlFlowNode = cfgNode;
context.Block = block;
context.IndexOfFirstAlreadyTransformedInstruction = block.Instructions.Count;
block.RunTransforms(PostOrderTransforms, context);
context.StepEndGroup();
}
}
}
}
Loading

0 comments on commit 9af97da

Please sign in to comment.